Files
TunnelX/AppTunnel/Views/ConnectionTabView.xaml
T
2026-05-18 14:14:47 +03:30

985 lines
60 KiB
XML

<UserControl x:Class="AppTunnel.Views.ConnectionTabView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:conv="clr-namespace:AppTunnel.Converters"
xmlns:models="clr-namespace:AppTunnel.Models">
<UserControl.Resources>
<conv:BoolToVisibilityConverter x:Key="BoolToVis"/>
<conv:InverseBoolToVisibilityConverter x:Key="InverseBoolToVis"/>
<conv:StringToColorConverter x:Key="StringToColor"/>
<conv:EnumToVisibilityConverter x:Key="EnumToVis"/>
</UserControl.Resources>
<ScrollViewer FlowDirection="{Binding AppFlowDirection}"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<StackPanel HorizontalAlignment="Stretch" Margin="16,8,16,4">
<!-- ══ DISCONNECTED STATE: Show connection form ══ -->
<StackPanel Visibility="{Binding IsConnected, Converter={StaticResource InverseBoolToVis}}">
<!-- OpenVPN connecting view -->
<Border Background="#33221812"
BorderBrush="{StaticResource WarningBrush}"
BorderThickness="1"
CornerRadius="12"
Padding="18,16"
Margin="0,0,0,10"
Visibility="{Binding IsOpenVpnConnectionPending, Converter={StaticResource BoolToVis}}">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0"
Width="42"
Height="42"
CornerRadius="21"
Background="#22FFC107"
VerticalAlignment="Top">
<TextBlock Text="⏳"
FontSize="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<StackPanel Grid.Column="2">
<TextBlock Text="در حال اتصال OpenVPN"
FontSize="15"
FontWeight="SemiBold"
Foreground="{StaticResource WarningBrush}"/>
<TextBlock Text="{Binding StatusText}"
TextWrapping="Wrap"
FontSize="11"
LineHeight="18"
Margin="0,5,0,0"
Foreground="{StaticResource TextPrimaryBrush}"/>
<TextBlock Text="تا قبل از بالا آمدن آداپتر، مسیرهای سیستم تغییر داده نمی‌شود. اگر اتصال طولانی شد، فایل .ovpn، نام کاربری/رمز یا نصب OpenVPN Community را بررسی کنید."
TextWrapping="Wrap"
FontSize="10"
LineHeight="17"
Margin="0,6,0,0"
Foreground="{StaticResource TextSecondaryBrush}"/>
</StackPanel>
<Button Grid.Column="4"
Style="{StaticResource DangerButton}"
Content="لغو اتصال"
Command="{Binding ConnectCommand}"
VerticalAlignment="Top"
Padding="14,7"/>
</Grid>
</StackPanel>
</Border>
<!-- ── Profile Card ── -->
<Border Style="{StaticResource CardPanel}"
Visibility="{Binding IsOpenVpnConnectionPending, Converter={StaticResource InverseBoolToVis}}">
<StackPanel>
<Grid Margin="0,0,0,12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Text="کانفیگ‌ها و پروفایل‌ها"
FontSize="16"
FontWeight="SemiBold"
Foreground="{StaticResource TextPrimaryBrush}"/>
<TextBlock Text="یک کانفیگ را انتخاب کنید، ویرایش کنید یا کانفیگ جدید بسازید."
FontSize="11"
Foreground="{StaticResource TextSecondaryBrush}"
Margin="0,4,0,0"/>
</StackPanel>
<Button Grid.Column="2"
Style="{StaticResource PrimaryButton}"
Content="افزودن کانفیگ جدید"
FontSize="12"
Padding="16,9"
Command="{Binding NewProfileCommand}"/>
</Grid>
<ListBox ItemsSource="{Binding Profiles}"
SelectedItem="{Binding SelectedProfile, Mode=TwoWay}"
Background="Transparent"
BorderThickness="0"
Padding="0"
PreviewMouseWheel="OnProfileListPreviewMouseWheel"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
SelectionMode="Single">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0,0,0,6"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Background" Value="#121212"/>
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="ItemBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="10"
Padding="10,8">
<ContentPresenter/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ItemBorder" Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
<Setter TargetName="ItemBorder" Property="Background" Value="#181818"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="ItemBorder" Property="BorderBrush" Value="{StaticResource PrimaryBrush}"/>
<Setter TargetName="ItemBorder" Property="Background" Value="#24180F"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<StackPanel Orientation="Horizontal" FlowDirection="{Binding DataContext.AppFlowDirection, RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBlock Text="{Binding Name}"
FontSize="13"
FontWeight="SemiBold"
Foreground="{StaticResource TextPrimaryBrush}"
TextTrimming="CharacterEllipsis"/>
<Border Background="#18E8803A"
CornerRadius="8"
Padding="7,2"
Margin="8,0,0,0">
<TextBlock Text="{Binding TunnelTypeDisplay}"
FontSize="9"
Foreground="{StaticResource AccentBrush}"/>
</Border>
</StackPanel>
<StackPanel Orientation="Horizontal"
FlowDirection="LeftToRight"
Margin="0,5,0,0">
<TextBlock Text="{Binding EndpointDisplay}"
FontSize="10"
Foreground="{StaticResource TextSecondaryBrush}"
TextTrimming="CharacterEllipsis"/>
<TextBlock Text=" • "
FontSize="10"
Foreground="{StaticResource TextSecondaryBrush}"/>
<TextBlock Text="{Binding ReadinessText}"
Foreground="{Binding ReadinessColor, Converter={StaticResource StringToColor}}"
FontSize="10"
FontWeight="SemiBold"/>
</StackPanel>
</StackPanel>
<StackPanel Grid.Column="2"
Orientation="Horizontal"
FlowDirection="{Binding DataContext.AppFlowDirection, RelativeSource={RelativeSource AncestorType=UserControl}}"
VerticalAlignment="Center">
<Button Style="{StaticResource PrimaryButton}"
Content="فعال"
FontSize="10"
Padding="10,5"
Command="{Binding DataContext.SelectProfileCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}"
ToolTip="استفاده از این کانفیگ برای اتصال"/>
<Button Style="{StaticResource SecondaryButton}"
Content="ویرایش"
FontSize="10"
Padding="9,5"
Margin="5,0,0,0"
Command="{Binding DataContext.EditProfileCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}"/>
<Button Style="{StaticResource SecondaryButton}"
Content="کپی"
FontSize="10"
Padding="9,5"
Margin="5,0,0,0"
Command="{Binding DataContext.DuplicateProfileCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}"/>
<Button Style="{StaticResource DangerButton}"
Content="حذف"
FontSize="10"
Padding="9,5"
Margin="5,0,0,0"
Command="{Binding DataContext.DeleteProfileCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Border Background="#151515"
BorderBrush="#22FFFFFF"
BorderThickness="1"
CornerRadius="12"
Padding="12,10"
Margin="0,10,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal" FlowDirection="{Binding AppFlowDirection}">
<Ellipse Width="9" Height="9"
Fill="{Binding StatusColor, Converter={StaticResource StringToColor}}"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding StatusText}"
FontSize="12"
FontWeight="SemiBold"
Foreground="{StaticResource TextPrimaryBrush}"
Margin="7,0,0,0"
VerticalAlignment="Center"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
<TextBlock FontSize="10"
Text="{Binding SelectedProfileSummaryText}"
Foreground="{StaticResource TextSecondaryBrush}"
Margin="0,4,0,0"
FontWeight="SemiBold"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
<Button Grid.Column="2"
Command="{Binding ConnectCommand}"
Content="{Binding ConnectButtonText}"
MinWidth="160"
Height="44"
Padding="18,8"
ToolTip="{Binding ConnectButtonToolTip}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="#10B981"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="12"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#059669"/>
</Trigger>
<DataTrigger Binding="{Binding ConnectionState}" Value="{x:Static models:ConnectionState.Connecting}">
<Setter Property="Background" Value="#F97316"/>
</DataTrigger>
<DataTrigger Binding="{Binding ConnectionState}" Value="{x:Static models:ConnectionState.Error}">
<Setter Property="Background" Value="#DC2626"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</Border>
</StackPanel>
</Border>
<!-- ── Server Settings Card ── -->
<Border Style="{StaticResource CardPanel}"
Visibility="Collapsed">
<StackPanel>
<TextBlock Style="{StaticResource SectionHeader}" Text="🌐 تنظیمات سرور"
Margin="0,0,0,4"/>
<!-- Tunnel Type selector -->
<TextBlock Style="{StaticResource FieldLabel}" Text="نوع اتصال"/>
<ComboBox Style="{StaticResource DarkComboBox}"
SelectedValue="{Binding CurrentTunnelType, Mode=TwoWay}"
SelectedValuePath="Tag"
Margin="0,0,0,8">
<ComboBoxItem Content="L2TP/IPsec"
Tag="{x:Static models:TunnelType.L2tpIpsec}"/>
<ComboBoxItem Content="V2Ray / Xray"
Tag="{x:Static models:TunnelType.V2Ray}"/>
<ComboBoxItem Content="OpenVPN"
Tag="{x:Static models:TunnelType.OpenVpn}"/>
<ComboBoxItem Content="SOCKS / Proxy"
Tag="{x:Static models:TunnelType.SocksProxy}"/>
</ComboBox>
<!-- L2TP fields (shown only when TunnelType = L2tpIpsec) -->
<StackPanel Visibility="{Binding CurrentTunnelType,
Converter={StaticResource EnumToVis},
ConverterParameter=L2tpIpsec}">
<!-- Row 1: Server + Username -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource FieldLabel}" Text="آدرس سرور"/>
<TextBox Style="{StaticResource ModernTextBox}"
Text="{Binding ServerAddress, UpdateSourceTrigger=PropertyChanged}"
FlowDirection="LeftToRight"/>
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Style="{StaticResource FieldLabel}" Text="نام کاربری"/>
<TextBox Style="{StaticResource ModernTextBox}"
Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}"
FlowDirection="LeftToRight"/>
</StackPanel>
</Grid>
<!-- Row 2: Password + PSK -->
<Grid Margin="0,5,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource FieldLabel}" Text="رمز عبور"/>
<PasswordBox x:Name="PasswordField"
Style="{StaticResource ModernPasswordBox}"
FlowDirection="LeftToRight"/>
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Style="{StaticResource FieldLabel}" Text="Pre-Shared Key"/>
<PasswordBox x:Name="PskField"
Style="{StaticResource ModernPasswordBox}"
FlowDirection="LeftToRight"/>
</StackPanel>
</Grid>
</StackPanel>
<!-- End L2TP fields -->
<!-- V2Ray fields (shown only when TunnelType = V2Ray) -->
<StackPanel Visibility="{Binding CurrentTunnelType,
Converter={StaticResource EnumToVis},
ConverterParameter=V2Ray}">
<TextBlock Style="{StaticResource FieldLabel}" Text="کانفیگ V2Ray"/>
<TextBox Style="{StaticResource ModernTextBox}"
Text="{Binding SelectedV2RayConfig,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
AcceptsReturn="True"
MinHeight="96"
FontFamily="Consolas"
FlowDirection="LeftToRight"
VerticalScrollBarVisibility="Auto"
TextWrapping="Wrap"/>
<Grid Margin="0,6,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Style="{StaticResource SecondaryButton}"
Content="پیست"
Command="{Binding PasteConfigCommand}"
FontSize="10"
Padding="10,5"
ToolTip="خواندن کانفیگ از کلیپ‌بورد"/>
<Button Grid.Column="2"
Style="{StaticResource SecondaryButton}"
Content="پاک کردن"
Command="{Binding ClearConfigCommand}"
FontSize="10"
Padding="10,5"/>
<TextBlock Grid.Column="4"
Text="{Binding ConfigCoreHint}"
FontSize="10"
FontWeight="SemiBold"
Foreground="{StaticResource AccentBrush}"
VerticalAlignment="Center"/>
<TextBlock Grid.Column="6"
Text="{Binding ConfigValidationText}"
FontSize="10"
Foreground="{StaticResource TextSecondaryBrush}"
FlowDirection="LeftToRight"
TextTrimming="CharacterEllipsis"
VerticalAlignment="Center"/>
</Grid>
</StackPanel>
<!-- End V2Ray fields -->
<!-- OpenVPN fields (shown only when TunnelType = OpenVpn) -->
<StackPanel Visibility="{Binding CurrentTunnelType,
Converter={StaticResource EnumToVis},
ConverterParameter=OpenVpn}">
<Border Background="#11FFFFFF" CornerRadius="8" Padding="10,8" Margin="0,0,0,8">
<StackPanel>
<TextBlock Text="TunnelX فایل .ovpn را با OpenVPN Community اجرا می‌کند و مسیر/DNS پیش‌فرض OpenVPN را کنترل می‌کند تا فقط برنامه‌های انتخابی از تونل عبور کنند."
TextWrapping="Wrap"
FontSize="11"
Foreground="{StaticResource TextSecondaryBrush}"/>
<TextBlock Text="OpenVPN Connect به‌تنهایی کافی نیست؛ اگر Community نصب نباشد، از دکمه دانلود پایین استفاده کنید."
TextWrapping="Wrap"
FontSize="10"
Margin="0,5,0,0"
Foreground="{StaticResource WarningBrush}"/>
</StackPanel>
</Border>
<TextBlock Style="{StaticResource FieldLabel}" Text="فایل OpenVPN (.ovpn)"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0"
Style="{StaticResource ModernTextBox}"
Text="{Binding SelectedOpenVpnConfigPath}"
IsReadOnly="True"
FlowDirection="LeftToRight"/>
<Button Grid.Column="2"
Style="{StaticResource SecondaryButton}"
Content="انتخاب فایل"
Command="{Binding BrowseOpenVpnConfigCommand}"
FontSize="10"
Padding="12,6"/>
<Button Grid.Column="4"
Style="{StaticResource SecondaryButton}"
Content="حذف فایل"
Command="{Binding ClearConfigCommand}"
FontSize="10"
Padding="12,6"/>
</Grid>
<Border Background="#11FFFFFF" CornerRadius="8" Padding="10,8" Margin="0,6,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding OpenVpnPrerequisiteText}"
Foreground="{Binding OpenVpnPrerequisiteColor, Converter={StaticResource StringToColor}}"
FontSize="10"
TextWrapping="Wrap"
FlowDirection="{Binding AppFlowDirection}"/>
<Button Grid.Column="2"
Style="{StaticResource SecondaryButton}"
Content="دانلود OpenVPN"
Command="{Binding OpenOpenVpnCommunityDownloadCommand}"
FontSize="10"
Padding="10,5"/>
</Grid>
</Border>
<Grid Margin="0,6,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1">
<TextBlock Style="{StaticResource FieldLabel}" Text="نام کاربری"/>
<TextBox Style="{StaticResource ModernTextBox}"
Text="{Binding OpenVpnUsername, UpdateSourceTrigger=PropertyChanged}"
FlowDirection="LeftToRight"/>
</StackPanel>
<StackPanel Grid.Column="3">
<TextBlock Style="{StaticResource FieldLabel}" Text="رمز عبور"/>
<PasswordBox x:Name="OpenVpnPasswordField"
Style="{StaticResource ModernPasswordBox}"
FlowDirection="LeftToRight"/>
</StackPanel>
</Grid>
<TextBlock Text="{Binding ConfigValidationText}"
FontSize="10"
Foreground="{StaticResource TextSecondaryBrush}"
TextWrapping="Wrap"
Margin="0,6,0,0"/>
</StackPanel>
<!-- End OpenVPN fields -->
<!-- SOCKS/Proxy fields (shown only when TunnelType = SocksProxy) -->
<StackPanel Visibility="{Binding CurrentTunnelType,
Converter={StaticResource EnumToVis},
ConverterParameter=SocksProxy}">
<Border Background="#11FFFFFF" CornerRadius="8" Padding="10,8" Margin="0,0,0,8">
<StackPanel>
<TextBlock Text="برای سرورهای SOCKS5 یا HTTP Proxy، اطلاعات را جداگانه وارد کنید. TunnelX از همین پراکسی یک TUN داخلی می‌سازد تا اسپلیت‌تانلینگ برنامه‌های انتخابی مثل سایر نوع‌های اتصال کار کند."
TextWrapping="Wrap"
FontSize="11"
Foreground="{StaticResource TextSecondaryBrush}"/>
<TextBlock Text="اگر پراکسی شما نام کاربری یا رمز ندارد، فیلدهای احراز هویت را خالی بگذارید."
TextWrapping="Wrap"
FontSize="10"
Margin="0,5,0,0"
Foreground="{StaticResource AccentBrush}"/>
</StackPanel>
</Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource FieldLabel}" Text="نوع پراکسی"/>
<ComboBox Style="{StaticResource DarkComboBox}"
SelectedValue="{Binding ProxyProtocol, Mode=TwoWay}"
SelectedValuePath="Tag">
<ComboBoxItem Content="SOCKS5"
Tag="{x:Static models:ProxyProtocol.Socks5}"/>
<ComboBoxItem Content="HTTP Proxy"
Tag="{x:Static models:ProxyProtocol.Http}"/>
</ComboBox>
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Style="{StaticResource FieldLabel}" Text="پورت"/>
<TextBox Style="{StaticResource ModernTextBox}"
Text="{Binding ProxyPortText, UpdateSourceTrigger=PropertyChanged}"
FlowDirection="LeftToRight"/>
</StackPanel>
</Grid>
<TextBlock Style="{StaticResource FieldLabel}" Text="آدرس IP یا دامنه سرور" Margin="0,6,0,0"/>
<TextBox Style="{StaticResource ModernTextBox}"
Text="{Binding ProxyServerAddress, UpdateSourceTrigger=PropertyChanged}"
FlowDirection="LeftToRight"/>
<Grid Margin="0,6,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource FieldLabel}" Text="نام کاربری"/>
<TextBox Style="{StaticResource ModernTextBox}"
Text="{Binding ProxyUsername, UpdateSourceTrigger=PropertyChanged}"
FlowDirection="LeftToRight"/>
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Style="{StaticResource FieldLabel}" Text="رمز عبور"/>
<PasswordBox x:Name="ProxyPasswordField"
Style="{StaticResource ModernPasswordBox}"
FlowDirection="LeftToRight"/>
</StackPanel>
</Grid>
<TextBlock Text="{Binding ConfigValidationText}"
FontSize="10"
Foreground="{StaticResource TextSecondaryBrush}"
TextWrapping="Wrap"
Margin="0,6,0,0"/>
</StackPanel>
<!-- End SOCKS/Proxy fields -->
<Border Background="#11FFFFFF" CornerRadius="8" Padding="10,8" Margin="0,8,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="🏓 سرور" FontSize="11"
FontWeight="SemiBold"
Foreground="{StaticResource TextPrimaryBrush}"
VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="{Binding ServerPingResult}"
FontSize="11"
Foreground="{StaticResource AccentBrush}"
FlowDirection="LeftToRight"
VerticalAlignment="Center"
TextTrimming="CharacterEllipsis"/>
<Button Grid.Column="4"
Style="{StaticResource SecondaryButton}"
Content="{Binding ServerPingButtonText}"
Command="{Binding TestServerPingCommand}"
FontSize="11"
Padding="10,5"
ToolTip="قبل از اتصال، دسترسی و latency سرور را تست می‌کند"/>
</Grid>
</Border>
</StackPanel>
</Border>
</StackPanel>
<!-- End Disconnected State -->
<!-- ══ CONNECTED STATE: compact status dashboard ══ -->
<StackPanel Visibility="{Binding IsConnected, Converter={StaticResource BoolToVis}}">
<!-- Connection summary -->
<Grid Margin="0,4,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.95*"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="0.9*"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="1.45*"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="0.95*"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="0.95*"/>
</Grid.ColumnDefinitions>
<!-- Connected profile -->
<Border Grid.Column="0" Background="#1A6CCB5F" CornerRadius="10" Padding="10,8"
ToolTip="وضعیت و پروفایل فعال">
<StackPanel HorizontalAlignment="Center">
<TextBlock Text="✓" FontSize="15" FontWeight="Bold"
Foreground="{StaticResource SuccessBrush}"
HorizontalAlignment="Center"/>
<TextBlock Text="{Binding ConnectedBadgeText}" FontSize="11" FontWeight="SemiBold"
Foreground="{StaticResource SuccessBrush}"
HorizontalAlignment="Center"
Margin="0,2,0,0"/>
<TextBlock Text="{Binding ConnectedProfileName}" FontSize="10"
FontWeight="SemiBold"
Foreground="{StaticResource TextSecondaryBrush}"
HorizontalAlignment="Center"
TextAlignment="Center"
TextTrimming="CharacterEllipsis"
MaxWidth="120"/>
</StackPanel>
</Border>
<!-- Duration -->
<Border Grid.Column="2" Background="#11FFFFFF" CornerRadius="10" Padding="10,8"
ToolTip="مدت زمان اتصال فعلی از لحظه برقراری اتصال">
<StackPanel HorizontalAlignment="Center">
<TextBlock Text="⏱" FontSize="14" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding ConnectionDuration}" FontSize="14"
FontWeight="Bold" Foreground="{StaticResource AccentBrush}"
HorizontalAlignment="Center" Margin="0,2,0,0"
FlowDirection="LeftToRight"/>
<TextBlock Text="مدت" FontSize="9"
Foreground="{StaticResource TextSecondaryBrush}"
HorizontalAlignment="Center"/>
</StackPanel>
</Border>
<!-- Connection / Exit IP -->
<Border Grid.Column="4" Background="#11FFFFFF" CornerRadius="10" Padding="10,8"
ToolTip="IP عمومی‌ای که مقصدهای اینترنتی شما را با آن می‌بینند">
<StackPanel HorizontalAlignment="Center">
<TextBlock Text="🌐" FontSize="14" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding ConnectionIpText}" FontSize="13"
FontWeight="SemiBold" Foreground="{StaticResource SuccessBrush}"
HorizontalAlignment="Center" Margin="0,2,0,0"
FlowDirection="LeftToRight"/>
<TextBlock Text="{Binding ConnectionIpLabel}" FontSize="9"
Foreground="{StaticResource TextSecondaryBrush}"
HorizontalAlignment="Center"/>
</StackPanel>
</Border>
<!-- Tunnel Traffic -->
<Border Grid.Column="6" Background="#11FFFFFF" CornerRadius="10" Padding="10,8"
ToolTip="مجموع ترافیک ارسال و دریافت عبوری از تونل VPN (کل تونل)">
<StackPanel HorizontalAlignment="Center">
<TextBlock Text="📊" FontSize="14" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding TotalTraffic}" FontSize="13"
FontWeight="SemiBold" Foreground="{StaticResource WarningBrush}"
HorizontalAlignment="Center" Margin="0,2,0,0"
FlowDirection="LeftToRight"/>
<TextBlock Text="تونل" FontSize="9"
Foreground="{StaticResource TextSecondaryBrush}"
HorizontalAlignment="Center"/>
</StackPanel>
</Border>
<!-- Direct diagnostic -->
<Border Grid.Column="8" Background="#11FFFFFF" CornerRadius="10" Padding="10,8"
ToolTip="نمایش تشخیصی ترافیک خارج از تونل. این عدد در مصرف تونل و تاریخچه ثبت نمی‌شود.">
<StackPanel HorizontalAlignment="Center">
<TextBlock Text="📡" FontSize="14" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding DirectTraffic}" FontSize="13"
FontWeight="SemiBold" Foreground="{StaticResource TextPrimaryBrush}"
HorizontalAlignment="Center" Margin="0,2,0,0"
FlowDirection="LeftToRight"/>
<TextBlock Text="خارج تونل" FontSize="9"
Foreground="{StaticResource TextSecondaryBrush}"
HorizontalAlignment="Center"/>
</StackPanel>
</Border>
</Grid>
<!-- Route, manual proxy, ping -->
<Grid Margin="0,6,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="#11FFFFFF" CornerRadius="10" Padding="12,12" MinHeight="136">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Text="🌐 عبور کل سیستم" FontSize="12" FontWeight="SemiBold"
Foreground="{StaticResource TextPrimaryBrush}"/>
<TextBlock Text="{Binding RouteModeDescription}" FontSize="10"
Foreground="{StaticResource TextSecondaryBrush}"
TextWrapping="Wrap"
LineHeight="17"
Margin="0,4,10,0"/>
</StackPanel>
<CheckBox Grid.Column="1"
Style="{StaticResource ToggleSwitch}"
IsChecked="{Binding IsFullRouteEnabled, Mode=TwoWay}"
VerticalAlignment="Center"
ToolTip="روشن: کل ویندوز از تونل. خاموش: فقط برنامه‌ها و قوانین انتخابی."/>
</Grid>
</Border>
<Border Grid.Column="2" Background="#11FFFFFF" CornerRadius="10" Padding="12,12" MinHeight="136"
ToolTip="اگر برنامه‌ای خودکار وارد تونل نشد، این آدرس را در تنظیمات Proxy همان برنامه وارد کنید.">
<StackPanel>
<TextBlock Text="🧦 پروکسی دستی" FontSize="12" FontWeight="SemiBold"
Foreground="{StaticResource TextPrimaryBrush}"/>
<TextBlock Text="{Binding MixedProxyInfo}" FontSize="11" FontWeight="SemiBold"
Foreground="{StaticResource AccentBrush}"
FlowDirection="LeftToRight"
Margin="0,5,0,0"/>
<TextBlock Text="این آدرس داخلی را در برنامه‌هایی وارد کنید که تنظیم Proxy جداگانه دارند یا خودکار وارد تونل نمی‌شوند."
FontSize="10"
Foreground="{StaticResource TextSecondaryBrush}"
TextWrapping="Wrap"
LineHeight="17"
Margin="0,4,0,0"/>
</StackPanel>
</Border>
<Border Grid.Column="4" Background="#11FFFFFF" CornerRadius="10" Padding="12,12" MinHeight="136">
<StackPanel>
<TextBlock Text="🏓 تست مسیر" FontSize="12" FontWeight="SemiBold"
Foreground="{StaticResource TextPrimaryBrush}"
Margin="0,0,0,3"/>
<TextBlock Text="یک دامنه یا IP را از داخل تونل تست کنید."
FontSize="10"
Foreground="{StaticResource TextSecondaryBrush}"
TextWrapping="Wrap"
Margin="0,0,0,6"/>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="6"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding PingTarget, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ModernTextBox}"
FlowDirection="LeftToRight" FontSize="11"
Padding="8,6" VerticalAlignment="Center"
ToolTip="IP یا دامنه مقصد برای تست از داخل تونل"/>
<Button Grid.Row="0" Grid.Column="2" Content="{Binding PingButtonText}"
Command="{Binding TogglePingCommand}"
Style="{StaticResource SecondaryButton}"
FontSize="10" Padding="10,6"
ToolTip="تست همین مقصد از داخل مسیر تونل"/>
<Button Grid.Row="2" Grid.ColumnSpan="3" Content="{Binding ConnectedServerPingButtonText}"
Command="{Binding TestConnectedServerPingCommand}"
Style="{StaticResource SecondaryButton}"
FontSize="10" Padding="9,5"
HorizontalAlignment="Stretch"
ToolTip="دسترسی به سرور همین اتصال را تست می‌کند"/>
</Grid>
<TextBlock Text="{Binding PingResult}" FontSize="11"
FontWeight="SemiBold" FlowDirection="LeftToRight"
Foreground="{StaticResource AccentBrush}"
Margin="0,6,0,0"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
</Border>
</Grid>
<Button Command="{Binding ConnectCommand}"
MinWidth="220"
Height="50"
Padding="18,8"
HorizontalAlignment="Center"
Margin="0,10,0,0"
ToolTip="قطع اتصال فعلی">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="#DC2626"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="DisconnectButtonBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="16"
Padding="{TemplateBinding Padding}">
<Border.Effect>
<DropShadowEffect Color="#B91C1C"
BlurRadius="18"
Opacity="0.28"
ShadowDepth="0"/>
</Border.Effect>
<Grid FlowDirection="{Binding AppFlowDirection}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0"
Width="34"
Height="34"
CornerRadius="17"
Background="#26FFFFFF"
VerticalAlignment="Center">
<TextBlock Text="⏻"
FontSize="18"
FontWeight="Bold"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<StackPanel Grid.Column="2" VerticalAlignment="Center">
<TextBlock Text="قطع اتصال"
FontSize="16"
FontWeight="Bold"
Foreground="White"
HorizontalAlignment="Center"/>
</StackPanel>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="DisconnectButtonBorder" Property="Background" Value="#B91C1C"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="DisconnectButtonBorder" Property="Background" Value="#991B1B"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
<Border Background="#24E8803A"
BorderBrush="#66E8803A"
BorderThickness="1"
CornerRadius="20"
Padding="24,18"
Margin="0,16,0,0"
MinWidth="560"
MaxWidth="760"
HorizontalAlignment="Stretch">
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
FlowDirection="{Binding AppFlowDirection}">
<TextBlock Text="{Binding AdPlaceholderTitleText}"
FontSize="20"
FontWeight="Bold"
Foreground="{StaticResource TextPrimaryBrush}"
HorizontalAlignment="Center"
TextAlignment="Center"/>
<TextBlock Text="{Binding AdAudienceText}"
Visibility="{Binding HasGitHubInstallCount, Converter={StaticResource BoolToVis}}"
FontSize="12"
FontWeight="SemiBold"
Foreground="{StaticResource TextSecondaryBrush}"
HorizontalAlignment="Center"
TextAlignment="Center"
TextWrapping="Wrap"
MaxWidth="560"
Margin="0,6,0,0"/>
<Button
Content="{Binding AdRequestButtonText}"
Command="{Binding OpenAdRequestCommand}"
Style="{StaticResource PrimaryButton}"
Padding="18,10"
FontSize="13"
MinWidth="130"
HorizontalAlignment="Center"
Margin="0,12,0,0"
VerticalAlignment="Center"
ToolTip="{Binding AdRequestButtonText}"/>
</StackPanel>
</Border>
</StackPanel>
<!-- End Connected State -->
<!-- Split Tunnel Tip (always visible) -->
<Border Background="#1AFFC107" BorderBrush="#33FFC107" BorderThickness="1"
CornerRadius="10" Padding="10,8" Margin="0,8,0,0">
<StackPanel>
<TextBlock TextWrapping="Wrap" FontSize="10" LineHeight="17"
FlowDirection="{Binding AppFlowDirection}"
Foreground="{StaticResource TextPrimaryBrush}">
<Run Text="💡 در حالت انتخابی، فقط برنامه‌های فعال در تب "/>
<Run Text="«برنامه‌ها»" FontWeight="SemiBold"/>
<Run Text=" از تونل عبور می‌کنند؛ بقیه مستقیم می‌مانند."/>
</TextBlock>
<TextBlock TextWrapping="Wrap" FontSize="10" LineHeight="17"
FlowDirection="{Binding AppFlowDirection}"
Foreground="{StaticResource TextSecondaryBrush}">
<Run Text="📌 برای تلگرام، واتس‌اپ و برنامه‌های Store، "/>
<Run Text="Microsoft Edge WebView2"
FlowDirection="LeftToRight"
FontWeight="SemiBold"/>
<Run Text=" را هم به لیست تونل اضافه کنید."/>
</TextBlock>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</UserControl>