UI-Entwurf umgesetzt, Rocket.Chat-Tool, Deploymentcenter 2.4
Sicherungspunkt vor dem Aufraeumen. Buendelt die Arbeit, die seit dem Abschluss der Avalonia-Portierung im Arbeitsverzeichnis lag. Oberflaeche - Entwurf aus Mockup/ umgesetzt: Theme.axaml (Farben je Thema, Barlow als mitgelieferte Schrift), Icons.axaml (Symbolgeometrien), Shell.axaml (eigene ControlThemes statt Fluent umzufaerben). - Neue Steuerelemente StrokeIcon und BlueprintFrame, Seiten fuer Token-Verbrauch und Agenten-Chats, Werkzeug-Einstellungen als Seite statt eigenem Fenster, Texteditor-Fenster. - ThemeManager mit hellem und dunklem Thema; die beiden Pinsel-Konverter entfallen, weil ein fester Farbwert den Themenwechsel nicht ueberlebt. Rocket.Chat - Neues Tool-Projekt (Client, Konfiguration, Workspace-Dateien) nach der Bauform des Telegram-Tools: rocketchat_poll als Tool-Job, geweckt wird nur, wenn wirklich etwas anliegt. - send_file ist freigabepflichtig, send_message bewusst nicht: Der Raum ist Arbeitsraum, der Schutz sitzt an der Raum-Allowlist. - Konzept-Doc um die Messung gegen die echte Instanz 8.7 ergaenzt; drei Annahmen waren falsch und sind korrigiert. Deploymentcenter - DC6 (Update anwenden) und DC7 (Erstinstallation ueber setup.json) erledigt, DC3 fuer win-x64/dev; deploy/publish.py als Release-Strecke. - AppHost.DisposeAsync gegen doppeltes Herunterfahren gesperrt - sonst ueberschreibt eine zweite Abmeldung den Wartungszustand am Watchdog. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
xmlns:conv="using:ClawdDotNet.Desktop.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="1100" d:DesignHeight="700"
|
||||
x:Class="ClawdDotNet.Desktop.Views.AgentChatsPageView"
|
||||
x:DataType="vm:AgentChatsPageViewModel">
|
||||
|
||||
<DockPanel MaxWidth="820" HorizontalAlignment="Left">
|
||||
|
||||
<!-- Wer mit wem -->
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Spacing="10"
|
||||
Margin="0,0,0,16" VerticalAlignment="Center">
|
||||
<TextBlock Text="Agent A:" FontWeight="SemiBold" VerticalAlignment="Center" />
|
||||
<ComboBox Width="220" ItemsSource="{Binding Agents}" SelectedItem="{Binding AgentA}" />
|
||||
|
||||
<TextBlock Text="Agent B:" FontWeight="SemiBold" VerticalAlignment="Center"
|
||||
Margin="8,0,0,0" />
|
||||
<ComboBox Width="220" ItemsSource="{Binding Agents}" SelectedItem="{Binding AgentB}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Leerzustand: erklärt, warum hier noch nichts steht -->
|
||||
<Border Classes="card" DockPanel.Dock="Top" VerticalAlignment="Top"
|
||||
IsVisible="{Binding IsEmpty}">
|
||||
<TextBlock Classes="muted" Text="{Binding EmptyText}" TextWrapping="Wrap" />
|
||||
</Border>
|
||||
|
||||
<!-- Verlauf -->
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" IsVisible="{Binding !IsEmpty}">
|
||||
<ItemsControl ItemsSource="{Binding Messages}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Spacing="10" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:AgentMessageItem">
|
||||
<Border Classes="card" MaxWidth="560"
|
||||
Classes.accent="{Binding !IsFromAgentA}"
|
||||
HorizontalAlignment="{Binding !IsFromAgentA,
|
||||
Converter={x:Static conv:ChatAlignmentConverter.Instance}}">
|
||||
<StackPanel Spacing="4">
|
||||
<DockPanel>
|
||||
<TextBlock FontSize="12" FontWeight="SemiBold" Text="{Binding SenderName}" />
|
||||
<TextBlock Classes="caption" DockPanel.Dock="Right" HorizontalAlignment="Right"
|
||||
Text="{Binding Time, StringFormat='{}{0:dd.MM. HH:mm}'}" />
|
||||
</DockPanel>
|
||||
<SelectableTextBlock FontSize="13.5" TextWrapping="Wrap" Text="{Binding Text}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
</DockPanel>
|
||||
|
||||
</UserControl>
|
||||
@@ -0,0 +1,8 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace ClawdDotNet.Desktop.Views;
|
||||
|
||||
public partial class AgentChatsPageView : UserControl
|
||||
{
|
||||
public AgentChatsPageView() => InitializeComponent();
|
||||
}
|
||||
@@ -3,153 +3,283 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="700"
|
||||
Padding="16"
|
||||
xmlns:ctrl="using:ClawdDotNet.Desktop.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="1280" d:DesignHeight="820"
|
||||
x:Class="ClawdDotNet.Desktop.Views.AgentsPageView"
|
||||
x:DataType="vm:AgentsPageViewModel">
|
||||
|
||||
<!--
|
||||
Vier Felder wie in der WinForms-Vorlage: oben Agentenliste und Agenteneinstellungen,
|
||||
unten Werkzeugliste und Werkzeugeinstellungen — aber mit ziehbaren Teilern statt
|
||||
fester SplitterDistance und mit flachen Feldern statt PropertyGrid.
|
||||
|
||||
Die Teilerstellungen werden im Code-Behind gelesen und gespeichert: ColumnDefinition
|
||||
liegt nicht im Sichtbarkeitsbaum und erbt deshalb keinen DataContext — eine Bindung
|
||||
darauf wuerde stillschweigend nichts tun.
|
||||
-->
|
||||
<DockPanel>
|
||||
|
||||
<!-- Statusbar am unteren Rand -->
|
||||
<Border Classes="statusbar" DockPanel.Dock="Bottom">
|
||||
<TextBlock Text="{Binding StatusText}" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<TextBlock DockPanel.Dock="Bottom" Classes="caption" Margin="0,10,0,0"
|
||||
Text="{Binding StatusText}" TextWrapping="Wrap" />
|
||||
|
||||
<!-- Master-Detail Split -->
|
||||
<Grid ColumnDefinitions="260,*">
|
||||
<Grid x:Name="OuterGrid" RowDefinitions="*,Auto,*">
|
||||
|
||||
<!-- Linke Spalte: Agenten-Liste -->
|
||||
<DockPanel Grid.Column="0" Margin="0,0,12,0">
|
||||
|
||||
<!-- Toolbar -->
|
||||
<StackPanel Classes="toolbar" DockPanel.Dock="Top">
|
||||
<Button Content="Neu" Command="{Binding AddAgentCommand}" Classes="accent" />
|
||||
<Button Content="Entfernen" Command="{Binding RemoveAgentCommand}" IsEnabled="{Binding SelectedAgent, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
<Button Content="Speichern" Command="{Binding SaveAgentsCommand}" />
|
||||
</StackPanel>
|
||||
<!-- ══════════════════ Oben: Agenten ══════════════════ -->
|
||||
<Grid x:Name="TopGrid" Grid.Row="0" ColumnDefinitions="0.44*,Auto,0.56*">
|
||||
|
||||
<!-- Liste -->
|
||||
<ListBox ItemsSource="{Binding Agents}" SelectedItem="{Binding SelectedAgent}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="vm:AgentItemViewModel">
|
||||
<StackPanel Margin="4">
|
||||
<TextBlock Text="{Binding DisplayName}" FontWeight="Bold" />
|
||||
<TextBlock Text="{Binding Model}" Classes="caption" />
|
||||
<DockPanel Grid.Column="0" Margin="0,0,14,0">
|
||||
|
||||
<WrapPanel DockPanel.Dock="Top" Classes="toolbar">
|
||||
<Button Classes="toolbar" Command="{Binding AddAgentCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconPlus}" Width="14" Height="14" />
|
||||
<TextBlock Text="Agent" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Button>
|
||||
|
||||
</DockPanel>
|
||||
<Button Classes="toolbar" Command="{Binding RemoveAgentCommand}"
|
||||
IsEnabled="{Binding SelectedAgent, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconMinus}" Width="14" Height="14" />
|
||||
<TextBlock Text="Agent" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- Rechte Spalte: Agenten-Details -->
|
||||
<ContentControl Grid.Column="1" Content="{Binding SelectedAgent}">
|
||||
<ContentControl.ContentTemplate>
|
||||
<DataTemplate DataType="vm:AgentItemViewModel">
|
||||
<TabControl>
|
||||
<Border Classes="sep" />
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding RunAgentNowCommand}"
|
||||
IsEnabled="{Binding !IsRunning}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconPlay}" Width="12" Height="12" IsFilled="True" />
|
||||
<TextBlock Text="Jetzt ausf." />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Border Classes="sep" />
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding EditIdentityCommand}"
|
||||
IsEnabled="{Binding SelectedAgent, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconPencil}" Width="14" Height="14" />
|
||||
<TextBlock Text="Identity" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding EditSoulCommand}"
|
||||
IsEnabled="{Binding SelectedAgent, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconPencil}" Width="14" Height="14" />
|
||||
<TextBlock Text="Soul" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Border Classes="sep" />
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding OpenAgentFolderCommand}"
|
||||
IsEnabled="{Binding SelectedAgent, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconFolder}" Width="14" Height="14" />
|
||||
<TextBlock Text="Ordner" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding SaveAgentsCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconSave}" Width="14" Height="14" />
|
||||
<TextBlock Text="Speichern" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</WrapPanel>
|
||||
|
||||
<Border DockPanel.Dock="Top" Classes="thead">
|
||||
<Grid ColumnDefinitions="1.4*,1.6*,2*">
|
||||
<TextBlock Grid.Column="0" Classes="th" Text="ANZEIGENAME" />
|
||||
<TextBlock Grid.Column="1" Classes="th" Text="MODELL" />
|
||||
<TextBlock Grid.Column="2" Classes="th" Text="KURZINFO" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<ListBox Classes="table" ItemsSource="{Binding Agents}"
|
||||
SelectedItem="{Binding SelectedAgent}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="vm:AgentItemViewModel">
|
||||
<Border Classes="tr" Padding="7">
|
||||
<Grid ColumnDefinitions="1.4*,1.6*,2*">
|
||||
<TextBlock Grid.Column="0" Classes="td" FontWeight="SemiBold"
|
||||
Text="{Binding DisplayName}" />
|
||||
<TextBlock Grid.Column="1" Classes="td dim" Text="{Binding Model}" />
|
||||
<TextBlock Grid.Column="2" Classes="td dim" Text="{Binding Description}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
|
||||
<GridSplitter x:Name="TopSplitter" Grid.Column="1" Classes="vertical"
|
||||
ResizeDirection="Columns" />
|
||||
|
||||
<!-- Einstellungen des gewählten Agenten -->
|
||||
<ScrollViewer Grid.Column="2" VerticalScrollBarVisibility="Auto" Padding="4,0,0,0">
|
||||
<ContentControl Content="{Binding SelectedAgent}">
|
||||
<ContentControl.ContentTemplate>
|
||||
<DataTemplate DataType="vm:AgentItemViewModel">
|
||||
<StackPanel Spacing="20" MaxWidth="640" HorizontalAlignment="Left">
|
||||
|
||||
<!-- TAB 1: Allgemein & Modell -->
|
||||
<TabItem Header="Allgemein & Modell">
|
||||
<ScrollViewer Padding="8">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Classes="kicker" Text="ALLGEMEIN & MODELL" />
|
||||
<TextBlock Classes="h2" Text="{Binding DisplayName}" />
|
||||
|
||||
<Grid ColumnDefinitions="140,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Agent ID:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding AgentId}" IsReadOnly="True" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Anzeigename:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding DisplayName}" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Modell:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<ComboBox Grid.Row="2" Grid.Column="1" ItemsSource="{Binding $parent[UserControl].((vm:AgentsPageViewModel)DataContext).KnownModels}" SelectedItem="{Binding Model}" HorizontalAlignment="Stretch" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Rolle / Beschreibung:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Description}" AcceptsReturn="True" Height="60" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="Prompt Caching:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<ComboBox Grid.Row="4" Grid.Column="1" ItemsSource="{Binding $parent[UserControl].((vm:AgentsPageViewModel)DataContext).CachingOptions}" SelectedItem="{Binding PromptCaching}" Width="150" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Agent-ID" />
|
||||
<TextBox Text="{Binding AgentId}" IsReadOnly="True" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="Anzeigename" />
|
||||
<TextBox Text="{Binding DisplayName}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Limits Card -->
|
||||
<Border Classes="card" Margin="0,8,0,0">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Schutzgrenzen (LoopGuard & Budget)" Classes="heading" />
|
||||
<Grid ColumnDefinitions="*,*" RowDefinitions="Auto,Auto,Auto">
|
||||
<StackPanel Grid.Row="0" Grid.Column="0" Spacing="4">
|
||||
<TextBlock Text="Max. Schritte pro Run:" />
|
||||
<NumericUpDown Value="{Binding MaxSteps}" Minimum="1" Maximum="100" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" Spacing="4" Margin="8,0,0,0">
|
||||
<TextBlock Text="Timeout (Sekunden):" />
|
||||
<NumericUpDown Value="{Binding TimeoutSeconds}" Minimum="10" Maximum="3600" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Grid.Column="0" Spacing="4" Margin="0,8,0,0">
|
||||
<TextBlock Text="Max. Kontext-Tokens:" />
|
||||
<NumericUpDown Value="{Binding MaxContextTokens}" Minimum="1000" Maximum="2000000" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Grid.Column="1" Spacing="4" Margin="8,8,0,0">
|
||||
<TextBlock Text="Max. Tool-Ergebnis (Zeichen):" />
|
||||
<NumericUpDown Value="{Binding MaxToolResultChars}" Minimum="1000" Maximum="500000" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Modell" />
|
||||
<ComboBox HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding $parent[UserControl].((vm:AgentsPageViewModel)DataContext).KnownModels}"
|
||||
SelectedItem="{Binding Model}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="Prompt Caching" />
|
||||
<ComboBox HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding $parent[UserControl].((vm:AgentsPageViewModel)DataContext).CachingOptions}"
|
||||
SelectedItem="{Binding PromptCaching}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Rolle / Beschreibung" />
|
||||
<TextBox Text="{Binding Description}" AcceptsReturn="True" Height="54"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="System-Prompt (zusätzliche Anweisungen)" />
|
||||
<TextBox Text="{Binding SystemPrompt}" AcceptsReturn="True" Height="70"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<!-- TAB 2: System Prompt & Seele -->
|
||||
<TabItem Header="System-Prompt & Soul">
|
||||
<ScrollViewer Padding="8">
|
||||
<Border Classes="hr" />
|
||||
|
||||
<StackPanel Spacing="12">
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="System-Prompt (Zusätzliche Anweisungen):" FontWeight="Bold" />
|
||||
<TextBox Text="{Binding SystemPrompt}" AcceptsReturn="True" Height="120" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<TextBlock Classes="kicker" Text="GRENZEN" />
|
||||
<TextBlock Classes="h2" Text="Schutzgrenzen (LoopGuard & Budget)" />
|
||||
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="Identität (Identity):" FontWeight="Bold" />
|
||||
<TextBox Text="{Binding Identity}" AcceptsReturn="True" Height="100" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Max. Schritte pro Run" />
|
||||
<TextBox Text="{Binding MaxSteps}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="Timeout (Sekunden)" />
|
||||
<TextBox Text="{Binding TimeoutSeconds}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="Seele & Werte (Soul):" FontWeight="Bold" />
|
||||
<TextBox Text="{Binding Soul}" AcceptsReturn="True" Height="100" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Max. Kontext-Tokens" />
|
||||
<TextBox Text="{Binding MaxContextTokens}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="Max. Tool-Ergebnis (Zeichen)" />
|
||||
<TextBox Text="{Binding MaxToolResultChars}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Tagesbudget ($ USD)" />
|
||||
<TextBox Text="{Binding DailyCostUsd}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="Tagesbudget (Tokens)" />
|
||||
<TextBox Text="{Binding DailyTokens}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<!-- TAB 3: Werkzeuge (Tools) -->
|
||||
<TabItem Header="Werkzeuge (Tools)">
|
||||
<ScrollViewer Padding="8">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Zugelassene Werkzeuge für diesen Agenten:" Classes="heading" />
|
||||
<ItemsControl ItemsSource="{Binding AvailableTools}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:ToolItemViewModel">
|
||||
<Grid ColumnDefinitions="200,Auto" Margin="0,4">
|
||||
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsEnabled}" Grid.Column="0" VerticalAlignment="Center" />
|
||||
<Button Content="⚙ Konfigurieren" Command="{Binding $parent[UserControl].((vm:AgentsPageViewModel)DataContext).ConfigureToolCommand}" CommandParameter="{Binding}" IsEnabled="{Binding IsEnabled}" Grid.Column="1" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ContentControl.ContentTemplate>
|
||||
</ContentControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
</TabControl>
|
||||
</DataTemplate>
|
||||
</ContentControl.ContentTemplate>
|
||||
</ContentControl>
|
||||
<GridSplitter x:Name="VerticalSplitter" Grid.Row="1" Classes="horizontal"
|
||||
ResizeDirection="Rows" Margin="0,8" />
|
||||
|
||||
<!-- ══════════════════ Unten: Werkzeuge ══════════════════ -->
|
||||
<Grid x:Name="BottomGrid" Grid.Row="2" ColumnDefinitions="0.44*,Auto,0.56*">
|
||||
|
||||
<DockPanel Grid.Column="0" Margin="0,0,14,0">
|
||||
<TextBlock DockPanel.Dock="Top" Classes="kicker" Margin="0,4,0,8"
|
||||
Text="{Binding SelectedAgent.DisplayName,
|
||||
StringFormat='ZUGELASSENE WERKZEUGE FÜR {0}'}" />
|
||||
|
||||
<Border DockPanel.Dock="Top" Classes="thead">
|
||||
<Grid ColumnDefinitions="36,1.4*,2.4*">
|
||||
<TextBlock Grid.Column="1" Classes="th" Text="WERKZEUG" />
|
||||
<TextBlock Grid.Column="2" Classes="th" Text="BESCHREIBUNG" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<ListBox Classes="table" ItemsSource="{Binding SelectedAgent.AvailableTools}"
|
||||
SelectedItem="{Binding SelectedTool}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="vm:ToolItemViewModel">
|
||||
<Border Classes="tr" Padding="7">
|
||||
<Grid ColumnDefinitions="36,1.4*,2.4*">
|
||||
<CheckBox Grid.Column="0" IsChecked="{Binding IsEnabled}"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock Grid.Column="1" Classes="td" FontWeight="SemiBold"
|
||||
Text="{Binding Name}" />
|
||||
<TextBlock Grid.Column="2" Classes="td dim" Text="{Binding Description}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
|
||||
<GridSplitter x:Name="BottomSplitter" Grid.Column="1" Classes="vertical"
|
||||
ResizeDirection="Columns" />
|
||||
|
||||
<DockPanel Grid.Column="2" Margin="4,0,0,0">
|
||||
<StackPanel DockPanel.Dock="Top" Spacing="2" Margin="0,4,0,10">
|
||||
<TextBlock Classes="kicker" Text="WERKZEUG-EINSTELLUNGEN" />
|
||||
<DockPanel>
|
||||
<TextBlock Classes="h2" VerticalAlignment="Center"
|
||||
Text="{Binding SelectedTool.Name}" />
|
||||
<Button DockPanel.Dock="Right" HorizontalAlignment="Right" Classes="toolbar primary"
|
||||
Command="{Binding SaveToolSettingsCommand}"
|
||||
IsEnabled="{Binding ToolSettings, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconSave}" Width="14" Height="14" />
|
||||
<TextBlock Text="Übernehmen" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</DockPanel>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<ContentControl Content="{Binding ToolSettings}" />
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</DockPanel>
|
||||
|
||||
</UserControl>
|
||||
|
||||
@@ -1,46 +1,101 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using ClawdDotNet.Desktop.ViewModels;
|
||||
|
||||
namespace ClawdDotNet.Desktop.Views;
|
||||
|
||||
public partial class AgentsPageView : UserControl
|
||||
{
|
||||
private AgentsPageViewModel? _viewModel;
|
||||
|
||||
public AgentsPageView()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Ein Zug am Teiler ist erst zu Ende, wenn die Maus losgelassen wird. Waehrend
|
||||
// des Ziehens zu speichern hiesse, fuer eine Bewegung ein paar Dutzend Mal die
|
||||
// Einstellungsdatei zu schreiben.
|
||||
TopSplitter.DragCompleted += (_, _) => SaveSplits();
|
||||
BottomSplitter.DragCompleted += (_, _) => SaveSplits();
|
||||
VerticalSplitter.DragCompleted += (_, _) => SaveSplits();
|
||||
}
|
||||
|
||||
protected override void OnDataContextChanged(EventArgs e)
|
||||
{
|
||||
base.OnDataContextChanged(e);
|
||||
if (DataContext is AgentsPageViewModel vm)
|
||||
{
|
||||
vm.RequestAddAgentDialog += OnRequestAddAgentDialog;
|
||||
vm.RequestToolSettingsDialog += OnRequestToolSettingsDialog;
|
||||
}
|
||||
|
||||
if (DataContext is not AgentsPageViewModel vm) return;
|
||||
|
||||
_viewModel = vm;
|
||||
vm.RequestAddAgentDialog += OnRequestAddAgentDialog;
|
||||
vm.RequestTextEditorDialog += OnRequestTextEditorDialog;
|
||||
|
||||
ApplySavedSplits(vm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setzt die gespeicherten Teilerstellungen.
|
||||
///
|
||||
/// <para>Von Hand und nicht per Bindung: <see cref="ColumnDefinition"/> und
|
||||
/// <see cref="RowDefinition"/> liegen nicht im Sichtbarkeitsbaum und erben deshalb
|
||||
/// keinen DataContext. Eine Bindung auf ihre Breite bliebe ohne Wirkung — und zwar
|
||||
/// ohne Fehlermeldung.</para>
|
||||
/// </summary>
|
||||
private void ApplySavedSplits(AgentsPageViewModel vm)
|
||||
{
|
||||
SetRatio(TopGrid.ColumnDefinitions[0], TopGrid.ColumnDefinitions[2], vm.SplitTopList);
|
||||
SetRatio(BottomGrid.ColumnDefinitions[0], BottomGrid.ColumnDefinitions[2], vm.SplitBottomList);
|
||||
|
||||
OuterGrid.RowDefinitions[0].Height = new GridLength(vm.SplitVertical, GridUnitType.Star);
|
||||
OuterGrid.RowDefinitions[2].Height = new GridLength(1 - vm.SplitVertical, GridUnitType.Star);
|
||||
}
|
||||
|
||||
private static void SetRatio(ColumnDefinition first, ColumnDefinition second, double ratio)
|
||||
{
|
||||
first.Width = new GridLength(ratio, GridUnitType.Star);
|
||||
second.Width = new GridLength(1 - ratio, GridUnitType.Star);
|
||||
}
|
||||
|
||||
private void SaveSplits()
|
||||
{
|
||||
if (_viewModel is null) return;
|
||||
|
||||
_viewModel.SaveSplits(
|
||||
RowRatio(OuterGrid.RowDefinitions[0], OuterGrid.RowDefinitions[2]),
|
||||
ColumnRatio(TopGrid.ColumnDefinitions[0], TopGrid.ColumnDefinitions[2]),
|
||||
ColumnRatio(BottomGrid.ColumnDefinitions[0], BottomGrid.ColumnDefinitions[2]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Das Verhaeltnis wird aus den tatsaechlichen Pixelbreiten gebildet, nicht aus den
|
||||
/// Sternwerten: Der GridSplitter setzt beim Ziehen mitunter absolute Breiten, und
|
||||
/// dann stuenden in den Sternwerten noch die Zahlen von vor dem Zug.
|
||||
/// </summary>
|
||||
private static double ColumnRatio(ColumnDefinition first, ColumnDefinition second)
|
||||
=> Ratio(first.ActualWidth, second.ActualWidth);
|
||||
|
||||
private static double RowRatio(RowDefinition first, RowDefinition second)
|
||||
=> Ratio(first.ActualHeight, second.ActualHeight);
|
||||
|
||||
private static double Ratio(double first, double second)
|
||||
{
|
||||
var total = first + second;
|
||||
return total > 0 ? first / total : 0.5;
|
||||
}
|
||||
|
||||
private async Task<AddAgentResultData?> OnRequestAddAgentDialog(AddAgentViewModel vm)
|
||||
{
|
||||
var topLevel = TopLevel.GetTopLevel(this);
|
||||
if (topLevel is Window window)
|
||||
{
|
||||
var dialog = new AddAgentWindow { DataContext = vm };
|
||||
await dialog.ShowDialog(window);
|
||||
return vm.Result;
|
||||
}
|
||||
return null;
|
||||
if (TopLevel.GetTopLevel(this) is not Window window) return null;
|
||||
|
||||
var dialog = new AddAgentWindow { DataContext = vm };
|
||||
await dialog.ShowDialog(window);
|
||||
return vm.Result;
|
||||
}
|
||||
|
||||
private async Task<Dictionary<string, object?>?> OnRequestToolSettingsDialog(ToolSettingsViewModel vm)
|
||||
private async Task<string?> OnRequestTextEditorDialog(string title, string text)
|
||||
{
|
||||
var topLevel = TopLevel.GetTopLevel(this);
|
||||
if (topLevel is Window window)
|
||||
{
|
||||
var dialog = new ToolSettingsWindow { DataContext = vm };
|
||||
await dialog.ShowDialog(window);
|
||||
return vm.Result;
|
||||
}
|
||||
return null;
|
||||
if (TopLevel.GetTopLevel(this) is not Window window) return null;
|
||||
|
||||
return await TextEditorWindow.ShowAsync(window, title, text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,95 +3,147 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="700"
|
||||
Padding="16"
|
||||
xmlns:ctrl="using:ClawdDotNet.Desktop.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="1100" d:DesignHeight="740"
|
||||
x:Class="ClawdDotNet.Desktop.Views.BackupPageView"
|
||||
x:DataType="vm:BackupPageViewModel">
|
||||
|
||||
<DockPanel>
|
||||
|
||||
<!-- Statusbar am unteren Rand -->
|
||||
<Border Classes="statusbar" DockPanel.Dock="Bottom">
|
||||
<TextBlock Text="{Binding StatusText}" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<TextBlock DockPanel.Dock="Bottom" Classes="caption" Margin="0,10,0,0"
|
||||
Text="{Binding StatusText}" TextWrapping="Wrap" />
|
||||
|
||||
<ScrollViewer>
|
||||
<StackPanel Spacing="16">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Spacing="24" MaxWidth="820" HorizontalAlignment="Left">
|
||||
|
||||
<TextBlock Text="Sicherung & Wiederherstellung" Classes="heading" FontSize="20" />
|
||||
<!-- ══════════════════ Manuell ══════════════════ -->
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Classes="kicker" Text="MANUELL" />
|
||||
<TextBlock Classes="h2" Text="Sicherung erstellen" />
|
||||
|
||||
<!-- Card: Neue Sicherung erstellen -->
|
||||
<Border Classes="card">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Text="Manuelle Sicherung erstellen" Classes="heading" />
|
||||
|
||||
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto">
|
||||
<TextBox Text="{Binding TargetFolder}" Grid.Column="0" PlaceholderText="Zielordner für Sicherungen..." />
|
||||
<Button Content="Ordner wählen" Click="OnBrowseTargetFolder" Grid.Column="1" Margin="8,0,0,0" />
|
||||
</Grid>
|
||||
|
||||
<!-- Zugangsdaten Optionen -->
|
||||
<TextBlock Text="Zugangsdaten (API-Schlüssel, Passwörter):" FontWeight="Bold" Margin="0,4,0,0" />
|
||||
<RadioButton Content="Zugangsdaten NICHT mit sichern (ohne Passphrase)" IsChecked="{Binding SecretsExclude}" />
|
||||
<RadioButton Content="Mit Passphrase verschlüsseln" IsChecked="{Binding SecretsPassphrase}" />
|
||||
|
||||
<StackPanel Spacing="8" IsVisible="{Binding SecretsPassphrase}" Margin="20,0,0,0">
|
||||
<TextBox Text="{Binding Passphrase}" PasswordChar="*" PlaceholderText="Passphrase eingeben" Width="300" HorizontalAlignment="Left" />
|
||||
<TextBox Text="{Binding PassphraseRepeat}" PasswordChar="*" PlaceholderText="Passphrase wiederholen" Width="300" HorizontalAlignment="Left" />
|
||||
<Grid ColumnDefinitions="*,Auto" VerticalAlignment="Bottom">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Zielordner" />
|
||||
<TextBox Text="{Binding TargetFolder}"
|
||||
PlaceholderText="Zielordner für Sicherungen…" />
|
||||
</StackPanel>
|
||||
<Button Grid.Column="1" Content="Durchsuchen…" Click="OnBrowseTargetFolder"
|
||||
Margin="8,0,0,0" VerticalAlignment="Bottom" />
|
||||
</Grid>
|
||||
|
||||
<!-- Inhalt Optionen -->
|
||||
<CheckBox Content="Protokolle (Logs) mit einschließen" IsChecked="{Binding IncludeLogs}" />
|
||||
<CheckBox Content="Chatverläufe mit einschließen" IsChecked="{Binding IncludeChatHistory}" />
|
||||
|
||||
<Button Content="▶ Sicherung jetzt erstellen" Command="{Binding CreateBackupCommand}"
|
||||
IsEnabled="{Binding !IsBusy}" Classes="accent" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Card: Automatische Sicherung -->
|
||||
<Border Classes="card">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Text="Automatische Sicherung" Classes="heading" />
|
||||
|
||||
<CheckBox Content="Automatische tägliche Sicherung aktivieren" IsChecked="{Binding AutoBackupEnabled}" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="16" IsEnabled="{Binding AutoBackupEnabled}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock Text="Uhrzeit:" VerticalAlignment="Center" />
|
||||
<TextBox Text="{Binding AutoBackupTime}" Width="80" PlaceholderText="03:00" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock Text="Max. Anzahl behalten:" VerticalAlignment="Center" />
|
||||
<NumericUpDown Value="{Binding KeepCount}" Minimum="1" Maximum="999" Width="120" />
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Classes="label" Text="Zugangsdaten" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="18">
|
||||
<RadioButton GroupName="secrets" Content="nicht mitsichern"
|
||||
IsChecked="{Binding SecretsExclude}" />
|
||||
<RadioButton GroupName="secrets" Content="mit Passphrase schützen"
|
||||
IsChecked="{Binding SecretsPassphrase}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Vorhandene Sicherungen List -->
|
||||
<Border Classes="card">
|
||||
<DockPanel Height="260">
|
||||
<StackPanel Classes="toolbar" DockPanel.Dock="Top">
|
||||
<TextBlock Text="Vorhandene Sicherungen" Classes="heading" VerticalAlignment="Center" />
|
||||
<Button Content="Aktualisieren" Command="{Binding RefreshBackupListCommand}" Margin="12,0,0,0" />
|
||||
<Button Content="Ordner öffnen" Command="{Binding ShowSelectedInFolderCommand}" IsEnabled="{Binding SelectedBackup, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
<Button Content="Löschen" Command="{Binding DeleteSelectedCommand}" IsEnabled="{Binding SelectedBackup, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
<Button Content="Wiederherstellen..." Command="{Binding RestoreSelectedCommand}" IsEnabled="{Binding SelectedBackup, Converter={x:Static ObjectConverters.IsNotNull}}" Classes="accent" />
|
||||
<StackPanel Spacing="8" Margin="24,0,0,0" IsVisible="{Binding SecretsPassphrase}">
|
||||
<TextBox Text="{Binding Passphrase}" PasswordChar="•" Width="320"
|
||||
HorizontalAlignment="Left" PlaceholderText="Passphrase eingeben" />
|
||||
<TextBox Text="{Binding PassphraseRepeat}" PasswordChar="•" Width="320"
|
||||
HorizontalAlignment="Left" PlaceholderText="Passphrase wiederholen" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Classes="label" Text="Umfang" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="18">
|
||||
<CheckBox Content="Chatverläufe" IsChecked="{Binding IncludeChatHistory}" />
|
||||
<CheckBox Content="Protokolle" IsChecked="{Binding IncludeLogs}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<Button Classes="primary" HorizontalAlignment="Left" Margin="0,4,0,0"
|
||||
Command="{Binding CreateBackupCommand}" IsEnabled="{Binding !IsBusy}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconPlay}" Width="12" Height="12" IsFilled="True" />
|
||||
<TextBlock Text="Sicherung jetzt erstellen" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<Border Classes="hr" />
|
||||
|
||||
<!-- ══════════════════ Automatik ══════════════════ -->
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock Classes="kicker" Text="AUTOMATIK" />
|
||||
<TextBlock Classes="h2" Text="Automatische Sicherung" />
|
||||
|
||||
<CheckBox Content="täglich sichern um" IsChecked="{Binding AutoBackupEnabled}" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="24" Margin="24,0,0,0"
|
||||
IsEnabled="{Binding AutoBackupEnabled}">
|
||||
<StackPanel Width="100">
|
||||
<TextBlock Classes="label" Text="Uhrzeit" />
|
||||
<TextBox Text="{Binding AutoBackupTime}" PlaceholderText="03:00" />
|
||||
</StackPanel>
|
||||
|
||||
<DataGrid ItemsSource="{Binding Backups}" SelectedItem="{Binding SelectedBackup}" AutoGenerateColumns="False" IsReadOnly="True">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Dateiname" Binding="{Binding FileName}" Width="220" />
|
||||
<DataGridTextColumn Header="Erstellt am" Binding="{Binding LastWriteTime, StringFormat='{}{0:g}'}" Width="150" />
|
||||
<DataGridTextColumn Header="Instanz" Binding="{Binding InstanceName}" Width="130" />
|
||||
<DataGridTextColumn Header="Größe" Binding="{Binding SizeFormatted}" Width="100" />
|
||||
<DataGridTextColumn Header="Zugangsdaten" Binding="{Binding SecretsInfo}" Width="*" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
<StackPanel Width="160">
|
||||
<TextBlock Classes="label" Text="Aufbewahren" />
|
||||
<TextBox Text="{Binding KeepCount}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Classes="caption" Margin="24,0,0,0" TextWrapping="Wrap"
|
||||
Text="Die Automatik sichert ohne Zugangsdaten — eine gespeicherte Passphrase wäre wirkungslos." />
|
||||
</StackPanel>
|
||||
|
||||
<Border Classes="hr" />
|
||||
|
||||
<!-- ══════════════════ Bestand ══════════════════ -->
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock Classes="kicker" Text="BESTAND" />
|
||||
|
||||
<DockPanel>
|
||||
<TextBlock Classes="h2" Text="Vorhandene Sicherungen" VerticalAlignment="Center" />
|
||||
|
||||
<WrapPanel DockPanel.Dock="Right" HorizontalAlignment="Right" Classes="toolbar"
|
||||
Margin="0">
|
||||
<Button Classes="toolbar" Content="Aktualisieren"
|
||||
Command="{Binding RefreshBackupListCommand}" />
|
||||
<Button Classes="toolbar" Content="Ordner öffnen"
|
||||
Command="{Binding ShowSelectedInFolderCommand}"
|
||||
IsEnabled="{Binding SelectedBackup, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
<Button Classes="toolbar" Content="Löschen"
|
||||
Command="{Binding DeleteSelectedCommand}"
|
||||
IsEnabled="{Binding SelectedBackup, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
<Button Classes="toolbar primary" Content="Wiederherstellen…"
|
||||
Command="{Binding RestoreSelectedCommand}"
|
||||
IsEnabled="{Binding SelectedBackup, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
</WrapPanel>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<Border Classes="thead">
|
||||
<Grid ColumnDefinitions="2.6*,1.5*,1.3*,0.9*,1.3*">
|
||||
<TextBlock Grid.Column="0" Classes="th" Text="DATEINAME" />
|
||||
<TextBlock Grid.Column="1" Classes="th" Text="ERSTELLT AM" />
|
||||
<TextBlock Grid.Column="2" Classes="th" Text="INSTANZ" />
|
||||
<TextBlock Grid.Column="3" Classes="th" Text="GRÖSSE" />
|
||||
<TextBlock Grid.Column="4" Classes="th" Text="ZUGANGSDATEN" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<ListBox Classes="table" MaxHeight="300" Margin="0,-10,0,0"
|
||||
ItemsSource="{Binding Backups}" SelectedItem="{Binding SelectedBackup}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="vm:BackupItemViewModel">
|
||||
<Border Classes="tr" Padding="7">
|
||||
<Grid ColumnDefinitions="2.6*,1.5*,1.3*,0.9*,1.3*">
|
||||
<TextBlock Grid.Column="0" Classes="td" Text="{Binding FileName}" />
|
||||
<TextBlock Grid.Column="1" Classes="td"
|
||||
Text="{Binding LastWriteTime, StringFormat='{}{0:dd.MM.yyyy HH:mm}'}" />
|
||||
<TextBlock Grid.Column="2" Classes="td" Text="{Binding InstanceName}" />
|
||||
<TextBlock Grid.Column="3" Classes="td" Text="{Binding SizeFormatted}" />
|
||||
<TextBlock Grid.Column="4" Classes="td dim" Text="{Binding SecretsInfo}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -3,23 +3,19 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
xmlns:ctrl="using:ClawdDotNet.Desktop.Controls"
|
||||
xmlns:conv="using:ClawdDotNet.Desktop.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="700"
|
||||
Padding="16"
|
||||
mc:Ignorable="d" d:DesignWidth="1100" d:DesignHeight="700"
|
||||
x:Class="ClawdDotNet.Desktop.Views.ChatPageView"
|
||||
x:DataType="vm:ChatPageViewModel">
|
||||
|
||||
<DockPanel>
|
||||
<DockPanel MaxWidth="960" HorizontalAlignment="Left">
|
||||
|
||||
<!-- Statusleiste am unteren Rand -->
|
||||
<Border Classes="statusbar" DockPanel.Dock="Bottom">
|
||||
<TextBlock Text="{Binding StatusText}" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
|
||||
<!-- Top Toolbar -->
|
||||
<StackPanel Classes="toolbar" DockPanel.Dock="Top">
|
||||
<TextBlock Text="Agent:" VerticalAlignment="Center" FontWeight="Bold" />
|
||||
<ComboBox ItemsSource="{Binding AvailableAgents}" SelectedItem="{Binding SelectedAgent}" MinWidth="200">
|
||||
<!-- Agentenauswahl und Verlaufsbefehle -->
|
||||
<StackPanel DockPanel.Dock="Top" Classes="toolbar" Margin="0,0,0,16">
|
||||
<TextBlock Text="Agent:" FontWeight="SemiBold" VerticalAlignment="Center" />
|
||||
<ComboBox ItemsSource="{Binding AvailableAgents}" SelectedItem="{Binding SelectedAgent}"
|
||||
Width="220">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding DisplayName}" />
|
||||
@@ -27,43 +23,67 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Button Content="Verlauf leeren" Command="{Binding ClearHistoryCommand}" Margin="12,0,0,0" />
|
||||
<Button Content="Abbrechen" Command="{Binding AbortCommand}" IsEnabled="{Binding IsSending}" />
|
||||
<Border Classes="sep" />
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding ClearHistoryCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconTrash}" Width="14" Height="14" />
|
||||
<TextBlock Text="Verlauf leeren" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding AbortCommand}" IsEnabled="{Binding IsSending}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconStop}" Width="12" Height="12" IsFilled="True" />
|
||||
<TextBlock Text="Abbrechen" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Eingabezeile unten -->
|
||||
<Grid DockPanel.Dock="Bottom" ColumnDefinitions="*,Auto" Margin="0,8,0,0">
|
||||
<TextBox Text="{Binding InputPrompt}" PlaceholderText="Nachricht eingeben... (Strg+Eingabe zum Senden)"
|
||||
AcceptsReturn="True" Height="60" TextWrapping="Wrap" Grid.Column="0" KeyDown="OnInputKeyDown" />
|
||||
<Button Content="Senden" Command="{Binding SendMessageCommand}" IsEnabled="{Binding !IsSending}"
|
||||
Classes="accent" VerticalAlignment="Stretch" Margin="8,0,0,0" Grid.Column="1" Width="90" />
|
||||
<!-- Statuszeile des Bereichs -->
|
||||
<TextBlock DockPanel.Dock="Bottom" Classes="caption" Margin="0,8,0,0"
|
||||
Text="{Binding StatusText}" />
|
||||
|
||||
<!-- Eingabe -->
|
||||
<Grid DockPanel.Dock="Bottom" ColumnDefinitions="*,Auto" Margin="0,12,0,0">
|
||||
<TextBox Grid.Column="0" Text="{Binding InputPrompt}"
|
||||
PlaceholderText="Nachricht eingeben… (Strg+Eingabe zum Senden)"
|
||||
AcceptsReturn="True" Height="60" TextWrapping="Wrap"
|
||||
KeyDown="OnInputKeyDown" />
|
||||
|
||||
<Button Grid.Column="1" Classes="primary" Content="Senden" Width="100"
|
||||
Command="{Binding SendMessageCommand}" IsEnabled="{Binding !IsSending}"
|
||||
VerticalAlignment="Stretch" Margin="8,0,0,0" />
|
||||
</Grid>
|
||||
|
||||
<!-- Nachrichtenverlauf (Mitte) -->
|
||||
<ScrollViewer x:Name="ChatScroller">
|
||||
<!-- Verlauf -->
|
||||
<ScrollViewer x:Name="ChatScroller" VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl ItemsSource="{Binding ChatEntries}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Spacing="10" />
|
||||
<StackPanel Spacing="12" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:ChatEntryItemViewModel">
|
||||
<!-- Distinct styling: User right-aligned bubble vs Assistant left-aligned card -->
|
||||
<DockPanel HorizontalAlignment="{Binding IsUser, Converter={x:Static conv:ChatAlignmentConverter.Instance}}">
|
||||
<Border Classes="card" CornerRadius="8" MaxWidth="680"
|
||||
Background="{Binding IsUser, Converter={x:Static conv:ChatBubbleBrushConverter.Instance}, TargetNullValue={x:Null}}">
|
||||
<StackPanel Spacing="4">
|
||||
<DockPanel>
|
||||
<TextBlock Text="{Binding Sender}" FontWeight="Bold" FontSize="12" />
|
||||
<TextBlock Text="{Binding Timestamp, StringFormat='{}{0:HH:mm}'}" Classes="caption" HorizontalAlignment="Right" Margin="8,0,0,0" />
|
||||
</DockPanel>
|
||||
<!-- Eigene Nachrichten rechts und in Akzent hinterlegt, Antworten links
|
||||
als schlichter Kasten — wie im Entwurf. -->
|
||||
<Border Classes="card" MaxWidth="660"
|
||||
Classes.accent="{Binding IsUser}"
|
||||
HorizontalAlignment="{Binding IsUser,
|
||||
Converter={x:Static conv:ChatAlignmentConverter.Instance}}">
|
||||
<StackPanel Spacing="4">
|
||||
<DockPanel>
|
||||
<TextBlock Text="{Binding Sender}" FontWeight="SemiBold" FontSize="12" />
|
||||
<TextBlock Classes="caption" DockPanel.Dock="Right" HorizontalAlignment="Right"
|
||||
Margin="8,0,0,0"
|
||||
Text="{Binding Timestamp, StringFormat='{}{0:HH:mm}'}" />
|
||||
</DockPanel>
|
||||
|
||||
<SelectableTextBlock Text="{Binding Text}" TextWrapping="Wrap" FontSize="13" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DockPanel>
|
||||
<SelectableTextBlock Text="{Binding Text}" TextWrapping="Wrap" FontSize="13.5" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
@@ -3,67 +3,111 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="600"
|
||||
xmlns:ctrl="using:ClawdDotNet.Desktop.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="1100" d:DesignHeight="700"
|
||||
x:Class="ClawdDotNet.Desktop.Views.InfoPageView"
|
||||
x:DataType="vm:InfoPageViewModel">
|
||||
|
||||
<ScrollViewer Padding="16">
|
||||
<StackPanel Spacing="16">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<!-- Etwas Luft nach aussen: Die Eckmarken des BlueprintFrame ragen ueber den
|
||||
Rahmen hinaus und wuerden sonst am Rand des Bereichs abgeschnitten. -->
|
||||
<StackPanel Spacing="26" Margin="8" MaxWidth="820" HorizontalAlignment="Left">
|
||||
|
||||
<!-- Titel -->
|
||||
<TextBlock Text="Software- & Instanz-Informationen" Classes="heading" FontSize="20" />
|
||||
<!-- Die einzige Karte des Entwurfs mit Eckmarken -->
|
||||
<ctrl:BlueprintFrame MaxWidth="420" HorizontalAlignment="Left">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Classes="h2" Text="ClawdDotNet" />
|
||||
<TextBlock Classes="muted" FontSize="13">
|
||||
<Run Text="Version" /><Run Text=" " /><Run Text="{Binding AppVersion}" /><Run Text=" · Avalonia UI · net10.0" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</ctrl:BlueprintFrame>
|
||||
|
||||
<!-- Anwendungs-Details Card -->
|
||||
<Border Classes="card">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Anwendung" Classes="heading" />
|
||||
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Version:" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding AppVersion}" />
|
||||
<!-- Anwendung -->
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock Classes="kicker" Text="ANWENDUNG" />
|
||||
<TextBlock Classes="h2" Text="Build" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Build-Summary:" FontWeight="Bold" Margin="0,4,0,0" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding BuildSummary}" Margin="0,4,0,0" TextWrapping="Wrap" />
|
||||
<Grid ColumnDefinitions="170,*" RowDefinitions="Auto,Auto" >
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Classes="label" Text="Version" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding AppVersion}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Classes="label" Text="Build-Zusammenfassung"
|
||||
Margin="0,6,0,0" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding BuildSummary}"
|
||||
TextWrapping="Wrap" Margin="0,6,0,0" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<Border Classes="hr" />
|
||||
|
||||
<!-- Instanz -->
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock Classes="kicker" Text="INSTANZ" />
|
||||
|
||||
<DockPanel>
|
||||
<TextBlock Classes="h2" Text="Aktuelle Instanz" VerticalAlignment="Center" />
|
||||
<Button DockPanel.Dock="Right" HorizontalAlignment="Right" Classes="toolbar"
|
||||
Command="{Binding OpenInstanceFolderCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconFolder}" Width="14" Height="14" />
|
||||
<TextBlock Text="Instanz-Ordner öffnen" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</DockPanel>
|
||||
|
||||
<Grid ColumnDefinitions="170,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Classes="label" Text="Name" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding InstanceName}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Classes="label" Text="ID" Margin="0,6,0,0" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Classes="mono" Text="{Binding InstanceId}"
|
||||
Margin="0,6,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Classes="label" Text="Pfad" Margin="0,6,0,0" />
|
||||
<SelectableTextBlock Grid.Row="2" Grid.Column="1" Text="{Binding InstancePath}"
|
||||
TextWrapping="Wrap" Margin="0,6,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Classes="label" Text="Log-Ordner" Margin="0,6,0,0" />
|
||||
<SelectableTextBlock Grid.Row="3" Grid.Column="1" Text="{Binding LogDirectory}"
|
||||
TextWrapping="Wrap" Margin="0,6,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Classes="label" Text="OpenRouter" Margin="0,6,0,0" />
|
||||
<TextBlock Grid.Row="4" Grid.Column="1" Text="{Binding OpenRouterStatus}" Margin="0,6,0,0" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<Border Classes="hr" />
|
||||
|
||||
<!-- Baugruppen -->
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock Classes="kicker" Text="MODULE" />
|
||||
<TextBlock Classes="h2" Text="Baugruppen" />
|
||||
|
||||
<Border Classes="thead">
|
||||
<Grid ColumnDefinitions="1.6*,0.6*,1.4*,3*">
|
||||
<TextBlock Grid.Column="0" Classes="th" Text="MODUL" />
|
||||
<TextBlock Grid.Column="1" Classes="th" Text="BUILD" />
|
||||
<TextBlock Grid.Column="2" Classes="th" Text="BUILD-DATUM" />
|
||||
<TextBlock Grid.Column="3" Classes="th" Text="ÄNDERUNGEN" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<!-- Instanz-Details Card -->
|
||||
<Border Classes="card">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Aktuelle Instanz" Classes="heading" />
|
||||
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="140,*">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Name:" FontWeight="Bold" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding InstanceName}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="ID:" FontWeight="Bold" Margin="0,4,0,0" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding InstanceId}" Margin="0,4,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Pfad:" FontWeight="Bold" Margin="0,4,0,0" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding InstancePath}" Margin="0,4,0,0" TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Log-Ordner:" FontWeight="Bold" Margin="0,4,0,0" />
|
||||
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding LogDirectory}" Margin="0,4,0,0" TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="OpenRouter Status:" FontWeight="Bold" Margin="0,4,0,0" />
|
||||
<TextBlock Grid.Row="4" Grid.Column="1" Text="{Binding OpenRouterStatus}" Margin="0,4,0,0" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Assembly Build Info Card -->
|
||||
<Border Classes="card">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Module & Baugruppen" Classes="heading" />
|
||||
<DataGrid ItemsSource="{Binding Assemblies}" AutoGenerateColumns="False" IsReadOnly="True" GridLinesVisibility="Horizontal">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Modul" Binding="{Binding ShortName}" Width="160" />
|
||||
<DataGridTextColumn Header="Build #" Binding="{Binding BuildNumber}" Width="80" />
|
||||
<DataGridTextColumn Header="Build-Datum" Binding="{Binding BuildDate}" Width="160" />
|
||||
<DataGridTextColumn Header="Änderungen" Binding="{Binding Changes}" Width="*" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<ItemsControl ItemsSource="{Binding Assemblies}" Margin="0,-10,0,0">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:AssemblyInfoItem">
|
||||
<Border Classes="tr" Padding="7">
|
||||
<Grid ColumnDefinitions="1.6*,0.6*,1.4*,3*">
|
||||
<TextBlock Grid.Column="0" Classes="td" FontWeight="SemiBold" Text="{Binding ShortName}" />
|
||||
<TextBlock Grid.Column="1" Classes="td" Text="{Binding BuildNumber}" />
|
||||
<TextBlock Grid.Column="2" Classes="td dim" Text="{Binding BuildDate}" />
|
||||
<TextBlock Grid.Column="3" Classes="td dim" Text="{Binding Changes}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -3,71 +3,121 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
xmlns:models="using:ClawdDotNet.App.Models"
|
||||
mc:Ignorable="d" d:DesignWidth="720" d:DesignHeight="460"
|
||||
xmlns:ctrl="using:ClawdDotNet.Desktop.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="560"
|
||||
x:Class="ClawdDotNet.Desktop.Views.InstancePickerWindow"
|
||||
x:DataType="vm:InstancePickerViewModel"
|
||||
Icon="/Assets/app.ico"
|
||||
Title="ClawdDotNet – Instanz wählen"
|
||||
Width="720" Height="460"
|
||||
Title="ClawdDotNet – Instanz"
|
||||
Width="640" Height="560"
|
||||
MinWidth="520" MinHeight="420"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<DockPanel Margin="16">
|
||||
<!-- Rahmen mit Eckmarken wie im Entwurf; 12 Punkte Abstand, weil die Marken nach
|
||||
aussen ragen. -->
|
||||
<ctrl:BlueprintFrame Margin="12" Padding="16">
|
||||
<DockPanel>
|
||||
|
||||
<TextBlock DockPanel.Dock="Top" Classes="heading" Text="Instanz wählen" />
|
||||
|
||||
<TextBlock DockPanel.Dock="Top" Classes="caption" Margin="0,0,0,12"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding InstancesDirectory, StringFormat='Verzeichnis: {0}'}" />
|
||||
|
||||
<!-- Fehlerzeile: nimmt keinen Platz weg, solange nichts anliegt -->
|
||||
<TextBlock DockPanel.Dock="Bottom"
|
||||
Margin="0,8,0,0"
|
||||
Foreground="{DynamicResource SystemFillColorCriticalBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding ErrorText}"
|
||||
IsVisible="{Binding ErrorText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||
|
||||
<!-- Aktionen -->
|
||||
<Grid DockPanel.Dock="Bottom" ColumnDefinitions="*,Auto,Auto" Margin="0,12,0,0">
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="6" Grid.Column="0">
|
||||
<TextBox Width="200"
|
||||
Watermark="Name der neuen Instanz"
|
||||
Text="{Binding NewInstanceName}" />
|
||||
<Button Content="Anlegen" Command="{Binding CreateCommand}" />
|
||||
<StackPanel DockPanel.Dock="Top" Spacing="6" Margin="0,0,0,14">
|
||||
<TextBlock Classes="h1" Text="{Binding Title}" />
|
||||
<TextBlock Classes="muted" FontSize="13" Text="{Binding Hint}" TextWrapping="Wrap" />
|
||||
<TextBlock Classes="caption" TextWrapping="Wrap"
|
||||
Text="{Binding InstancesDirectory, StringFormat='Verzeichnis: {0}'}" />
|
||||
</StackPanel>
|
||||
|
||||
<Button Grid.Column="1" Content="Abbrechen" Margin="0,0,6,0"
|
||||
Command="{Binding CancelCommand}" />
|
||||
<!-- Fehlerzeile: nimmt keinen Platz weg, solange nichts anliegt -->
|
||||
<TextBlock DockPanel.Dock="Bottom" Margin="0,10,0,0" TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource DangerBrush}"
|
||||
Text="{Binding ErrorText}"
|
||||
IsVisible="{Binding ErrorText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||
|
||||
<Button Grid.Column="2" Content="Starten"
|
||||
Classes="accent"
|
||||
IsDefault="True"
|
||||
Command="{Binding StartCommand}" />
|
||||
</Grid>
|
||||
<!-- Neue Instanz anlegen -->
|
||||
<StackPanel DockPanel.Dock="Bottom" Spacing="10" Margin="0,14,0,0">
|
||||
<Border Classes="hr" />
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBox Grid.Column="0" Text="{Binding NewInstanceName}"
|
||||
PlaceholderText="Name der neuen Instanz" />
|
||||
<Button Grid.Column="1" Classes="primary" Margin="8,0,0,0"
|
||||
Command="{Binding CreateCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconPlus}" Width="14" Height="14" />
|
||||
<TextBlock Text="Anlegen" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Liste -->
|
||||
<DataGrid ItemsSource="{Binding Instances}"
|
||||
SelectedItem="{Binding Selected}"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
SelectionMode="Single"
|
||||
GridLinesVisibility="Horizontal">
|
||||
<!-- Abschluss -->
|
||||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" Spacing="8"
|
||||
HorizontalAlignment="Right" Margin="0,14,0,0">
|
||||
<Button Content="Abbrechen" Command="{Binding CancelCommand}" IsCancel="True" />
|
||||
<Button Classes="primary" Content="{Binding ConfirmLabel}" IsDefault="True"
|
||||
Command="{Binding StartCommand}" />
|
||||
</StackPanel>
|
||||
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Instanzname" Width="2*"
|
||||
Binding="{Binding InstanceName, x:DataType=models:InstanceInfo}" />
|
||||
<DataGridTextColumn Header="Ordner" Width="2*"
|
||||
Binding="{Binding FolderName, x:DataType=models:InstanceInfo}" />
|
||||
<DataGridTextColumn Header="Agenten" Width="Auto"
|
||||
Binding="{Binding AgentCount, x:DataType=models:InstanceInfo}" />
|
||||
<DataGridTextColumn Header="API-Key" Width="Auto"
|
||||
Binding="{Binding ApiKeyStatus, x:DataType=models:InstanceInfo}" />
|
||||
</DataGrid.Columns>
|
||||
<!-- Liste -->
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl x:Name="InstanceList" ItemsSource="{Binding Instances}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Spacing="6" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
</DataGrid>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:InstanceChoiceViewModel">
|
||||
<Border Classes="card" Padding="8,8">
|
||||
<DockPanel>
|
||||
|
||||
</DockPanel>
|
||||
<!-- Monogramm mit Statuspunkt -->
|
||||
<Panel DockPanel.Dock="Left" Width="30" Height="30" Margin="0,0,10,0"
|
||||
VerticalAlignment="Center">
|
||||
<Border Width="26" Height="26" Background="{DynamicResource Accent600Brush}"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<TextBlock Classes="h2" FontSize="10" Text="{Binding Monogram}"
|
||||
Foreground="{DynamicResource Neutral100Brush}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
|
||||
<Ellipse Width="10" Height="10" HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Stroke="{DynamicResource SurfaceBrush}" StrokeThickness="2"
|
||||
Fill="{DynamicResource SuccessBrush}"
|
||||
IsVisible="{Binding IsReady}"
|
||||
ToolTip.Tip="API-Key hinterlegt" />
|
||||
<Ellipse Width="10" Height="10" HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Stroke="{DynamicResource SurfaceBrush}" StrokeThickness="2"
|
||||
Fill="{DynamicResource Neutral500Brush}"
|
||||
IsVisible="{Binding !IsReady}"
|
||||
ToolTip.Tip="Kein API-Key — Agenten bleiben deaktiviert" />
|
||||
</Panel>
|
||||
|
||||
<Button DockPanel.Dock="Right" Classes="toolbar" VerticalAlignment="Center"
|
||||
Content="{Binding $parent[ItemsControl].((vm:InstancePickerViewModel)DataContext).ConfirmLabel}"
|
||||
Command="{Binding $parent[ItemsControl].((vm:InstancePickerViewModel)DataContext).ActivateCommand}"
|
||||
CommandParameter="{Binding}" />
|
||||
|
||||
<Border DockPanel.Dock="Right" Classes="tag ok" Margin="0,0,8,0"
|
||||
IsVisible="{Binding IsCurrent}">
|
||||
<TextBlock Text="Aktiv" />
|
||||
</Border>
|
||||
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<TextBlock Classes="h2" FontSize="13.5" Text="{Binding InstanceName}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<TextBlock Classes="caption" Text="{Binding Summary}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
</DockPanel>
|
||||
</ctrl:BlueprintFrame>
|
||||
|
||||
</Window>
|
||||
|
||||
@@ -10,15 +10,21 @@ public partial class InstancePickerWindow : Window
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Doppelklick auf eine Zeile startet — wie in der WinForms-Fassung. Das laesst
|
||||
// sich nicht sinnvoll als Befehl binden, weil DataGrid kein DoubleTapped je
|
||||
// Zeile anbietet; deshalb hier und nicht im Ansichtsmodell.
|
||||
AddHandler(DoubleTappedEvent, OnDoubleTapped, handledEventsToo: false);
|
||||
// Doppelklick auf eine Zeile bestaetigt — wie in der WinForms-Fassung.
|
||||
//
|
||||
// Der Behandler haengt an der Liste und nicht mehr am Fenster: Am Fenster loeste
|
||||
// auch ein Doppelklick ins Namensfeld die Auswahl aus, was seit dem Umbau
|
||||
// deutlich leichter passiert, weil das Feld gross und mittig steht.
|
||||
InstanceList.AddHandler(DoubleTappedEvent, OnRowDoubleTapped, handledEventsToo: false);
|
||||
}
|
||||
|
||||
private void OnDoubleTapped(object? sender, TappedEventArgs e)
|
||||
private void OnRowDoubleTapped(object? sender, TappedEventArgs e)
|
||||
{
|
||||
if (DataContext is InstancePickerViewModel vm && vm.StartCommand.CanExecute(null))
|
||||
vm.StartCommand.Execute(null);
|
||||
if (DataContext is not InstancePickerViewModel vm) return;
|
||||
|
||||
// Getroffen wird irgendein Element in der Zeile; der Datenkontext ist derselbe.
|
||||
if ((e.Source as Control)?.DataContext is not InstanceChoiceViewModel choice) return;
|
||||
|
||||
vm.ActivateCommand.Execute(choice);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
</Border>
|
||||
|
||||
<TextBox Watermark="XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"
|
||||
<TextBox PlaceholderText="XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"
|
||||
Text="{Binding Key}"
|
||||
FontFamily="{DynamicResource ContentControlThemeFontFamily}" />
|
||||
|
||||
|
||||
@@ -4,33 +4,50 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
xmlns:svc="using:ClawdDotNet.App.Services"
|
||||
xmlns:ctrl="using:ClawdDotNet.Desktop.Controls"
|
||||
xmlns:conv="using:ClawdDotNet.Desktop.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="600"
|
||||
mc:Ignorable="d" d:DesignWidth="1100" d:DesignHeight="700"
|
||||
x:Class="ClawdDotNet.Desktop.Views.LogPageView"
|
||||
x:DataType="vm:LogPageViewModel">
|
||||
|
||||
<DockPanel>
|
||||
|
||||
<!-- Werkzeugleiste -->
|
||||
<StackPanel Classes="toolbar" DockPanel.Dock="Top">
|
||||
<StackPanel DockPanel.Dock="Top" Classes="toolbar">
|
||||
|
||||
<TextBlock Text="Modul:" VerticalAlignment="Center" />
|
||||
<ComboBox ItemsSource="{Binding Modules}"
|
||||
SelectedItem="{Binding SelectedModule}"
|
||||
MinWidth="180" />
|
||||
<TextBlock Text="Modul:" FontSize="12.5" VerticalAlignment="Center" />
|
||||
<ComboBox ItemsSource="{Binding Modules}" SelectedItem="{Binding SelectedModule}"
|
||||
Width="180" MinHeight="30" Padding="8,3" />
|
||||
|
||||
<TextBlock Text="Stufe:" VerticalAlignment="Center" Margin="12,0,0,0" />
|
||||
<ComboBox ItemsSource="{Binding Levels}"
|
||||
SelectedItem="{Binding SelectedLevel}"
|
||||
MinWidth="100" />
|
||||
<TextBlock Text="Stufe:" FontSize="12.5" VerticalAlignment="Center" Margin="6,0,0,0" />
|
||||
<ComboBox ItemsSource="{Binding Levels}" SelectedItem="{Binding SelectedLevel}"
|
||||
Width="120" MinHeight="30" Padding="8,3" />
|
||||
|
||||
<Button Content="Aktualisieren" Command="{Binding RefreshModulesCommand}" Margin="12,0,0,0" />
|
||||
<Button Content="Leeren" Command="{Binding ClearCommand}" />
|
||||
<Button Content="Ordner öffnen" Command="{Binding OpenFolderCommand}" />
|
||||
<Border Classes="sep" />
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding RefreshModulesCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconRefresh}" Width="14" Height="14" />
|
||||
<TextBlock Text="Aktualisieren" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding ClearCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconTrash}" Width="14" Height="14" />
|
||||
<TextBlock Text="Leeren" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding OpenFolderCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconFolder}" Width="14" Height="14" />
|
||||
<TextBlock Text="Ordner öffnen" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<CheckBox Content="Mitlaufen" IsChecked="{Binding FollowTail}"
|
||||
VerticalAlignment="Center" Margin="12,0,0,0" />
|
||||
|
||||
VerticalAlignment="Center" Margin="8,0,0,0" />
|
||||
</StackPanel>
|
||||
|
||||
<!--
|
||||
@@ -38,29 +55,41 @@
|
||||
einzelne Zeile herauskopieren, und die Virtualisierung des ItemsControl haelt auch
|
||||
bei 2000 Zeilen die Darstellung fluessig.
|
||||
-->
|
||||
<ScrollViewer Name="Scroller" Padding="8">
|
||||
<ItemsControl ItemsSource="{Binding Lines}">
|
||||
<Border Classes="console">
|
||||
<ScrollViewer Name="Scroller" VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Auto">
|
||||
<ItemsControl ItemsSource="{Binding Lines}">
|
||||
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="svc:LogLine">
|
||||
<SelectableTextBlock Text="{Binding Text}"
|
||||
FontFamily="Consolas, Menlo, DejaVu Sans Mono, monospace"
|
||||
FontSize="12"
|
||||
TextWrapping="NoWrap"
|
||||
Foreground="{Binding Level,
|
||||
Converter={x:Static conv:LogLevelBrushConverter.Instance},
|
||||
TargetNullValue={x:Null}}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="svc:LogLine">
|
||||
<!-- Nur Warnung und Fehler werden hervorgehoben. Alles einzufärben macht
|
||||
die Ansicht unruhig und lenkt von genau den Zeilen ab, die auffallen
|
||||
sollen; der Rest behält die Vordergrundfarbe des Themas. -->
|
||||
<SelectableTextBlock Classes="mono"
|
||||
Classes.warn="{Binding Level,
|
||||
Converter={x:Static conv:StringEqualsConverter.Instance},
|
||||
ConverterParameter=WRN}"
|
||||
Classes.err="{Binding Level,
|
||||
Converter={x:Static conv:StringEqualsConverter.Instance},
|
||||
ConverterParameter=ERR}"
|
||||
Classes.ftl="{Binding Level,
|
||||
Converter={x:Static conv:StringEqualsConverter.Instance},
|
||||
ConverterParameter=FTL}"
|
||||
Text="{Binding Text}"
|
||||
LineHeight="21"
|
||||
TextWrapping="NoWrap" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
</DockPanel>
|
||||
|
||||
|
||||
@@ -3,70 +3,224 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="1100" d:DesignHeight="700"
|
||||
xmlns:ctrl="using:ClawdDotNet.Desktop.Controls"
|
||||
xmlns:conv="using:ClawdDotNet.Desktop.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="1440" d:DesignHeight="860"
|
||||
x:Class="ClawdDotNet.Desktop.Views.MainWindow"
|
||||
x:DataType="vm:MainWindowViewModel"
|
||||
Icon="/Assets/app.ico"
|
||||
Title="ClawdDotNet"
|
||||
Width="1100" Height="700"
|
||||
MinWidth="800" MinHeight="500">
|
||||
Width="1440" Height="860"
|
||||
MinWidth="960" MinHeight="600">
|
||||
|
||||
<Design.DataContext>
|
||||
<vm:MainWindowViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<DockPanel>
|
||||
<Grid ColumnDefinitions="Auto,*">
|
||||
|
||||
<!-- Haupt-Menüleiste -->
|
||||
<Menu DockPanel.Dock="Top">
|
||||
<MenuItem Header="_Datei">
|
||||
<MenuItem Header="_Beenden" Command="{Binding ExitCommand}" InputGesture="Alt+F4" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Bearbeiten">
|
||||
<MenuItem Header="_Instanz-Ordner öffnen" Command="{Binding OpenInstanceFolderCommand}" />
|
||||
<MenuItem Header="_Log-Ordner öffnen" Command="{Binding OpenLogFolderCommand}" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Instanz">
|
||||
<MenuItem Header="_Instanz-Manager..." Command="{Binding OpenInstanceManagerCommand}" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Ansicht">
|
||||
<MenuItem Header="_Chat" Command="{Binding SelectPageByTitleCommand}" CommandParameter="Chat" />
|
||||
<MenuItem Header="_Logs" Command="{Binding SelectPageByTitleCommand}" CommandParameter="Logs" />
|
||||
<MenuItem Header="_Agenten" Command="{Binding SelectPageByTitleCommand}" CommandParameter="Agenten" />
|
||||
<MenuItem Header="_Einstellungen" Command="{Binding SelectPageByTitleCommand}" CommandParameter="Einstellungen" />
|
||||
<MenuItem Header="_Aufgaben" Command="{Binding SelectPageByTitleCommand}" CommandParameter="Aufgaben" />
|
||||
<MenuItem Header="_Sicherung" Command="{Binding SelectPageByTitleCommand}" CommandParameter="Sicherung" />
|
||||
<MenuItem Header="_Info" Command="{Binding SelectPageByTitleCommand}" CommandParameter="Info" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Hilfe">
|
||||
<MenuItem Header="_Info" Command="{Binding SelectPageByTitleCommand}" CommandParameter="Info" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<!-- ══════════════════════════ Seitenleiste ══════════════════════════ -->
|
||||
<Border Grid.Column="0" Classes="sidebar" Width="{Binding SidebarWidth}">
|
||||
<DockPanel>
|
||||
|
||||
<!-- Statusleiste -->
|
||||
<Border Classes="statusbar" DockPanel.Dock="Bottom">
|
||||
<TextBlock Classes="caption" Text="{Binding StatusText}" />
|
||||
<!-- Marke und Einklapp-Schalter -->
|
||||
<Border DockPanel.Dock="Top" BorderBrush="{DynamicResource DividerBrush}"
|
||||
BorderThickness="0,0,0,1" Padding="14">
|
||||
<DockPanel>
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconLogo}" Width="20" Height="20"
|
||||
Foreground="{DynamicResource AccentBrush}"
|
||||
DockPanel.Dock="Left" VerticalAlignment="Center" />
|
||||
|
||||
<Button DockPanel.Dock="Right" Classes="flat"
|
||||
Command="{Binding ToggleSidebarCommand}"
|
||||
ToolTip.Tip="Seitenleiste ein-/ausklappen"
|
||||
Padding="4" VerticalAlignment="Center">
|
||||
<ctrl:StrokeIcon Width="14" Height="14"
|
||||
Data="{Binding IsSidebarCollapsed,
|
||||
Converter={x:Static conv:SidebarChevronConverter.Instance}}" />
|
||||
</Button>
|
||||
|
||||
<TextBlock Classes="h2" Text="ClawdDotNet" Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsSidebarExpanded}" />
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Fußzeile: Version -->
|
||||
<TextBlock DockPanel.Dock="Bottom" Classes="caption" Margin="16,10"
|
||||
Text="{Binding VersionText}"
|
||||
IsVisible="{Binding IsSidebarExpanded}" />
|
||||
|
||||
<!-- Instanz-Schalter: der einzige Knopf mit Akzentrahmen, weil er als
|
||||
einziger die ganze Anwendung umschaltet. -->
|
||||
<Button DockPanel.Dock="Top" Classes="instance" Margin="12,12,12,0"
|
||||
Command="{Binding OpenInstanceManagerCommand}"
|
||||
ToolTip.Tip="Instanz wechseln">
|
||||
<DockPanel>
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconLogo}" Width="15" Height="15"
|
||||
DockPanel.Dock="Left" VerticalAlignment="Center" />
|
||||
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconChevronDown}" Width="14" Height="14"
|
||||
DockPanel.Dock="Right" VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsSidebarExpanded}" />
|
||||
|
||||
<StackPanel Margin="8,0,0,0" IsVisible="{Binding IsSidebarExpanded}">
|
||||
<TextBlock Text="INSTANZ" FontSize="9" LetterSpacing="1"
|
||||
Foreground="{DynamicResource AccentBrush}" />
|
||||
<TextBlock Classes="h2" FontSize="13" Text="{Binding InstanceName}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Button>
|
||||
|
||||
<!-- Navigation -->
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" Padding="10,14">
|
||||
<ItemsControl ItemsSource="{Binding NavGroups}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Spacing="18" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:NavGroupViewModel">
|
||||
<StackPanel Spacing="2">
|
||||
|
||||
<TextBlock Classes="navgroup" Text="{Binding Title}"
|
||||
IsVisible="{Binding IsTitleVisible}" />
|
||||
|
||||
<ItemsControl ItemsSource="{Binding Items}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Spacing="2" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:NavItemViewModel">
|
||||
<Button Classes="nav"
|
||||
Classes.selected="{Binding IsSelected}"
|
||||
Classes.collapsed="{Binding IsCollapsed}"
|
||||
Command="{Binding SelectCommand}"
|
||||
ToolTip.Tip="{Binding Title}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<ctrl:StrokeIcon Width="17" Height="17"
|
||||
VerticalAlignment="Center"
|
||||
Data="{Binding IconKey,
|
||||
Converter={x:Static conv:IconConverter.Instance}}" />
|
||||
<TextBlock Text="{Binding Title}" VerticalAlignment="Center"
|
||||
IsVisible="{Binding !IsCollapsed}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Bereiche (Seitenleiste links) -->
|
||||
<TabControl ItemsSource="{Binding Pages}"
|
||||
SelectedItem="{Binding SelectedPage}"
|
||||
TabStripPlacement="Left">
|
||||
<!-- ══════════════════════════ Hauptbereich ══════════════════════════ -->
|
||||
<DockPanel Grid.Column="1">
|
||||
|
||||
<TabControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:PageViewModel">
|
||||
<TextBlock Text="{Binding Title}" Margin="4,8" />
|
||||
</DataTemplate>
|
||||
</TabControl.ItemTemplate>
|
||||
<!-- Kopfzeile -->
|
||||
<Border DockPanel.Dock="Top" Classes="topbar">
|
||||
<DockPanel>
|
||||
<TextBlock Classes="h1" Text="{Binding PageTitle}" VerticalAlignment="Center" />
|
||||
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate DataType="vm:PageViewModel">
|
||||
<ContentControl Content="{Binding}" />
|
||||
</DataTemplate>
|
||||
</TabControl.ContentTemplate>
|
||||
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" Spacing="10"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<TextBlock Classes="caption" Text="{Binding ThemeLabel}" VerticalAlignment="Center" />
|
||||
<Button Classes="icon" Command="{Binding ToggleThemeCommand}"
|
||||
ToolTip.Tip="Dunkelmodus umschalten">
|
||||
<ctrl:StrokeIcon Width="16" Height="16"
|
||||
Data="{Binding ThemeIconKey,
|
||||
Converter={x:Static conv:IconConverter.Instance}}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
</TabControl>
|
||||
<!-- Benachrichtigungsleiste: nimmt keinen Platz, solange nichts anliegt -->
|
||||
<ItemsControl DockPanel.Dock="Top" ItemsSource="{Binding Notifications}"
|
||||
IsVisible="{Binding HasNotifications}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:NotificationViewModel">
|
||||
<Panel>
|
||||
<!-- Zwei Grundflächen statt eines umgerechneten Pinsels: So bleiben die
|
||||
Farben an DynamicResource gebunden und wechseln mit dem Thema mit. -->
|
||||
<Border Background="{DynamicResource DangerBarBrush}"
|
||||
BorderBrush="{DynamicResource DangerBrush}" BorderThickness="3,0,0,0"
|
||||
IsVisible="{Binding IsError}" />
|
||||
<Border Background="{DynamicResource WarningSoftBrush}"
|
||||
BorderBrush="{DynamicResource WarningBrush}" BorderThickness="3,0,0,0"
|
||||
IsVisible="{Binding IsWarning}" />
|
||||
|
||||
</DockPanel>
|
||||
<DockPanel Margin="24,8">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconAlert}" Width="15" Height="15"
|
||||
DockPanel.Dock="Left" VerticalAlignment="Center"
|
||||
Margin="0,0,10,0" />
|
||||
|
||||
<Button DockPanel.Dock="Right" Classes="flat" Padding="2"
|
||||
Command="{Binding DismissCommand}" ToolTip.Tip="Ausblenden"
|
||||
VerticalAlignment="Center">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconClose}" Width="13" Height="13" />
|
||||
</Button>
|
||||
|
||||
<TextBlock FontSize="12.5" VerticalAlignment="Center" TextWrapping="Wrap">
|
||||
<Run Text="{Binding InstanceName}" FontWeight="SemiBold" /><Run Text=": " /><Run Text="{Binding Text}" />
|
||||
</TextBlock>
|
||||
</DockPanel>
|
||||
</Panel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- Statusleiste -->
|
||||
<Border DockPanel.Dock="Bottom" Classes="statusbar">
|
||||
<DockPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock Classes="caption" Text="{Binding InstanceName, StringFormat='Instanz: {0}'}" />
|
||||
<TextBlock Classes="caption" Text="·" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
|
||||
<Ellipse Width="7" Height="7" VerticalAlignment="Center"
|
||||
Fill="{DynamicResource SuccessBrush}"
|
||||
IsVisible="{Binding IsApiReachable}" />
|
||||
<Ellipse Width="7" Height="7" VerticalAlignment="Center"
|
||||
Fill="{DynamicResource DangerBrush}"
|
||||
IsVisible="{Binding !IsApiReachable}" />
|
||||
<TextBlock Classes="caption" Text="{Binding ApiStatusText, StringFormat='API {0}'}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Classes="caption" Text="·"
|
||||
IsVisible="{Binding UsageText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||
<TextBlock Classes="caption" Text="{Binding UsageText}"
|
||||
ToolTip.Tip="{Binding UsageTooltip}"
|
||||
IsVisible="{Binding UsageText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||
|
||||
<!-- Der Trenner gehoert zum Abschnitt dahinter: Ohne Guthabenangabe stuende
|
||||
sonst ein Punkt am Ende der Zeile, der nichts mehr trennt. -->
|
||||
<TextBlock Classes="caption" Text="·"
|
||||
IsVisible="{Binding CreditsText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||
<TextBlock Classes="caption" Text="{Binding CreditsText}"
|
||||
IsVisible="{Binding CreditsText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock DockPanel.Dock="Right" Classes="caption" HorizontalAlignment="Right"
|
||||
Text="{Binding ThemeLabel}" />
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Inhalt -->
|
||||
<ContentControl Content="{Binding SelectedPage}" Margin="20" />
|
||||
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
||||
</Window>
|
||||
|
||||
@@ -13,16 +13,12 @@ public partial class MainWindow : Window
|
||||
protected override void OnDataContextChanged(EventArgs e)
|
||||
{
|
||||
base.OnDataContextChanged(e);
|
||||
if (DataContext is MainWindowViewModel vm)
|
||||
{
|
||||
vm.RequestExit += Close;
|
||||
vm.RequestInstanceManagerDialog += OnOpenInstanceManager;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnOpenInstanceManager()
|
||||
{
|
||||
var dialog = new InstancePickerWindow();
|
||||
await dialog.ShowDialog(this);
|
||||
// Der Instanzwechsel haengt nicht mehr hier, sondern in App: Er braucht den
|
||||
// Aufbau und die Lebensdauer der Anwendung, und beides kennt das Fenster nicht.
|
||||
// Vorher wurde hier ein InstancePickerWindow ganz ohne DataContext geoeffnet —
|
||||
// ein leerer Dialog, dessen Auswahl niemand ausgewertet hat.
|
||||
if (DataContext is MainWindowViewModel vm)
|
||||
vm.RequestExit += Close;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,94 +3,128 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="700"
|
||||
Padding="16"
|
||||
xmlns:ctrl="using:ClawdDotNet.Desktop.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="1100" d:DesignHeight="740"
|
||||
x:Class="ClawdDotNet.Desktop.Views.SettingsPageView"
|
||||
x:DataType="vm:SettingsPageViewModel">
|
||||
|
||||
<DockPanel>
|
||||
|
||||
<!-- Statusbar am unteren Rand -->
|
||||
<Border Classes="statusbar" DockPanel.Dock="Bottom">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="8">
|
||||
<Button Content="Verwerfen / Zurücksetzen" Command="{Binding LoadFromHostCommand}" />
|
||||
<Button Content="Speichern" Command="{Binding SaveSettingsCommand}" Classes="accent" />
|
||||
<!-- Aktionen unten: Der Entwurf setzt flache Felder ohne Karten, damit bleibt am
|
||||
unteren Rand nur die Zeile mit Verwerfen und Speichern. -->
|
||||
<Border DockPanel.Dock="Bottom" BorderBrush="{DynamicResource DividerBrush}"
|
||||
BorderThickness="0,1,0,0" Padding="0,12,0,0" Margin="0,16,0,0">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right">
|
||||
<Button Content="Verwerfen" Command="{Binding LoadFromHostCommand}" />
|
||||
<Button Classes="primary" Command="{Binding SaveSettingsCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconSave}" Width="14" Height="14" />
|
||||
<TextBlock Text="Speichern" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<ScrollViewer>
|
||||
<StackPanel Spacing="16">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Spacing="26" MaxWidth="680" HorizontalAlignment="Left">
|
||||
|
||||
<TextBlock Text="Anwendungs- & Instanz-Einstellungen" Classes="heading" FontSize="20" />
|
||||
<!-- ══════════════════ Instanz ══════════════════ -->
|
||||
<StackPanel Spacing="14">
|
||||
<TextBlock Classes="kicker" Text="INSTANZ" />
|
||||
|
||||
<!-- Card: Instanz-Einstellungen -->
|
||||
<Border Classes="card">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Text="Aktuelle Instanz" Classes="heading" />
|
||||
|
||||
<Grid ColumnDefinitions="180,*" RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Instanz-Name:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding InstanceName}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="OpenRouter API-Key:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding OpenRouterApiKey}" PasswordChar="*" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Tagesbudget ($ USD):" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<NumericUpDown Grid.Row="2" Grid.Column="1" Value="{Binding InstanceDailyCostUsd}" Minimum="0" Maximum="10000" FormatString="F2" Width="140" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Tagesbudget (Tokens):" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding InstanceDailyTokens}" Minimum="0" Maximum="100000000" FormatString="0" Width="180" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
</Grid>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Instanz-Name" />
|
||||
<TextBox Text="{Binding InstanceName}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Card: Allgemein & Pfade -->
|
||||
<Border Classes="card">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Text="Allgemein & Pfade" Classes="heading" />
|
||||
|
||||
<Grid ColumnDefinitions="180,*" RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Log-Verzeichnis:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding LogDirectory}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Instanzen-Verzeichnis:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding InstancesDirectory}" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Minimaler Log-Level:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<ComboBox Grid.Row="2" Grid.Column="1" ItemsSource="{Binding LogLevels}" SelectedItem="{Binding MinimumLogLevel}" Width="140" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="OpenRouter Base-URL:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding OpenRouterBaseUrl}" Margin="0,8,0,0" />
|
||||
</Grid>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="OpenRouter API-Key" />
|
||||
<TextBox Text="{Binding OpenRouterApiKey}" PasswordChar="•" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Card: Deploymentcenter & Lizenz -->
|
||||
<Border Classes="card">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Text="Deploymentcenter & Lizenz" Classes="heading" />
|
||||
<Grid ColumnDefinitions="*,16,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Tagesbudget ($ USD)" />
|
||||
<TextBox Text="{Binding InstanceDailyCostUsd}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="Tagesbudget (Tokens)" />
|
||||
<TextBox Text="{Binding InstanceDailyTokens}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<Grid ColumnDefinitions="180,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Server-URL:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding DeploymentcenterUrl}" />
|
||||
<Border Classes="hr" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Master-Token:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding DeploymentcenterToken}" PasswordChar="*" Margin="0,8,0,0" />
|
||||
<!-- ══════════════════ Allgemein ══════════════════ -->
|
||||
<StackPanel Spacing="14">
|
||||
<TextBlock Classes="kicker" Text="ALLGEMEIN" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Update-Kanal:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<ComboBox Grid.Row="2" Grid.Column="1" ItemsSource="{Binding UpdateChannels}" SelectedItem="{Binding UpdateChannel}" Width="140" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Umgebung:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<ComboBox Grid.Row="3" Grid.Column="1" ItemsSource="{Binding Environments}" SelectedItem="{Binding DeploymentcenterEnvironment}" Width="160" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="Lizenzschlüssel:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding LicenseKey}" PasswordChar="*" Margin="0,8,0,0" />
|
||||
</Grid>
|
||||
|
||||
<CheckBox Content="Fehler automatisch an Deploymentcenter melden" IsChecked="{Binding ErrorReportingEnabled}" Margin="0,4,0,0" />
|
||||
<CheckBox Content="Beim Start automatisch auf Updates prüfen" IsChecked="{Binding UpdateCheckEnabled}" />
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Log-Verzeichnis" />
|
||||
<TextBox Text="{Binding LogDirectory}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Instanzen-Verzeichnis" />
|
||||
<TextBox Text="{Binding InstancesDirectory}" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid ColumnDefinitions="*,16,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Minimaler Log-Level" />
|
||||
<ComboBox ItemsSource="{Binding LogLevels}" SelectedItem="{Binding MinimumLogLevel}"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="OpenRouter Base-URL" />
|
||||
<TextBox Text="{Binding OpenRouterBaseUrl}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<Border Classes="hr" />
|
||||
|
||||
<!-- ══════════════════ Deploymentcenter ══════════════════ -->
|
||||
<StackPanel Spacing="14">
|
||||
<TextBlock Classes="kicker" Text="DEPLOYMENTCENTER & LIZENZ" />
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Server-URL" />
|
||||
<TextBox Text="{Binding DeploymentcenterUrl}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Master-Token" />
|
||||
<TextBox Text="{Binding DeploymentcenterToken}" PasswordChar="•" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid ColumnDefinitions="*,16,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Update-Kanal" />
|
||||
<ComboBox ItemsSource="{Binding UpdateChannels}" SelectedItem="{Binding UpdateChannel}"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="Umgebung" />
|
||||
<ComboBox ItemsSource="{Binding Environments}"
|
||||
SelectedItem="{Binding DeploymentcenterEnvironment}"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Lizenzschlüssel" />
|
||||
<TextBox Text="{Binding LicenseKey}" PasswordChar="•" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="10" Margin="0,4,0,0">
|
||||
<CheckBox Content="Fehler automatisch an Deploymentcenter melden"
|
||||
IsChecked="{Binding ErrorReportingEnabled}" />
|
||||
<CheckBox Content="Beim Start automatisch auf Updates prüfen"
|
||||
IsChecked="{Binding UpdateCheckEnabled}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -3,100 +3,255 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="700"
|
||||
Padding="16"
|
||||
xmlns:models="using:ClawdDotNet.App.Models"
|
||||
xmlns:ctrl="using:ClawdDotNet.Desktop.Controls"
|
||||
xmlns:conv="using:ClawdDotNet.Desktop.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="740"
|
||||
x:Class="ClawdDotNet.Desktop.Views.TasksPageView"
|
||||
x:DataType="vm:TasksPageViewModel">
|
||||
|
||||
<DockPanel>
|
||||
|
||||
<!-- Statusbar unten -->
|
||||
<Border Classes="statusbar" DockPanel.Dock="Bottom">
|
||||
<TextBlock Text="{Binding StatusText}" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<!-- Segmentleiste statt Registerkarten. Die Knöpfe überlappen sich um einen
|
||||
Bildpunkt, damit zwischen ihnen nur eine Linie steht und nicht zwei. -->
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal"
|
||||
HorizontalAlignment="Left" Margin="0,0,0,14">
|
||||
<ToggleButton Content="Geplante Jobs" Margin="0,0,-1,0"
|
||||
IsChecked="{Binding IsJobsTab, Mode=OneWay}"
|
||||
Command="{Binding SelectTabCommand}" CommandParameter="Jobs" />
|
||||
<ToggleButton Content="Dienste" Margin="0,0,-1,0"
|
||||
IsChecked="{Binding IsServicesTab, Mode=OneWay}"
|
||||
Command="{Binding SelectTabCommand}" CommandParameter="Services" />
|
||||
<ToggleButton Content="Verlauf"
|
||||
IsChecked="{Binding IsHistoryTab, Mode=OneWay}"
|
||||
Command="{Binding SelectTabCommand}" CommandParameter="History" />
|
||||
</StackPanel>
|
||||
|
||||
<TabControl>
|
||||
<!-- Statuszeile des Bereichs -->
|
||||
<TextBlock DockPanel.Dock="Bottom" Classes="caption" Margin="0,10,0,0"
|
||||
Text="{Binding StatusText}" TextWrapping="Wrap" />
|
||||
|
||||
<!-- TAB 1: Jobs (Scheduled / Worker) -->
|
||||
<TabItem Header="Geplante Jobs">
|
||||
<DockPanel Margin="0,8,0,0">
|
||||
<Panel>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<StackPanel Classes="toolbar" DockPanel.Dock="Top">
|
||||
<Button Content="Job hinzufügen" Command="{Binding AddJobCommand}" Classes="accent" />
|
||||
<Button Content="Bearbeiten" Command="{Binding EditJobCommand}" IsEnabled="{Binding SelectedJob, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
<Button Content="Entfernen" Command="{Binding RemoveJobCommand}" IsEnabled="{Binding SelectedJob, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
<Button Content="▶ Jetzt ausführen" Command="{Binding RunJobNowCommand}" IsEnabled="{Binding SelectedJob, Converter={x:Static ObjectConverters.IsNotNull}}" Margin="12,0,0,0" />
|
||||
<Button Content="Aktualisieren" Command="{Binding RefreshJobListCommand}" />
|
||||
</StackPanel>
|
||||
<!-- ══════════════════ Geplante Jobs ══════════════════ -->
|
||||
<DockPanel IsVisible="{Binding IsJobsTab}">
|
||||
|
||||
<!-- Jobs DataGrid -->
|
||||
<DataGrid ItemsSource="{Binding JobEntries}" SelectedItem="{Binding SelectedJob}" AutoGenerateColumns="False" IsReadOnly="True" GridLinesVisibility="Horizontal">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Typ" Binding="{Binding JobType}" Width="120" />
|
||||
<DataGridTextColumn Header="Agent" Binding="{Binding AgentName}" Width="150" />
|
||||
<DataGridTextColumn Header="Tool / Nachricht" Binding="{Binding TaskMessage}" Width="*" />
|
||||
<DataGridTextColumn Header="Cron-Ausdruck" Binding="{Binding CronExpression}" Width="120" />
|
||||
<DataGridTextColumn Header="Nächster Lauf" Binding="{Binding NextRun}" Width="140" />
|
||||
<DataGridTextColumn Header="Letzter Lauf" Binding="{Binding LastRun}" Width="140" />
|
||||
<DataGridTextColumn Header="Status" Binding="{Binding Status}" Width="100" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
<WrapPanel DockPanel.Dock="Top" Classes="toolbar">
|
||||
<Button Classes="toolbar primary" Command="{Binding AddJobCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconPlus}" Width="14" Height="14" />
|
||||
<TextBlock Text="Job hinzufügen" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
<Button Classes="toolbar" Command="{Binding EditJobCommand}"
|
||||
IsEnabled="{Binding SelectedJob, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconPencil}" Width="14" Height="14" />
|
||||
<TextBlock Text="Bearbeiten" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- TAB 2: Dienste (Services) -->
|
||||
<TabItem Header="Dienste (Services)">
|
||||
<DockPanel Margin="0,8,0,0">
|
||||
<Button Classes="toolbar" Command="{Binding RemoveJobCommand}"
|
||||
IsEnabled="{Binding SelectedJob, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconTrash}" Width="14" Height="14" />
|
||||
<TextBlock Text="Entfernen" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<StackPanel Classes="toolbar" DockPanel.Dock="Top">
|
||||
<Button Content="Dienst hinzufügen" Command="{Binding AddServiceCommand}" Classes="accent" />
|
||||
<Button Content="Entfernen" Command="{Binding RemoveServiceCommand}" IsEnabled="{Binding SelectedService, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
<Button Content="Aktualisieren" Command="{Binding RefreshServiceListCommand}" />
|
||||
</StackPanel>
|
||||
<Button Classes="toolbar" Command="{Binding ToggleJobEnabledCommand}"
|
||||
IsEnabled="{Binding SelectedJob, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconPower}" Width="14" Height="14" />
|
||||
<TextBlock Text="Aktivieren/Deaktivieren" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- Services DataGrid -->
|
||||
<DataGrid ItemsSource="{Binding ServiceEntries}" SelectedItem="{Binding SelectedService}" AutoGenerateColumns="False" IsReadOnly="True" GridLinesVisibility="Horizontal">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Dienstname" Binding="{Binding Name}" Width="180" />
|
||||
<DataGridTextColumn Header="Typ" Binding="{Binding Type}" Width="100" />
|
||||
<DataGridTextColumn Header="Port" Binding="{Binding Port}" Width="80" />
|
||||
<DataGridTextColumn Header="Status" Binding="{Binding Status}" Width="110" />
|
||||
<DataGridTextColumn Header="Beschreibung" Binding="{Binding Description}" Width="*" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
<Border Classes="sep" />
|
||||
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
<Button Classes="toolbar" Command="{Binding RunJobNowCommand}"
|
||||
IsEnabled="{Binding SelectedJob, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconPlay}" Width="12" Height="12" IsFilled="True" />
|
||||
<TextBlock Text="Jetzt ausführen" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- TAB 3: Verlauf (Job History) -->
|
||||
<TabItem Header="Verlauf (History)">
|
||||
<DockPanel Margin="0,8,0,0">
|
||||
<Button Classes="toolbar" Command="{Binding RefreshJobListCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconRefresh}" Width="14" Height="14" />
|
||||
<TextBlock Text="Aktualisieren" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</WrapPanel>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<StackPanel Classes="toolbar" DockPanel.Dock="Top">
|
||||
<Button Content="Verlauf aktualisieren" Command="{Binding RefreshJobHistoryCommand}" />
|
||||
</StackPanel>
|
||||
<Border DockPanel.Dock="Top" Classes="thead">
|
||||
<Grid ColumnDefinitions="1.1*,1.3*,2.2*,1.1*,1.3*,1.3*,0.9*">
|
||||
<TextBlock Grid.Column="0" Classes="th" Text="TYP" />
|
||||
<TextBlock Grid.Column="1" Classes="th" Text="AGENT" />
|
||||
<TextBlock Grid.Column="2" Classes="th" Text="TOOL / NACHRICHT" />
|
||||
<TextBlock Grid.Column="3" Classes="th" Text="CRON" />
|
||||
<TextBlock Grid.Column="4" Classes="th" Text="NÄCHSTER LAUF" />
|
||||
<TextBlock Grid.Column="5" Classes="th" Text="LETZTER LAUF" />
|
||||
<TextBlock Grid.Column="6" Classes="th" Text="STATUS" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- History DataGrid -->
|
||||
<DataGrid ItemsSource="{Binding JobHistoryEntries}" AutoGenerateColumns="False" IsReadOnly="True" GridLinesVisibility="Horizontal">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Zeitpunkt" Binding="{Binding Time, StringFormat='{}{0:g}'}" Width="140" />
|
||||
<DataGridTextColumn Header="Job-Name" Binding="{Binding JobName}" Width="140" />
|
||||
<DataGridTextColumn Header="Agent" Binding="{Binding Agent}" Width="140" />
|
||||
<DataGridTextColumn Header="Beschreibung" Binding="{Binding JobDescription}" Width="220" />
|
||||
<DataGridTextColumn Header="Status" Binding="{Binding Status}" Width="100" />
|
||||
<DataGridTextColumn Header="Details / Info" Binding="{Binding Info}" Width="*" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
<ListBox Classes="table" ItemsSource="{Binding JobEntries}"
|
||||
SelectedItem="{Binding SelectedJob}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="vm:JobDisplayItemViewModel">
|
||||
<Border Classes="tr" Padding="7">
|
||||
<Grid ColumnDefinitions="1.1*,1.3*,2.2*,1.1*,1.3*,1.3*,0.9*">
|
||||
<TextBlock Grid.Column="0" Classes="td" Text="{Binding JobType}" />
|
||||
<TextBlock Grid.Column="1" Classes="td" Text="{Binding AgentName}" />
|
||||
<TextBlock Grid.Column="2" Classes="td" Text="{Binding TaskMessage}" />
|
||||
<TextBlock Grid.Column="3" Classes="td mono" Text="{Binding CronExpression}" />
|
||||
<TextBlock Grid.Column="4" Classes="td" Text="{Binding NextRun}" />
|
||||
<TextBlock Grid.Column="5" Classes="td" Text="{Binding LastRun}" />
|
||||
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
<Border Grid.Column="6" Classes="tag"
|
||||
Classes.ok="{Binding Status,
|
||||
Converter={x:Static conv:StringEqualsConverter.Instance},
|
||||
ConverterParameter=Aktiv}">
|
||||
<TextBlock Text="{Binding Status}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
|
||||
</TabControl>
|
||||
<!-- ══════════════════ Dienste ══════════════════ -->
|
||||
<DockPanel IsVisible="{Binding IsServicesTab}">
|
||||
|
||||
<WrapPanel DockPanel.Dock="Top" Classes="toolbar">
|
||||
<Button Classes="toolbar primary" Command="{Binding AddServiceCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconPlus}" Width="14" Height="14" />
|
||||
<TextBlock Text="Dienst hinzufügen" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding RemoveServiceCommand}"
|
||||
IsEnabled="{Binding SelectedService, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconTrash}" Width="14" Height="14" />
|
||||
<TextBlock Text="Entfernen" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Border Classes="sep" />
|
||||
|
||||
<!-- Der Entwurf zeigt hier „Start"/„Stop". Umgelegt wird aber das Kennzeichen
|
||||
in der Konfiguration, nicht ein laufender Prozess — deshalb die ehrlichere
|
||||
Beschriftung. -->
|
||||
<Button Classes="toolbar" Command="{Binding ToggleServiceEnabledCommand}"
|
||||
IsEnabled="{Binding SelectedService, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconPower}" Width="14" Height="14" />
|
||||
<TextBlock Text="Aktivieren/Deaktivieren" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding RefreshServiceListCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconRefresh}" Width="14" Height="14" />
|
||||
<TextBlock Text="Aktualisieren" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</WrapPanel>
|
||||
|
||||
<Border DockPanel.Dock="Top" Classes="thead">
|
||||
<Grid ColumnDefinitions="1.8*,1*,0.7*,1*,3*">
|
||||
<TextBlock Grid.Column="0" Classes="th" Text="DIENSTNAME" />
|
||||
<TextBlock Grid.Column="1" Classes="th" Text="TYP" />
|
||||
<TextBlock Grid.Column="2" Classes="th" Text="PORT" />
|
||||
<TextBlock Grid.Column="3" Classes="th" Text="STATUS" />
|
||||
<TextBlock Grid.Column="4" Classes="th" Text="BESCHREIBUNG" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<ListBox Classes="table" ItemsSource="{Binding ServiceEntries}"
|
||||
SelectedItem="{Binding SelectedService}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="vm:ServiceDisplayItemViewModel">
|
||||
<Border Classes="tr" Padding="7">
|
||||
<Grid ColumnDefinitions="1.8*,1*,0.7*,1*,3*">
|
||||
<TextBlock Grid.Column="0" Classes="td" FontWeight="SemiBold" Text="{Binding Name}" />
|
||||
<TextBlock Grid.Column="1" Classes="td" Text="{Binding Type}" />
|
||||
<TextBlock Grid.Column="2" Classes="td" Text="{Binding Port}" />
|
||||
|
||||
<Border Grid.Column="3" Classes="tag"
|
||||
Classes.ok="{Binding Status,
|
||||
Converter={x:Static conv:StringEqualsConverter.Instance},
|
||||
ConverterParameter=Bereit}">
|
||||
<TextBlock Text="{Binding Status}" />
|
||||
</Border>
|
||||
|
||||
<TextBlock Grid.Column="4" Classes="td dim" Text="{Binding Description}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
|
||||
<!-- ══════════════════ Verlauf ══════════════════ -->
|
||||
<DockPanel IsVisible="{Binding IsHistoryTab}">
|
||||
|
||||
<StackPanel DockPanel.Dock="Top" Classes="toolbar">
|
||||
<Button Classes="toolbar" Command="{Binding RefreshJobHistoryCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconRefresh}" Width="14" Height="14" />
|
||||
<TextBlock Text="Verlauf aktualisieren" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<Border DockPanel.Dock="Top" Classes="thead">
|
||||
<Grid ColumnDefinitions="1.3*,1.6*,1.4*,2*,0.9*,2.4*">
|
||||
<TextBlock Grid.Column="0" Classes="th" Text="ZEITPUNKT" />
|
||||
<TextBlock Grid.Column="1" Classes="th" Text="JOB" />
|
||||
<TextBlock Grid.Column="2" Classes="th" Text="AGENT" />
|
||||
<TextBlock Grid.Column="3" Classes="th" Text="BESCHREIBUNG" />
|
||||
<TextBlock Grid.Column="4" Classes="th" Text="STATUS" />
|
||||
<TextBlock Grid.Column="5" Classes="th" Text="DETAILS" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<ItemsControl ItemsSource="{Binding JobHistoryEntries}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="models:JobHistoryEntry">
|
||||
<Border Classes="tr" Padding="7">
|
||||
<Grid ColumnDefinitions="1.3*,1.6*,1.4*,2*,0.9*,2.4*">
|
||||
<TextBlock Grid.Column="0" Classes="td"
|
||||
Text="{Binding Time, StringFormat='{}{0:dd.MM. HH:mm}'}" />
|
||||
<TextBlock Grid.Column="1" Classes="td" Text="{Binding JobName}" />
|
||||
<TextBlock Grid.Column="2" Classes="td" Text="{Binding Agent}" />
|
||||
<TextBlock Grid.Column="3" Classes="td dim" Text="{Binding JobDescription}" />
|
||||
|
||||
<Border Grid.Column="4" Classes="tag"
|
||||
Classes.ok="{Binding Status,
|
||||
Converter={x:Static conv:StringEqualsConverter.Instance},
|
||||
ConverterParameter=Success}"
|
||||
Classes.bad="{Binding Status,
|
||||
Converter={x:Static conv:StringEqualsConverter.Instance},
|
||||
ConverterParameter=Error}">
|
||||
<TextBlock Text="{Binding Status}" />
|
||||
</Border>
|
||||
|
||||
<TextBlock Grid.Column="5" Classes="td dim" Text="{Binding Info}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</DockPanel>
|
||||
|
||||
</Panel>
|
||||
</DockPanel>
|
||||
|
||||
</UserControl>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ctrl="using:ClawdDotNet.Desktop.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="420"
|
||||
x:Class="ClawdDotNet.Desktop.Views.TextEditorWindow"
|
||||
Icon="/Assets/app.ico"
|
||||
Title="Text bearbeiten"
|
||||
Width="600" Height="420"
|
||||
MinWidth="420" MinHeight="300"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
|
||||
<!-- Der Rahmen mit Eckmarken sitzt hier wie im Entwurf um den ganzen Dialog. Die
|
||||
Marken ragen nach aussen, deshalb 12 Punkte Abstand zum Fensterrand. -->
|
||||
<ctrl:BlueprintFrame Margin="12" Padding="16">
|
||||
<DockPanel>
|
||||
|
||||
<TextBlock x:Name="TitleText" DockPanel.Dock="Top" Classes="h1"
|
||||
Text="Text bearbeiten" Margin="0,0,0,12" TextWrapping="Wrap" />
|
||||
|
||||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" Spacing="8"
|
||||
HorizontalAlignment="Right" Margin="0,14,0,0">
|
||||
<Button Content="Abbrechen" IsCancel="True" Click="OnCancel" />
|
||||
<Button Classes="primary" Content="Speichern" IsDefault="True" Click="OnSave" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBox x:Name="Editor" AcceptsReturn="True" TextWrapping="Wrap"
|
||||
VerticalContentAlignment="Top"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto" />
|
||||
|
||||
</DockPanel>
|
||||
</ctrl:BlueprintFrame>
|
||||
|
||||
</Window>
|
||||
@@ -0,0 +1,48 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
|
||||
namespace ClawdDotNet.Desktop.Views;
|
||||
|
||||
/// <summary>
|
||||
/// Kleiner Editor für die langen Textfelder eines Agenten (Identity, Soul).
|
||||
///
|
||||
/// <para>Ohne eigenes Ansichtsmodell: Der Dialog hat einen Titel, ein Textfeld und zwei
|
||||
/// Schaltflächen. Ein Ansichtsmodell dafür wäre eine Datei mehr, ohne dass irgendetwas
|
||||
/// prüfbar würde.</para>
|
||||
/// </summary>
|
||||
public partial class TextEditorWindow : Window
|
||||
{
|
||||
private string? _result;
|
||||
|
||||
public TextEditorWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Zeigt den Editor und liefert den bearbeiteten Text — oder <c>null</c>, wenn
|
||||
/// abgebrochen wurde. Das Schließen über das Fensterkreuz gilt als Abbruch: Sonst
|
||||
/// wäre ein weggeklickter Dialog dasselbe wie ein bestätigter.
|
||||
/// </summary>
|
||||
public static async Task<string?> ShowAsync(Window owner, string title, string text)
|
||||
{
|
||||
var window = new TextEditorWindow { Title = title };
|
||||
window.TitleText.Text = title;
|
||||
window.Editor.Text = text;
|
||||
|
||||
await window.ShowDialog(owner);
|
||||
return window._result;
|
||||
}
|
||||
|
||||
private void OnSave(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_result = Editor.Text ?? "";
|
||||
Close();
|
||||
}
|
||||
|
||||
private void OnCancel(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_result = null;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
xmlns:ctrl="using:ClawdDotNet.Desktop.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="1100" d:DesignHeight="700"
|
||||
x:Class="ClawdDotNet.Desktop.Views.TokenUsagePageView"
|
||||
x:DataType="vm:TokenUsagePageViewModel">
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Spacing="20">
|
||||
|
||||
<!-- Kennzahlen -->
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<Border Classes="card" MinWidth="160">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Classes="kicker" Text="TOKENS (7 TAGE)" />
|
||||
<TextBlock Classes="h2" Text="{Binding TotalTokens}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Classes="card" MinWidth="160">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Classes="kicker" Text="KOSTEN (7 TAGE)" />
|
||||
<TextBlock Classes="h2" Text="{Binding TotalCost}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Classes="card" MinWidth="160">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Classes="kicker" Text="RUNS (7 TAGE)" />
|
||||
<TextBlock Classes="h2" Text="{Binding TotalRuns}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Button Classes="toolbar" Command="{Binding RefreshCommand}" VerticalAlignment="Bottom">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ctrl:StrokeIcon Data="{StaticResource IconRefresh}" Width="14" Height="14" />
|
||||
<TextBlock Text="Aktualisieren" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Hinweis, solange nichts verzeichnet ist -->
|
||||
<TextBlock Classes="caption" Text="{Binding StatusText}" TextWrapping="Wrap"
|
||||
IsVisible="{Binding StatusText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||
|
||||
<!-- Tagesbalken -->
|
||||
<StackPanel Spacing="12" IsVisible="{Binding !IsEmpty}">
|
||||
<TextBlock Classes="h2" Text="Tokens pro Tag" />
|
||||
|
||||
<Border BorderBrush="{DynamicResource DividerBrush}" BorderThickness="0,0,0,1"
|
||||
Padding="0,0,0,2">
|
||||
<ItemsControl ItemsSource="{Binding Days7}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Rows="1" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:TokenDayItem">
|
||||
<StackPanel Spacing="6" VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Center" Margin="8,0">
|
||||
<TextBlock Classes="caption" Text="{Binding TokensLabel}"
|
||||
HorizontalAlignment="Center" />
|
||||
<Rectangle Width="28" Height="{Binding BarHeight}"
|
||||
Fill="{DynamicResource AccentBrush}"
|
||||
VerticalAlignment="Bottom" />
|
||||
<TextBlock FontSize="11.5" Text="{Binding DayLabel}"
|
||||
HorizontalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Aufschlüsselung -->
|
||||
<StackPanel Spacing="10" MaxWidth="860" HorizontalAlignment="Left"
|
||||
IsVisible="{Binding !IsEmpty}">
|
||||
<TextBlock Classes="h2" Text="Aufschlüsselung nach Agent & Modell" />
|
||||
|
||||
<Border Classes="thead">
|
||||
<Grid ColumnDefinitions="2*,2*,1.2*,1.2*,0.8*">
|
||||
<TextBlock Grid.Column="0" Classes="th" Text="AGENT" />
|
||||
<TextBlock Grid.Column="1" Classes="th" Text="MODELL" />
|
||||
<TextBlock Grid.Column="2" Classes="th" Text="TOKENS" />
|
||||
<TextBlock Grid.Column="3" Classes="th" Text="KOSTEN" />
|
||||
<TextBlock Grid.Column="4" Classes="th" Text="RUNS" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<ItemsControl ItemsSource="{Binding ByAgent}" Margin="0,-10,0,0">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:TokenAgentItem">
|
||||
<Border Classes="tr" Padding="7">
|
||||
<Grid ColumnDefinitions="2*,2*,1.2*,1.2*,0.8*">
|
||||
<TextBlock Grid.Column="0" Classes="td" FontWeight="SemiBold"
|
||||
Text="{Binding AgentName}" />
|
||||
<TextBlock Grid.Column="1" Classes="td dim" Text="{Binding Model}" />
|
||||
<TextBlock Grid.Column="2" Classes="td" Text="{Binding TokensLabel}" />
|
||||
<TextBlock Grid.Column="3" Classes="td" Text="{Binding CostLabel}" />
|
||||
<TextBlock Grid.Column="4" Classes="td" Text="{Binding Runs}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
</UserControl>
|
||||
@@ -0,0 +1,8 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace ClawdDotNet.Desktop.Views;
|
||||
|
||||
public partial class TokenUsagePageView : UserControl
|
||||
{
|
||||
public TokenUsagePageView() => InitializeComponent();
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
xmlns:conv="using:ClawdDotNet.Desktop.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="520" d:DesignHeight="420"
|
||||
x:Class="ClawdDotNet.Desktop.Views.ToolSettingsView"
|
||||
x:DataType="vm:ToolSettingsViewModel">
|
||||
|
||||
<!--
|
||||
Die Felder eines Werkzeugs — im Entwurf unten rechts auf der Agenten-Seite, nicht
|
||||
mehr in einem eigenen Fenster. Deshalb ein UserControl und kein Window: Der Inhalt
|
||||
ist derselbe, der Rahmen drumherum entscheidet der Aufrufer.
|
||||
|
||||
Flach statt in Karten und ohne verschachtelte Registerkarten (siehe
|
||||
Implementierungsleitfaden im Mockup-Verzeichnis): Beschriftung ueber dem Feld,
|
||||
hoechstens zwei Spalten.
|
||||
-->
|
||||
<StackPanel Spacing="12" MaxWidth="520" HorizontalAlignment="Left">
|
||||
|
||||
<!-- 1. FTP -->
|
||||
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
|
||||
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=FTP}">
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Host" />
|
||||
<TextBox Text="{Binding FtpHost}" PlaceholderText="ftp.example.com" />
|
||||
</StackPanel>
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Port" />
|
||||
<TextBox Text="{Binding FtpPort}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="Benutzername" />
|
||||
<TextBox Text="{Binding FtpUsername}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Passwort" />
|
||||
<TextBox Text="{Binding FtpPassword}" PasswordChar="•" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Root-Pfad" />
|
||||
<TextBox Text="{Binding FtpRootPath}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 2. Mail -->
|
||||
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
|
||||
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=Mail}">
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Benutzername" />
|
||||
<TextBox Text="{Binding MailUsername}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="Passwort" />
|
||||
<TextBox Text="{Binding MailPassword}" PasswordChar="•" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="IMAP-Host" />
|
||||
<TextBox Text="{Binding MailImapHost}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="IMAP-Port" />
|
||||
<TextBox Text="{Binding MailImapPort}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="SMTP-Host" />
|
||||
<TextBox Text="{Binding MailSmtpHost}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="SMTP-Port" />
|
||||
<TextBox Text="{Binding MailSmtpPort}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Erlaubte Empfänger" />
|
||||
<TextBox Text="{Binding MailAllowedRecipients}"
|
||||
PlaceholderText="adresse1@domain.de, adresse2@domain.de" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 3. Database -->
|
||||
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
|
||||
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=Database}">
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Typ" />
|
||||
<ComboBox ItemsSource="{Binding DbTypes}" SelectedItem="{Binding DbType}"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Connection-String" />
|
||||
<TextBox Text="{Binding DbConnectionString}" PasswordChar="•" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Zugriffsebene" />
|
||||
<ComboBox ItemsSource="{Binding DbAccessLevels}" SelectedItem="{Binding DbAccessLevel}"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Erlaubte Tabellen" />
|
||||
<TextBox Text="{Binding DbAllowedTables}" PlaceholderText="tabelle1, tabelle2" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 4. Telegram -->
|
||||
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
|
||||
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=Telegram}">
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Bot-Token" />
|
||||
<TextBox Text="{Binding TelegramBotToken}" PasswordChar="•" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Standard Chat-ID" />
|
||||
<TextBox Text="{Binding TelegramDefaultChatId}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Erlaubte Chat-IDs" />
|
||||
<TextBox Text="{Binding TelegramAllowedChatIds}" PlaceholderText="id1, id2" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 5. DirectAPI -->
|
||||
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
|
||||
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=DirectAPI}">
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Standard-Provider" />
|
||||
<TextBox Text="{Binding DirectApiDefaultProvider}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="Cache TTL (Sekunden)" />
|
||||
<TextBox Text="{Binding DirectApiCacheTtlSeconds}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Twelve Data Key" />
|
||||
<TextBox Text="{Binding DirectApiTwelveDataKey}" PasswordChar="•" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Alpha Vantage Key" />
|
||||
<TextBox Text="{Binding DirectApiAlphaVantageKey}" PasswordChar="•" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 6. WebFetch -->
|
||||
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
|
||||
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=WebFetch}">
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Erlaubte Domains" />
|
||||
<TextBox Text="{Binding WebFetchAllowedDomains}"
|
||||
PlaceholderText="reuters.com, coindesk.com" />
|
||||
</StackPanel>
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Max. Antwortgröße (KB)" />
|
||||
<TextBox Text="{Binding WebFetchMaxResponseKb}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="User Agent" />
|
||||
<TextBox Text="{Binding WebFetchUserAgent}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 7. SocialMediaManager -->
|
||||
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
|
||||
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=SocialMediaManager}">
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="X (Twitter) API-Key" />
|
||||
<TextBox Text="{Binding SmmXApiKey}" PasswordChar="•" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="X Accounts" />
|
||||
<TextBox Text="{Binding SmmXWatchAccounts}" PlaceholderText="account1, account2" />
|
||||
</StackPanel>
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="Subreddits" />
|
||||
<TextBox Text="{Binding SmmRedditWatchSubreddits}" PlaceholderText="stocks, investing" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="Reddit Post Limit" />
|
||||
<TextBox Text="{Binding SmmRedditPostLimit}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="OpenRouter Key" />
|
||||
<TextBox Text="{Binding SmmOpenRouterApiKey}" PasswordChar="•" />
|
||||
</StackPanel>
|
||||
<Grid ColumnDefinitions="*,12,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Classes="label" Text="STT-Modell" />
|
||||
<TextBox Text="{Binding SmmSttModel}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Classes="label" Text="YouTube-Kanäle" />
|
||||
<TextBox Text="{Binding SmmYoutubeChannels}" PlaceholderText="URL1, URL2" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 8. FileRW -->
|
||||
<StackPanel Spacing="12" IsVisible="{Binding ToolName,
|
||||
Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=FileRW}">
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Erlaubte Dateiendungen (persönlich)" />
|
||||
<TextBox Text="{Binding FileRwPersonalAllowedExtensions}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Zugriffsebene (geteilt)" />
|
||||
<ComboBox ItemsSource="{Binding FileRwAccessLevels}"
|
||||
SelectedItem="{Binding FileRwSharedAccessLevel}"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Erlaubte Dateiendungen (geteilt)" />
|
||||
<TextBox Text="{Binding FileRwSharedAllowedExtensions}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="label" Text="Geschützte Pfade" />
|
||||
<TextBox Text="{Binding FileRwProtectedPaths}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Werkzeuge ohne weitere Einstellungen -->
|
||||
<TextBlock Classes="muted" FontStyle="Italic" TextWrapping="Wrap"
|
||||
Text="Dieses Werkzeug benötigt keine weiteren Einstellungen."
|
||||
IsVisible="{Binding SettingsObject, Converter={x:Static ObjectConverters.IsNull}}" />
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</UserControl>
|
||||
@@ -0,0 +1,8 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace ClawdDotNet.Desktop.Views;
|
||||
|
||||
public partial class ToolSettingsView : UserControl
|
||||
{
|
||||
public ToolSettingsView() => InitializeComponent();
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:ClawdDotNet.Desktop.ViewModels"
|
||||
xmlns:models="using:ClawdDotNet.Models"
|
||||
xmlns:conv="using:ClawdDotNet.Desktop.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="560" d:DesignHeight="480"
|
||||
Width="580" Height="500"
|
||||
Padding="16"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
CanResize="False"
|
||||
Title="Werkzeug-Einstellungen"
|
||||
x:Class="ClawdDotNet.Desktop.Views.ToolSettingsWindow"
|
||||
x:DataType="vm:ToolSettingsViewModel">
|
||||
|
||||
<DockPanel>
|
||||
|
||||
<!-- Bottom Buttons -->
|
||||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right" Spacing="8" Margin="0,16,0,0">
|
||||
<Button Content="Abbrechen" Command="{Binding CancelCommand}" />
|
||||
<Button Content="Speichern" Command="{Binding ConfirmCommand}" Classes="accent" />
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer>
|
||||
<StackPanel Spacing="12">
|
||||
|
||||
<TextBlock Text="{Binding ToolName, StringFormat='Einstellungen für {0}'}" Classes="heading" FontSize="18" />
|
||||
|
||||
<!-- Forms for each Tool -->
|
||||
<StackPanel Spacing="8">
|
||||
|
||||
<!-- 1. FTP -->
|
||||
<Grid ColumnDefinitions="140,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto" IsVisible="{Binding ToolName, Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=FTP}">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Host:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding FtpHost}" PlaceholderText="ftp.example.com" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Port:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<NumericUpDown Grid.Row="1" Grid.Column="1" Value="{Binding FtpPort}" Minimum="1" Maximum="65535" Width="120" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Benutzername:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding FtpUsername}" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Passwort:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding FtpPassword}" PasswordChar="*" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="Root-Pfad:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding FtpRootPath}" Margin="0,8,0,0" />
|
||||
</Grid>
|
||||
|
||||
<!-- 2. Mail -->
|
||||
<Grid ColumnDefinitions="140,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto" IsVisible="{Binding ToolName, Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=Mail}">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Benutzername:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding MailUsername}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Passwort:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding MailPassword}" PasswordChar="*" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="IMAP-Host:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding MailImapHost}" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="IMAP-Port:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding MailImapPort}" Minimum="1" Maximum="65535" Width="120" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="SMTP-Host:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding MailSmtpHost}" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="5" Grid.Column="0" Text="SMTP-Port:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<NumericUpDown Grid.Row="5" Grid.Column="1" Value="{Binding MailSmtpPort}" Minimum="1" Maximum="65535" Width="120" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="6" Grid.Column="0" Text="Erlaubte Empfänger:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="6" Grid.Column="1" Text="{Binding MailAllowedRecipients}" PlaceholderText="email1@domain.de, email2@domain.de" Margin="0,8,0,0" />
|
||||
</Grid>
|
||||
|
||||
<!-- 3. Database -->
|
||||
<Grid ColumnDefinitions="140,*" RowDefinitions="Auto,Auto,Auto,Auto" IsVisible="{Binding ToolName, Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=Database}">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Typ:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<ComboBox Grid.Row="0" Grid.Column="1" ItemsSource="{Binding DbTypes}" SelectedItem="{Binding DbType}" HorizontalAlignment="Stretch" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Connection-String:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding DbConnectionString}" PasswordChar="*" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Zugriffsebene:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<ComboBox Grid.Row="2" Grid.Column="1" ItemsSource="{Binding DbAccessLevels}" SelectedItem="{Binding DbAccessLevel}" HorizontalAlignment="Stretch" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Erlaubte Tabellen:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding DbAllowedTables}" PlaceholderText="tabelle1, tabelle2" Margin="0,8,0,0" />
|
||||
</Grid>
|
||||
|
||||
<!-- 4. Telegram -->
|
||||
<Grid ColumnDefinitions="140,*" RowDefinitions="Auto,Auto,Auto" IsVisible="{Binding ToolName, Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=Telegram}">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Bot-Token:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding TelegramBotToken}" PasswordChar="*" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Standard Chat-ID:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding TelegramDefaultChatId}" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Erlaubte Chat-IDs:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding TelegramAllowedChatIds}" PlaceholderText="id1, id2" Margin="0,8,0,0" />
|
||||
</Grid>
|
||||
|
||||
<!-- 5. DirectAPI -->
|
||||
<Grid ColumnDefinitions="140,*" RowDefinitions="Auto,Auto,Auto,Auto" IsVisible="{Binding ToolName, Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=DirectAPI}">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Standard-Provider:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding DirectApiDefaultProvider}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Cache TTL (Sek.):" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<NumericUpDown Grid.Row="1" Grid.Column="1" Value="{Binding DirectApiCacheTtlSeconds}" Minimum="0" Maximum="86400" Width="120" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Twelve Data Key:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding DirectApiTwelveDataKey}" PasswordChar="*" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Alpha Vantage Key:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding DirectApiAlphaVantageKey}" PasswordChar="*" Margin="0,8,0,0" />
|
||||
</Grid>
|
||||
|
||||
<!-- 6. WebFetch -->
|
||||
<Grid ColumnDefinitions="140,*" RowDefinitions="Auto,Auto,Auto" IsVisible="{Binding ToolName, Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=WebFetch}">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Erlaubte Domains:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding WebFetchAllowedDomains}" PlaceholderText="reuters.com, bloomberg.com" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Max Response KB:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<NumericUpDown Grid.Row="1" Grid.Column="1" Value="{Binding WebFetchMaxResponseKb}" Minimum="1" Maximum="10000" Width="120" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="User Agent:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding WebFetchUserAgent}" Margin="0,8,0,0" />
|
||||
</Grid>
|
||||
|
||||
<!-- 7. SocialMediaManager -->
|
||||
<Grid ColumnDefinitions="160,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto" IsVisible="{Binding ToolName, Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=SocialMediaManager}">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="X (Twitter) API-Key:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding SmmXApiKey}" PasswordChar="*" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="X Accounts:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding SmmXWatchAccounts}" PlaceholderText="elonmusk, unusual_whales" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Subreddits:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding SmmRedditWatchSubreddits}" PlaceholderText="wallstreetbets, stocks" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Reddit Post Limit:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding SmmRedditPostLimit}" Minimum="1" Maximum="100" Width="120" HorizontalAlignment="Left" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="OpenRouter Key:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding SmmOpenRouterApiKey}" PasswordChar="*" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="5" Grid.Column="0" Text="STT Modell:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding SmmSttModel}" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="6" Grid.Column="0" Text="YouTube Kanäle:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="6" Grid.Column="1" Text="{Binding SmmYoutubeChannels}" PlaceholderText="URL1, URL2" Margin="0,8,0,0" />
|
||||
</Grid>
|
||||
|
||||
<!-- 8. FileRW -->
|
||||
<Grid ColumnDefinitions="180,*" RowDefinitions="Auto,Auto,Auto,Auto" IsVisible="{Binding ToolName, Converter={x:Static conv:ToolMatchConverter.Instance}, ConverterParameter=FileRW}">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Personal Allowed Ext:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding FileRwPersonalAllowedExtensions}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Shared Access Level:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<ComboBox Grid.Row="1" Grid.Column="1" ItemsSource="{Binding FileRwAccessLevels}" SelectedItem="{Binding FileRwSharedAccessLevel}" HorizontalAlignment="Stretch" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Shared Allowed Ext:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding FileRwSharedAllowedExtensions}" Margin="0,8,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Geschützte Pfade:" FontWeight="Bold" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding FileRwProtectedPaths}" Margin="0,8,0,0" />
|
||||
</Grid>
|
||||
|
||||
<!-- Fallback info for tools without extra config -->
|
||||
<TextBlock Text="Dieses Werkzeug benötigt keine weiteren Einstellungen."
|
||||
IsVisible="{Binding SettingsObject, Converter={x:Static ObjectConverters.IsNull}}"
|
||||
FontStyle="Italic" />
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
</DockPanel>
|
||||
|
||||
</Window>
|
||||
@@ -1,21 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using ClawdDotNet.Desktop.ViewModels;
|
||||
|
||||
namespace ClawdDotNet.Desktop.Views;
|
||||
|
||||
public partial class ToolSettingsWindow : Window
|
||||
{
|
||||
public ToolSettingsWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnDataContextChanged(EventArgs e)
|
||||
{
|
||||
base.OnDataContextChanged(e);
|
||||
if (DataContext is ToolSettingsViewModel vm)
|
||||
{
|
||||
vm.CloseRequested += () => Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user