openclaw/apps/windows/src/Clawdbot.Windows/ExecApprovalDialog.xaml
TAPUZE 7f33621721 feat(windows): Add Windows companion app (Phase 0-2)
- System tray icon with connection status
- Exec approval dialogs with queue and timeout
- Settings persistence to JSON (%LOCALAPPDATA%\Clawdbot)
- Auto-start on login via Windows Registry
- Notification sounds for approvals and connection events
- WebView2 Control UI embedding
- 23 unit tests passing
- Build scripts and Inno Setup installer template
- Community build guide documentation

Phase 0: Protocol models, WebSocket client, logging
Phase 1: Tray icon, exec approval dialogs, Control UI
Phase 2: Settings, auto-start, sounds, settings UI
2026-01-27 11:12:39 +02:00

213 lines
9.9 KiB
XML

<Window x:Class="Clawdbot.Windows.ExecApprovalDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Clawdbot - Command Approval"
Width="500"
Height="400"
MinWidth="400"
MinHeight="300"
WindowStartupLocation="CenterScreen"
WindowStyle="SingleBorderWindow"
ResizeMode="CanResize"
Topmost="True"
ShowInTaskbar="True"
Background="{StaticResource BackgroundDarkBrush}">
<Window.Resources>
<Style x:Key="SectionTitleStyle" TargetType="TextBlock">
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}"/>
<Setter Property="Margin" Value="0,0,0,4"/>
</Style>
<Style x:Key="DetailLabelStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="12"/>
<Setter Property="Foreground" Value="{StaticResource TextSecondaryBrush}"/>
</Style>
<Style x:Key="DetailValueStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="12"/>
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}"/>
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
</Style>
<Style x:Key="CommandBoxStyle" TargetType="TextBox">
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="FontFamily" Value="Consolas, Courier New"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Background" Value="#1A1A1A"/>
<Setter Property="Foreground" Value="#E0E0E0"/>
<Setter Property="BorderBrush" Value="#3A3A3A"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
</Style>
<Style x:Key="ActionButtonStyle" TargetType="Button">
<Setter Property="MinWidth" Value="100"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Padding" Value="16,0"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="Cursor" Value="Hand"/>
</Style>
<Style x:Key="AllowOnceButtonStyle" TargetType="Button" BasedOn="{StaticResource ActionButtonStyle}">
<Setter Property="Background" Value="#4CAF50"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#66BB6A"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#388E3C"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="AllowAlwaysButtonStyle" TargetType="Button" BasedOn="{StaticResource ActionButtonStyle}">
<Setter Property="Background" Value="#2196F3"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#42A5F5"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#1976D2"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DenyButtonStyle" TargetType="Button" BasedOn="{StaticResource ActionButtonStyle}">
<Setter Property="Background" Value="#F44336"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#EF5350"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#D32F2F"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Header -->
<StackPanel Grid.Row="0" Margin="0,0,0,16">
<TextBlock Text="⚠️ Allow this command?"
FontSize="18"
FontWeight="Bold"
Foreground="{StaticResource TextPrimaryBrush}"/>
<TextBlock Text="Review the command details before allowing execution."
FontSize="12"
Foreground="{StaticResource TextSecondaryBrush}"
Margin="0,4,0,0"/>
</StackPanel>
<!-- Command Section -->
<StackPanel Grid.Row="1" Margin="0,0,0,12">
<TextBlock Text="Command" Style="{StaticResource SectionTitleStyle}"/>
<TextBox x:Name="CommandTextBox"
Style="{StaticResource CommandBoxStyle}"
MaxHeight="100"/>
</StackPanel>
<!-- Context Section -->
<StackPanel Grid.Row="2" Margin="0,0,0,12">
<TextBlock Text="Context" Style="{StaticResource SectionTitleStyle}"/>
<Border Background="#1A1A1A" BorderBrush="#3A3A3A" BorderThickness="1" CornerRadius="4" Padding="12">
<StackPanel x:Name="ContextPanel">
<!-- Context details will be added programmatically -->
</StackPanel>
</Border>
</StackPanel>
<!-- Timer -->
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,0,0,12">
<TextBlock Text="⏱️ " FontSize="12" Foreground="{StaticResource TextSecondaryBrush}"/>
<TextBlock x:Name="TimerText"
Text="Expires in --:--"
FontSize="12"
Foreground="{StaticResource TextSecondaryBrush}"/>
</StackPanel>
<!-- Footer Note -->
<TextBlock Grid.Row="4"
Text="This command will run on this machine."
FontSize="11"
Foreground="{StaticResource TextSecondaryBrush}"
Margin="0,0,0,16"/>
<!-- Action Buttons -->
<StackPanel Grid.Row="5"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button x:Name="DenyButton"
Content="Don't Allow"
Style="{StaticResource DenyButtonStyle}"
Click="DenyButton_Click"
Margin="0,0,12,0"
ToolTip="Press Escape to deny"/>
<Button x:Name="AllowAlwaysButton"
Content="Always Allow"
Style="{StaticResource AllowAlwaysButtonStyle}"
Click="AllowAlwaysButton_Click"
Margin="0,0,12,0"
ToolTip="Add to allowlist and run"/>
<Button x:Name="AllowOnceButton"
Content="Allow Once"
Style="{StaticResource AllowOnceButtonStyle}"
Click="AllowOnceButton_Click"
IsDefault="True"
ToolTip="Press Enter to allow once"/>
</StackPanel>
</Grid>
</Window>