재구성 AX Agent 설정과 채팅 UI를 Claude형 구조로
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
This commit is contained in:
498
src/AxCopilot/Views/AgentSettingsWindow.xaml
Normal file
498
src/AxCopilot/Views/AgentSettingsWindow.xaml
Normal file
@@ -0,0 +1,498 @@
|
||||
<Window x:Class="AxCopilot.Views.AgentSettingsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="AX Agent 설정"
|
||||
Width="780"
|
||||
Height="760"
|
||||
MinWidth="700"
|
||||
MinHeight="580"
|
||||
WindowStyle="None"
|
||||
ResizeMode="CanResizeWithGrip"
|
||||
Background="{DynamicResource LauncherBackground}"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ShowInTaskbar="False">
|
||||
<Window.Resources>
|
||||
<Style x:Key="OutlineHoverBtn" TargetType="Button">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource BorderColor}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryText}"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Padding" Value="9,5"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="Bd"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="8"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ItemHoverBackground}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ToggleSwitch" TargetType="CheckBox">
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="CheckBox">
|
||||
<Grid Width="46" Height="26" VerticalAlignment="Center">
|
||||
<Border x:Name="Track"
|
||||
Width="46"
|
||||
Height="26"
|
||||
CornerRadius="13"
|
||||
Background="#D0D0E0"/>
|
||||
<Ellipse x:Name="Thumb"
|
||||
Width="20"
|
||||
Height="20"
|
||||
Margin="3,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Fill="White">
|
||||
<Ellipse.Effect>
|
||||
<DropShadowEffect BlurRadius="5" ShadowDepth="1" Opacity="0.25" Direction="270"/>
|
||||
</Ellipse.Effect>
|
||||
</Ellipse>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="Track" Property="Background" Value="{DynamicResource AccentColor}"/>
|
||||
<Setter TargetName="Thumb" Property="HorizontalAlignment" Value="Right"/>
|
||||
<Setter TargetName="Thumb" Property="Margin" Value="0,0,3,0"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Border Background="{DynamicResource LauncherBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="56"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border Grid.Row="0"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource SeparatorColor}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<Grid Margin="14,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="AX Agent 설정"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<Border Grid.Column="1"
|
||||
Cursor="Hand"
|
||||
Padding="8,4"
|
||||
CornerRadius="8"
|
||||
Background="Transparent"
|
||||
MouseLeftButtonUp="BtnClose_MouseLeftButtonUp">
|
||||
<TextBlock Text="닫기"
|
||||
Foreground="{DynamicResource SecondaryText}"
|
||||
FontSize="12"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<ScrollViewer Grid.Row="1"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel Margin="18,14,18,16">
|
||||
<TextBlock Text="테마"
|
||||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<WrapPanel Margin="0,8,0,0">
|
||||
<Border x:Name="ThemeSystemCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="ThemeSystemCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="System" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
<Border x:Name="ThemeLightCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="ThemeLightCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="Light" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
<Border x:Name="ThemeDarkCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="ThemeDarkCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="Dark" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
</WrapPanel>
|
||||
|
||||
<Border Height="1" Margin="0,10,0,10" Background="{DynamicResource SeparatorColor}"/>
|
||||
|
||||
<TextBlock Text="모델 및 연결"
|
||||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<TextBlock Text="서비스를 선택하고 모델, 연결 옵션, 운영 모드를 조정합니다."
|
||||
Margin="0,4,0,10"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource SecondaryText}"/>
|
||||
<WrapPanel>
|
||||
<Border x:Name="SvcOllamaCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="SvcOllamaCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="Ollama" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
<Border x:Name="SvcVllmCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="SvcVllmCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="vLLM" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
<Border x:Name="SvcGeminiCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="SvcGeminiCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="Gemini" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
<Border x:Name="SvcClaudeCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="SvcClaudeCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="Claude" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
</WrapPanel>
|
||||
<TextBox x:Name="ModelInput"
|
||||
Margin="0,6,0,8"
|
||||
Padding="8,6"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
FontSize="12"/>
|
||||
<WrapPanel x:Name="ModelChipPanel" Margin="0,0,0,8"/>
|
||||
<Grid Margin="0,6,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="vLLM SSL 인증서 검증 우회 허용"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<CheckBox x:Name="ChkVllmAllowInsecureTls"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource ToggleSwitch}"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="운영 모드"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<Button x:Name="BtnOperationMode"
|
||||
Grid.Column="1"
|
||||
MinWidth="140"
|
||||
Style="{StaticResource OutlineHoverBtn}"
|
||||
Click="BtnOperationMode_Click"/>
|
||||
</Grid>
|
||||
|
||||
<Border Height="1" Margin="0,10,0,10" Background="{DynamicResource SeparatorColor}"/>
|
||||
|
||||
<TextBlock Text="권한 및 실행"
|
||||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="권한 모드"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<Button x:Name="BtnPermissionMode"
|
||||
Grid.Column="1"
|
||||
MinWidth="120"
|
||||
Style="{StaticResource OutlineHoverBtn}"
|
||||
Click="BtnPermissionMode_Click"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="계획 모드"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<Button x:Name="BtnPlanMode"
|
||||
Grid.Column="1"
|
||||
MinWidth="120"
|
||||
Style="{StaticResource OutlineHoverBtn}"
|
||||
Click="BtnPlanMode_Click"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="추론 강도"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<Button x:Name="BtnReasoningMode"
|
||||
Grid.Column="1"
|
||||
MinWidth="120"
|
||||
Style="{StaticResource OutlineHoverBtn}"
|
||||
Click="BtnReasoningMode_Click"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="폴더 데이터 활용"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<Button x:Name="BtnFolderDataUsage"
|
||||
Grid.Column="1"
|
||||
MinWidth="120"
|
||||
Style="{StaticResource OutlineHoverBtn}"
|
||||
Click="BtnFolderDataUsage_Click"/>
|
||||
</Grid>
|
||||
|
||||
<StackPanel x:Name="AdvancedPanel">
|
||||
<Border Height="1" Margin="0,10,0,10" Background="{DynamicResource SeparatorColor}"/>
|
||||
|
||||
<TextBlock Text="컨텍스트 및 오류 관리"
|
||||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="자동 컨텍스트 압축 사용"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<CheckBox x:Name="ChkEnableProactiveCompact"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource ToggleSwitch}"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="압축 시작 한도(%)"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBox x:Name="TxtContextCompactTriggerPercent"
|
||||
Grid.Column="1"
|
||||
Padding="8,5"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
FontSize="12"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="최대 컨텍스트 토큰"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBox x:Name="TxtMaxContextTokens"
|
||||
Grid.Column="1"
|
||||
Padding="8,5"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
FontSize="12"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="오류 재시도 횟수"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBox x:Name="TxtMaxRetryOnError"
|
||||
Grid.Column="1"
|
||||
Padding="8,5"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
FontSize="12"/>
|
||||
</Grid>
|
||||
|
||||
<Border Height="1" Margin="0,10,0,10" Background="{DynamicResource SeparatorColor}"/>
|
||||
|
||||
<TextBlock Text="도구 및 검증"
|
||||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="스킬 시스템"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<CheckBox x:Name="ChkEnableSkillSystem"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource ToggleSwitch}"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="도구 훅 사용"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<CheckBox x:Name="ChkEnableToolHooks"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource ToggleSwitch}"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="입력 변형 반영"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<CheckBox x:Name="ChkEnableHookInputMutation"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource ToggleSwitch}"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="권한 갱신 반영"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<CheckBox x:Name="ChkEnableHookPermissionUpdate"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource ToggleSwitch}"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Cowork 검증 강제"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<CheckBox x:Name="ChkEnableCoworkVerification"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource ToggleSwitch}"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Code 검증 강제"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<CheckBox x:Name="ChkEnableCodeVerification"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource ToggleSwitch}"/>
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="병렬 도구 실행"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<CheckBox x:Name="ChkEnableParallelTools"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource ToggleSwitch}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<Border Grid.Row="2"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource SeparatorColor}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,14,0">
|
||||
<Button Content="고급 설정 열기"
|
||||
Style="{StaticResource OutlineHoverBtn}"
|
||||
Margin="0,0,8,0"
|
||||
Click="BtnOpenFullSettings_Click"/>
|
||||
<Button Content="저장"
|
||||
MinWidth="96"
|
||||
Margin="0,0,8,0"
|
||||
Background="{DynamicResource AccentColor}"
|
||||
Foreground="White"
|
||||
BorderThickness="0"
|
||||
Padding="10,6"
|
||||
Cursor="Hand"
|
||||
Click="BtnSave_Click"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
||||
@@ -112,6 +112,36 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="SettingsNavBtn" TargetType="RadioButton">
|
||||
<Setter Property="Foreground" Value="{DynamicResource SecondaryText}"/>
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Padding" Value="14,10"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="RadioButton">
|
||||
<Border x:Name="Bd"
|
||||
Background="Transparent"
|
||||
CornerRadius="10"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ItemHoverBackground}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryText}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource HintBackground}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryText}"/>
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<!-- 모든 드롭다운 메뉴는 코드-비하인드에서 커스텀 Popup으로 렌더링 -->
|
||||
</Window.Resources>
|
||||
|
||||
@@ -185,7 +215,9 @@
|
||||
<!-- 좌측: 사이드바 -->
|
||||
<!-- ══════════════════════════════════════════════════════ -->
|
||||
<Border x:Name="SidebarPanel" Grid.Column="1"
|
||||
Background="{DynamicResource ItemBackground}">
|
||||
Background="{DynamicResource LauncherBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="0,0,1,0">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="52"/>
|
||||
@@ -198,14 +230,14 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 헤더: 로고 + 새 대화 -->
|
||||
<Grid Grid.Row="0" Margin="14,0">
|
||||
<Grid Grid.Row="0" Margin="16,0">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Border Background="{DynamicResource AccentColor}" CornerRadius="6"
|
||||
Width="24" Height="24">
|
||||
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="12"
|
||||
<Border Background="{DynamicResource AccentColor}" CornerRadius="8"
|
||||
Width="28" Height="28">
|
||||
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="14"
|
||||
Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<TextBlock Text="AX Agent" FontSize="13.5" FontWeight="SemiBold"
|
||||
<TextBlock Text="AX Agent" FontSize="14.5" FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center" Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
@@ -219,8 +251,9 @@
|
||||
</Grid>
|
||||
|
||||
<!-- 검색 -->
|
||||
<Border Grid.Row="1" Background="{DynamicResource HintBackground}"
|
||||
CornerRadius="10" Margin="12,0,12,6" Padding="10,6">
|
||||
<Border Grid.Row="1" Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}" BorderThickness="1"
|
||||
CornerRadius="14" Margin="12,2,12,8" Padding="12,7">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
@@ -299,12 +332,12 @@
|
||||
</Border>
|
||||
|
||||
<!-- 탭별 좌측 메뉴 -->
|
||||
<Border Grid.Row="3" Margin="12,0,12,6"
|
||||
Background="{DynamicResource HintBackground}"
|
||||
<Border Grid.Row="3" Margin="12,0,12,8"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="10"
|
||||
Padding="10,8">
|
||||
CornerRadius="14"
|
||||
Padding="12,10">
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,4" Visibility="Collapsed">
|
||||
<TextBlock x:Name="SidebarModeBadgeIcon" Text=""
|
||||
@@ -648,6 +681,21 @@
|
||||
<Border Grid.Row="0" Background="{DynamicResource LauncherBackground}"
|
||||
BorderBrush="{DynamicResource SeparatorColor}" BorderThickness="0,0,0,1">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0"
|
||||
Orientation="Horizontal"
|
||||
VerticalAlignment="Center"
|
||||
Margin="46,0,0,0">
|
||||
<TextBlock x:Name="CurrentTabTitle"
|
||||
Text="AX Agent"
|
||||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
</StackPanel>
|
||||
<!-- 좌: 사이드바 토글 -->
|
||||
<Button x:Name="BtnToggleSidebar" Style="{StaticResource GhostBtn}"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Center" Margin="8,0"
|
||||
@@ -658,9 +706,10 @@
|
||||
</Button>
|
||||
|
||||
<!-- 중앙: 탭 메뉴 -->
|
||||
<Border HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
<Border Grid.Column="1"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Background="{DynamicResource HintBackground}"
|
||||
CornerRadius="10" Padding="3"
|
||||
CornerRadius="12" Padding="4"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<RadioButton x:Name="TabChat" Content="Chat" Style="{StaticResource TopTabBtn}"
|
||||
@@ -676,7 +725,8 @@
|
||||
</Border>
|
||||
|
||||
<!-- 우: 최소화 + 최대화 + 닫기 -->
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"
|
||||
<StackPanel Grid.Column="2"
|
||||
Orientation="Horizontal" HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center" Margin="0,0,4,0"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<Button Style="{StaticResource GhostBtn}" Click="BtnMinimize_Click" ToolTip="최소화">
|
||||
@@ -744,9 +794,10 @@
|
||||
<ScrollViewer x:Name="MessageScroll" Grid.Row="3"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
Background="{DynamicResource LauncherBackground}">
|
||||
Background="{DynamicResource LauncherBackground}"
|
||||
Padding="22,20,22,0">
|
||||
<StackPanel x:Name="MessagePanel"
|
||||
Margin="0,34,0,22"
|
||||
Margin="0,20,0,28"
|
||||
HorizontalAlignment="Stretch">
|
||||
<StackPanel.RenderTransform>
|
||||
<TranslateTransform/>
|
||||
@@ -757,7 +808,7 @@
|
||||
<!-- 빈 상태 -->
|
||||
<StackPanel x:Name="EmptyState" Grid.Row="3"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
MaxWidth="520">
|
||||
MaxWidth="660">
|
||||
|
||||
<!-- 부유 AI 아이콘 -->
|
||||
<Border x:Name="EmptyIcon" CornerRadius="24" Width="72" Height="72"
|
||||
@@ -791,11 +842,11 @@
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
|
||||
<TextBlock x:Name="EmptyStateTitle" Text="작업 유형을 선택하세요" FontSize="18" FontWeight="SemiBold"
|
||||
<TextBlock x:Name="EmptyStateTitle" Text="작업 유형을 선택하세요" FontSize="22" FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}" HorizontalAlignment="Center"/>
|
||||
<TextBlock x:Name="EmptyStateDesc" Text="주제에 맞는 전문 프리셋이 자동 적용됩니다"
|
||||
FontSize="12" Foreground="{DynamicResource SecondaryText}"
|
||||
HorizontalAlignment="Center" Margin="0,8,0,24"/>
|
||||
FontSize="12.5" Foreground="{DynamicResource SecondaryText}"
|
||||
HorizontalAlignment="Center" Margin="0,10,0,28"/>
|
||||
|
||||
<!-- 대화 주제 버튼 (프리셋에서 동적 생성) -->
|
||||
<WrapPanel x:Name="TopicButtonPanel" HorizontalAlignment="Center"
|
||||
@@ -992,15 +1043,18 @@
|
||||
</Border>
|
||||
|
||||
<!-- ── 입력 바 ── -->
|
||||
<Border Grid.Row="4" Margin="48,0,48,20">
|
||||
<Border Grid.Row="4"
|
||||
Margin="28,0,28,22"
|
||||
HorizontalAlignment="Center"
|
||||
MaxWidth="980">
|
||||
<StackPanel>
|
||||
<Border x:Name="DraftPreviewCard"
|
||||
Visibility="Collapsed"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
Background="{DynamicResource LauncherBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="18"
|
||||
Padding="12,10,12,10"
|
||||
CornerRadius="22"
|
||||
Padding="14,12,14,12"
|
||||
Margin="0,0,0,10">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="14" ShadowDepth="0" Opacity="0.06"/>
|
||||
@@ -1056,7 +1110,7 @@
|
||||
<StackPanel x:Name="DraftQueuePanel"
|
||||
Visibility="Collapsed"
|
||||
Margin="0,0,0,10"/>
|
||||
<Border x:Name="InputGlowBorder" CornerRadius="26" Opacity="0"
|
||||
<Border x:Name="InputGlowBorder" CornerRadius="30" Opacity="0"
|
||||
Margin="-3" IsHitTestVisible="False">
|
||||
<Border.BorderBrush>
|
||||
<LinearGradientBrush x:Name="RainbowBrush" StartPoint="0,0" EndPoint="1,1">
|
||||
@@ -1078,11 +1132,11 @@
|
||||
</Border>
|
||||
<!-- 실제 입력 영역 -->
|
||||
<Border x:Name="InputBorder"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
CornerRadius="22" Padding="6"
|
||||
BorderBrush="{DynamicResource BorderColor}" BorderThickness="1">
|
||||
Background="{DynamicResource LauncherBackground}"
|
||||
CornerRadius="28" Padding="8"
|
||||
BorderBrush="{DynamicResource BorderColor}" BorderThickness="1.2">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="18" ShadowDepth="0" Opacity="0.08"/>
|
||||
<DropShadowEffect BlurRadius="22" ShadowDepth="0" Opacity="0.10"/>
|
||||
</Border.Effect>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
@@ -1093,7 +1147,7 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Row 0: 모델 셀렉터 + 템플릿 버튼 -->
|
||||
<Grid Grid.Row="0" Margin="5,1,5,2">
|
||||
<Grid Grid.Row="0" Margin="8,4,8,6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
@@ -1104,8 +1158,8 @@
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="0"
|
||||
MinHeight="26"
|
||||
Padding="9,2.5"
|
||||
MinHeight="30"
|
||||
Padding="11,4"
|
||||
Click="BtnModelSelector_Click"
|
||||
ToolTip="모델/추론 빠른 설정"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
@@ -1128,9 +1182,9 @@
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0"
|
||||
MinHeight="26"
|
||||
MinHeight="30"
|
||||
MinWidth="60"
|
||||
Padding="7,2.5"
|
||||
Padding="10,4"
|
||||
Click="BtnTemplateSelector_Click"
|
||||
ToolTip="프롬프트 템플릿"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
@@ -1151,9 +1205,9 @@
|
||||
<!-- Row 1: 통합 설정 패널 -->
|
||||
<Border x:Name="InlineSettingsPanel"
|
||||
Grid.Row="1"
|
||||
Margin="8,2,8,3"
|
||||
Padding="9,8,9,7"
|
||||
CornerRadius="12"
|
||||
Margin="8,2,8,6"
|
||||
Padding="11,10,11,9"
|
||||
CornerRadius="14"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
Background="{DynamicResource SecondaryBackground}"
|
||||
@@ -1275,7 +1329,7 @@
|
||||
</ItemsControl>
|
||||
|
||||
<!-- 입력 영역 (Row 3) -->
|
||||
<Grid Grid.Row="3">
|
||||
<Grid Grid.Row="3" Margin="2,0,2,2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
@@ -1290,7 +1344,7 @@
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
Background="Transparent"
|
||||
CaretBrush="{DynamicResource AccentColor}"
|
||||
BorderThickness="0" Padding="12,8"
|
||||
BorderThickness="0" Padding="14,10"
|
||||
VerticalContentAlignment="Top"
|
||||
AcceptsReturn="True"
|
||||
PreviewKeyDown="InputBox_PreviewKeyDown"
|
||||
@@ -1330,7 +1384,7 @@
|
||||
|
||||
<!-- 파일 첨부 -->
|
||||
<Button x:Name="BtnAttach" Style="{StaticResource GhostBtn}" Grid.Column="1"
|
||||
Width="34" Height="34" Margin="0,0,2,0" VerticalAlignment="Bottom"
|
||||
Width="36" Height="36" Margin="0,0,4,0" VerticalAlignment="Bottom"
|
||||
Click="BtnAttach_Click" ToolTip="파일 첨부"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True"
|
||||
AllowDrop="True">
|
||||
@@ -1340,7 +1394,7 @@
|
||||
|
||||
<!-- 내보내기 -->
|
||||
<Button Style="{StaticResource GhostBtn}" Grid.Column="2"
|
||||
Width="34" Height="34" Margin="0,0,2,0" VerticalAlignment="Bottom"
|
||||
Width="36" Height="36" Margin="0,0,4,0" VerticalAlignment="Bottom"
|
||||
Click="BtnExport_Click" ToolTip="대화 내보내기"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="14"
|
||||
@@ -1349,7 +1403,7 @@
|
||||
|
||||
<!-- 일시정지 -->
|
||||
<Border x:Name="BtnPause" Grid.Column="3"
|
||||
Width="32" Height="32" Margin="0,0,2,0"
|
||||
Width="34" Height="34" Margin="0,0,4,0"
|
||||
CornerRadius="8" Cursor="Hand"
|
||||
Background="{DynamicResource ItemHoverBackground}" Visibility="Collapsed"
|
||||
VerticalAlignment="Bottom"
|
||||
@@ -1363,7 +1417,7 @@
|
||||
|
||||
<!-- 중지/전송 -->
|
||||
<Button x:Name="BtnStop" Grid.Column="3"
|
||||
Width="38" Height="38" Margin="4,0,4,0"
|
||||
Width="40" Height="40" Margin="4,0,4,0"
|
||||
Cursor="Hand" Click="BtnStop_Click"
|
||||
VerticalAlignment="Bottom" Visibility="Collapsed"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
@@ -1378,7 +1432,7 @@
|
||||
</Button.Template>
|
||||
</Button>
|
||||
<Button x:Name="BtnSend" Grid.Column="4"
|
||||
Width="38" Height="38" Margin="4,0,4,0"
|
||||
Width="42" Height="42" Margin="6,0,4,0"
|
||||
Cursor="Hand" Click="BtnSend_Click"
|
||||
VerticalAlignment="Bottom">
|
||||
<Button.Template>
|
||||
@@ -1657,6 +1711,507 @@
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- ══ AX Agent 설정 오버레이 (채팅 화면 내부 전체 덮기) ══ -->
|
||||
<Border x:Name="AgentSettingsOverlay"
|
||||
Grid.Column="2"
|
||||
Grid.ColumnSpan="3"
|
||||
Background="{DynamicResource LauncherBackground}"
|
||||
Visibility="Collapsed"
|
||||
Panel.ZIndex="120">
|
||||
<Grid Margin="0">
|
||||
<Border Background="{DynamicResource LauncherBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="0"
|
||||
Padding="0">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="220"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.RowSpan="2"
|
||||
Grid.Column="0"
|
||||
Background="{DynamicResource LauncherBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="0,0,1,0">
|
||||
<StackPanel Margin="24,26,20,18">
|
||||
<TextBlock Text="AX Agent 설정"
|
||||
FontSize="26"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
Margin="0,0,0,8"/>
|
||||
<TextBlock Text="핵심 동작과 연결 설정을 이 화면에서 바로 관리합니다."
|
||||
FontSize="12.5"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource SecondaryText}"
|
||||
Margin="0,0,0,20"/>
|
||||
<TextBlock Text="환경"
|
||||
FontSize="11"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource SecondaryText}"
|
||||
Margin="6,0,0,10"/>
|
||||
<RadioButton x:Name="OverlayNavCommon"
|
||||
Content="일반"
|
||||
GroupName="OverlayNav"
|
||||
Style="{StaticResource SettingsNavBtn}"
|
||||
HorizontalAlignment="Stretch"
|
||||
Checked="OverlayNav_Checked"
|
||||
Tag="common"
|
||||
IsChecked="True"/>
|
||||
<RadioButton x:Name="OverlayNavService"
|
||||
Content="서비스와 모델"
|
||||
GroupName="OverlayNav"
|
||||
Style="{StaticResource SettingsNavBtn}"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="0,6,0,0"
|
||||
Checked="OverlayNav_Checked"
|
||||
Tag="service"/>
|
||||
<RadioButton x:Name="OverlayNavPermission"
|
||||
Content="권한과 실행"
|
||||
GroupName="OverlayNav"
|
||||
Style="{StaticResource SettingsNavBtn}"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="0,6,0,0"
|
||||
Checked="OverlayNav_Checked"
|
||||
Tag="permission"/>
|
||||
<RadioButton x:Name="OverlayNavAdvanced"
|
||||
Content="고급"
|
||||
GroupName="OverlayNav"
|
||||
Style="{StaticResource SettingsNavBtn}"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="0,6,0,0"
|
||||
Checked="OverlayNav_Checked"
|
||||
Tag="advanced"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Grid Grid.Row="0" Grid.Column="1" Margin="16,14,16,10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Style="{StaticResource GhostBtn}"
|
||||
Padding="10,6"
|
||||
Click="BtnOverlaySettingsClose_Click"
|
||||
ToolTip="대화로 돌아가기">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text=""
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource SecondaryText}"
|
||||
Margin="0,0,6,0"/>
|
||||
<TextBlock Text="설정"
|
||||
FontSize="12.5"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<StackPanel Grid.Column="1" Orientation="Vertical" Margin="16,0,0,0">
|
||||
<TextBlock Text="AX Agent 설정"
|
||||
FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<TextBlock Text="채팅을 벗어나지 않고 AX Agent 전용 설정을 정리합니다"
|
||||
Margin="0,6,0,0"
|
||||
FontSize="12.5"
|
||||
Foreground="{DynamicResource SecondaryText}"/>
|
||||
</StackPanel>
|
||||
<Button Grid.Column="2"
|
||||
Style="{StaticResource GhostBtn}"
|
||||
Padding="8,6"
|
||||
Click="BtnOverlaySettingsClose_Click"
|
||||
ToolTip="설정 닫기">
|
||||
<TextBlock Text=""
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource SecondaryText}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
<ScrollViewer Grid.Row="1" Grid.Column="1"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
Padding="16,4,16,16">
|
||||
<StackPanel>
|
||||
<Border x:Name="OverlaySectionService"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="12"
|
||||
Padding="12"
|
||||
Margin="0,0,0,10">
|
||||
<StackPanel>
|
||||
<TextBlock Text="사용 서비스"
|
||||
FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<TextBlock Text="대화에 사용할 LLM 서비스와 모델을 선택합니다."
|
||||
Margin="0,4,0,10"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource SecondaryText}"/>
|
||||
<WrapPanel Margin="0,0,0,8">
|
||||
<Border x:Name="OverlaySvcOllamaCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="OverlaySvcOllamaCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="Ollama" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
<Border x:Name="OverlaySvcVllmCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="OverlaySvcVllmCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="vLLM" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
<Border x:Name="OverlaySvcGeminiCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="OverlaySvcGeminiCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="Gemini" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
<Border x:Name="OverlaySvcClaudeCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="OverlaySvcClaudeCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="Claude" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
</WrapPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Margin="0,0,6,0">
|
||||
<TextBlock Text="서비스"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource SecondaryText}"
|
||||
Margin="0,0,0,6"/>
|
||||
<ComboBox x:Name="CmbOverlayService"
|
||||
Height="30"
|
||||
SelectionChanged="CmbOverlayService_SelectionChanged"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1" Margin="6,0,0,0">
|
||||
<TextBlock Text="모델"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource SecondaryText}"
|
||||
Margin="0,0,0,6"/>
|
||||
<ComboBox x:Name="CmbOverlayModel"
|
||||
Height="30"
|
||||
SelectionChanged="CmbOverlayModel_SelectionChanged"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid x:Name="OverlayServiceDetailPanel" Margin="0,12,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Margin="0,0,6,0">
|
||||
<TextBlock x:Name="OverlayEndpointLabel"
|
||||
Text="기본 서버 주소"
|
||||
FontSize="11.5"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<TextBlock x:Name="OverlayEndpointHint"
|
||||
Text="선택한 서비스의 기본 연결 주소를 입력합니다."
|
||||
Margin="0,4,0,6"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource SecondaryText}"/>
|
||||
<TextBox x:Name="TxtOverlayServiceEndpoint"
|
||||
Padding="9,7"
|
||||
Background="{DynamicResource LauncherBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
FontSize="12"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1" Margin="6,0,0,0">
|
||||
<TextBlock x:Name="OverlayApiKeyLabel"
|
||||
Text="API 키"
|
||||
FontSize="11.5"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<TextBlock x:Name="OverlayApiKeyHint"
|
||||
Text="접근 제어가 필요한 경우에만 입력합니다."
|
||||
Margin="0,4,0,6"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource SecondaryText}"/>
|
||||
<PasswordBox x:Name="TxtOverlayServiceApiKey"
|
||||
Padding="9,7"
|
||||
Background="{DynamicResource LauncherBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
FontSize="12"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border x:Name="OverlaySectionQuick"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="12"
|
||||
Padding="12">
|
||||
<StackPanel>
|
||||
<TextBlock Text="빠른 제어"
|
||||
FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<TextBlock Text="Codex/Claude 스타일로 핵심 기능만 간결하게 제공합니다."
|
||||
Margin="0,4,0,10"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource SecondaryText}"/>
|
||||
<WrapPanel>
|
||||
<Button x:Name="BtnOverlayFastMode"
|
||||
Style="{StaticResource OutlineHoverBtn}"
|
||||
Margin="0,0,8,8"
|
||||
Padding="11,6"
|
||||
Content="Fast"
|
||||
Click="BtnInlineFastMode_Click"/>
|
||||
<Button x:Name="BtnOverlayReasoning"
|
||||
Style="{StaticResource OutlineHoverBtn}"
|
||||
Margin="0,0,8,8"
|
||||
Padding="11,6"
|
||||
Content="Reasoning"
|
||||
Click="BtnInlineReasoning_Click"/>
|
||||
<Button x:Name="BtnOverlayPlanMode"
|
||||
Style="{StaticResource OutlineHoverBtn}"
|
||||
Margin="0,0,8,8"
|
||||
Padding="11,6"
|
||||
Content="Plan"
|
||||
Click="BtnInlinePlanMode_Click"/>
|
||||
<Button x:Name="BtnOverlayPermission"
|
||||
Style="{StaticResource OutlineHoverBtn}"
|
||||
Margin="0,0,8,8"
|
||||
Padding="11,6"
|
||||
Content="권한"
|
||||
Click="BtnInlinePermission_Click"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border x:Name="OverlaySectionDetail"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="12"
|
||||
Padding="12"
|
||||
Margin="0,10,0,0">
|
||||
<StackPanel>
|
||||
<TextBlock x:Name="OverlayAnchorCommon"
|
||||
Text="상세 설정"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<TextBlock Text="트레이의 AX Agent 설정 항목을 이 화면으로 통합합니다."
|
||||
Margin="0,4,0,10"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource SecondaryText}"/>
|
||||
<Grid x:Name="OverlayAiEnabledRow" Margin="0,0,0,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel>
|
||||
<TextBlock Text="AX Agent 사용"
|
||||
FontSize="12.5"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryText}"/>
|
||||
<TextBlock Text="비활성화하면 AX Agent 대화 기능이 숨겨집니다."
|
||||
Margin="0,4,0,0"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource SecondaryText}"/>
|
||||
</StackPanel>
|
||||
<CheckBox x:Name="ChkOverlayAiEnabled"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToggleSwitch}"/>
|
||||
</Grid>
|
||||
<WrapPanel x:Name="OverlayThemePanel" Margin="0,0,0,8">
|
||||
<Border x:Name="OverlayThemeSystemCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="OverlayThemeSystemCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="System" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
<Border x:Name="OverlayThemeLightCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="OverlayThemeLightCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="Light" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
<Border x:Name="OverlayThemeDarkCard"
|
||||
Cursor="Hand"
|
||||
CornerRadius="10"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
Padding="10,8"
|
||||
Margin="0,0,8,8"
|
||||
MouseLeftButtonUp="OverlayThemeDarkCard_MouseLeftButtonUp">
|
||||
<TextBlock Text="Dark" FontSize="12" Foreground="{DynamicResource PrimaryText}"/>
|
||||
</Border>
|
||||
</WrapPanel>
|
||||
<StackPanel x:Name="OverlayModelEditorPanel">
|
||||
<TextBlock Text="등록 모델"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource SecondaryText}"
|
||||
Margin="0,4,0,6"/>
|
||||
<TextBox x:Name="TxtOverlayModelInput"
|
||||
Padding="8,6"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
FontSize="12"/>
|
||||
<WrapPanel x:Name="OverlayModelChipPanel" Margin="0,10,0,0"/>
|
||||
</StackPanel>
|
||||
<Grid x:Name="OverlayAnchorPermission" Margin="12,12,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="운영 모드"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<Button x:Name="BtnOverlayOperationMode"
|
||||
Grid.Column="1"
|
||||
MinWidth="140"
|
||||
Style="{StaticResource OutlineHoverBtn}"
|
||||
Click="BtnOverlayOperationMode_Click"/>
|
||||
</Grid>
|
||||
<Grid x:Name="OverlayFolderDataUsageRow" Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="폴더 데이터 활용"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<Button x:Name="BtnOverlayFolderDataUsage"
|
||||
Grid.Column="1"
|
||||
MinWidth="140"
|
||||
Style="{StaticResource OutlineHoverBtn}"
|
||||
Click="BtnOverlayFolderDataUsage_Click"/>
|
||||
</Grid>
|
||||
<Grid x:Name="OverlayTlsRow" Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="vLLM SSL 인증서 검증 우회 허용"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<CheckBox x:Name="ChkOverlayVllmAllowInsecureTls"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
<Grid x:Name="OverlayAnchorAdvanced" Margin="0,14,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="압축 시작 한도(%)"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBox x:Name="TxtOverlayContextCompactTriggerPercent"
|
||||
Grid.Column="1"
|
||||
Padding="8,5"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
FontSize="12"/>
|
||||
</Grid>
|
||||
<Grid x:Name="OverlayMaxContextTokensRow" Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="최대 컨텍스트 토큰"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBox x:Name="TxtOverlayMaxContextTokens"
|
||||
Grid.Column="1"
|
||||
Padding="8,5"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
FontSize="12"/>
|
||||
</Grid>
|
||||
<Grid x:Name="OverlayMaxRetryRow" Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="오류 재시도 횟수"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBox x:Name="TxtOverlayMaxRetryOnError"
|
||||
Grid.Column="1"
|
||||
Padding="8,5"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}"
|
||||
BorderThickness="1"
|
||||
Foreground="{DynamicResource PrimaryText}"
|
||||
FontSize="12"/>
|
||||
</Grid>
|
||||
<WrapPanel x:Name="OverlayAdvancedTogglePanel" Margin="0,14,0,0">
|
||||
<CheckBox x:Name="ChkOverlayEnableProactiveCompact" Content="자동 컨텍스트 압축" Margin="0,0,14,8"/>
|
||||
<CheckBox x:Name="ChkOverlayEnableSkillSystem" Content="스킬 시스템" Margin="0,0,14,8"/>
|
||||
<CheckBox x:Name="ChkOverlayEnableToolHooks" Content="도구 훅 사용" Margin="0,0,14,8"/>
|
||||
<CheckBox x:Name="ChkOverlayEnableHookInputMutation" Content="입력 변형 반영" Margin="0,0,14,8"/>
|
||||
<CheckBox x:Name="ChkOverlayEnableHookPermissionUpdate" Content="권한 갱신 반영" Margin="0,0,14,8"/>
|
||||
<CheckBox x:Name="ChkOverlayEnableCoworkVerification" Content="Cowork 검증" Margin="0,0,14,8"/>
|
||||
<CheckBox x:Name="ChkOverlayEnableCodeVerification" Content="Code 검증" Margin="0,0,14,8"/>
|
||||
<CheckBox x:Name="ChkOverlayEnableParallelTools" Content="병렬 도구 실행" Margin="0,0,14,8"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Background="{DynamicResource LauncherBackground}"
|
||||
Margin="0,12,0,0"
|
||||
Padding="0">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Content="저장"
|
||||
Background="{DynamicResource AccentColor}"
|
||||
Foreground="White"
|
||||
BorderThickness="0"
|
||||
Padding="12,6"
|
||||
Cursor="Hand"
|
||||
Click="BtnOverlaySave_Click"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- ══ 미리보기 스플리터 (Column 3) ══ -->
|
||||
<GridSplitter x:Name="PreviewSplitter" Grid.Column="3" Width="5"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace AxCopilot.Views;
|
||||
/// <summary>AX Agent 창. 데스크톱 코파일럿 스타일 — 사이드바 + 카테고리 분류 + 타임라인.</summary>
|
||||
public partial class ChatWindow : Window
|
||||
{
|
||||
private const string UnifiedAdminPassword = "axgo123!";
|
||||
private readonly SettingsService _settings;
|
||||
private readonly ChatStorageService _storage;
|
||||
private readonly DraftQueueProcessorService _draftQueueProcessor = new();
|
||||
@@ -76,7 +77,7 @@ public partial class ChatWindow : Window
|
||||
private readonly DispatcherTimer _typingTimer;
|
||||
private int _displayedLength; // 현재 화면에 표시된 글자 수
|
||||
private ResourceDictionary? _agentThemeDictionary;
|
||||
private AgentSettingsWindow? _agentSettingsWindow;
|
||||
private bool _isOverlaySettingsSyncing;
|
||||
|
||||
private sealed class ConversationMeta
|
||||
{
|
||||
@@ -984,6 +985,15 @@ public partial class ChatWindow : Window
|
||||
ApplyAgentThemeResources();
|
||||
ApplyExpressionLevelUi();
|
||||
ApplySidebarStateForActiveTab(animated: false);
|
||||
if (CurrentTabTitle != null)
|
||||
{
|
||||
CurrentTabTitle.Text = _activeTab switch
|
||||
{
|
||||
"Cowork" => "AX Agent · Cowork",
|
||||
"Code" => "AX Agent · Code",
|
||||
_ => "AX Agent · Chat",
|
||||
};
|
||||
}
|
||||
|
||||
// 폴더 바는 Cowork/Code 탭에서만 표시
|
||||
if (FolderBar != null)
|
||||
@@ -1892,9 +1902,9 @@ public partial class ChatWindow : Window
|
||||
return button;
|
||||
}
|
||||
|
||||
actionRow.Children.Add(CreateActionButton("활용하지 않음", "#FEF2F2", "#991B1B", () => SetToolPermissionOverride(latestDenied.ToolName!, PermissionModeCatalog.Deny)));
|
||||
actionRow.Children.Add(CreateActionButton("소극 활용", "#EEF2FF", "#1D4ED8", () => SetToolPermissionOverride(latestDenied.ToolName!, PermissionModeCatalog.Default)));
|
||||
actionRow.Children.Add(CreateActionButton("적극 활용", "#ECFDF5", "#166534", () => SetToolPermissionOverride(latestDenied.ToolName!, PermissionModeCatalog.AcceptEdits)));
|
||||
actionRow.Children.Add(CreateActionButton("읽기 전용", "#FEF2F2", "#991B1B", () => SetToolPermissionOverride(latestDenied.ToolName!, PermissionModeCatalog.Deny)));
|
||||
actionRow.Children.Add(CreateActionButton("권한 요청", "#EEF2FF", "#1D4ED8", () => SetToolPermissionOverride(latestDenied.ToolName!, PermissionModeCatalog.Default)));
|
||||
actionRow.Children.Add(CreateActionButton("편집 자동 승인", "#ECFDF5", "#166534", () => SetToolPermissionOverride(latestDenied.ToolName!, PermissionModeCatalog.AcceptEdits)));
|
||||
actionRow.Children.Add(CreateActionButton("예외 해제", "#F3F4F6", "#374151", () => SetToolPermissionOverride(latestDenied.ToolName!, null)));
|
||||
deniedStack.Children.Add(actionRow);
|
||||
}
|
||||
@@ -2117,7 +2127,6 @@ public partial class ChatWindow : Window
|
||||
BtnPermission_Click(this, new RoutedEventArgs());
|
||||
}
|
||||
|
||||
private bool _permissionTopBannerDismissed; // 상단 권한 배너 닫힘 상태
|
||||
private string _lastPermissionBannerMode = "";
|
||||
|
||||
private bool GetPermissionPopupSectionExpanded(string sectionKey, bool defaultValue = false)
|
||||
@@ -2137,7 +2146,6 @@ public partial class ChatWindow : Window
|
||||
|
||||
private void BtnPermissionTopBannerClose_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_permissionTopBannerDismissed = true;
|
||||
if (PermissionTopBanner != null)
|
||||
PermissionTopBanner.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
@@ -2167,7 +2175,6 @@ public partial class ChatWindow : Window
|
||||
|
||||
if (!string.Equals(_lastPermissionBannerMode, perm, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_permissionTopBannerDismissed = false;
|
||||
_lastPermissionBannerMode = perm;
|
||||
}
|
||||
|
||||
@@ -2182,10 +2189,10 @@ public partial class ChatWindow : Window
|
||||
PermissionTopBanner.BorderBrush = BrushFromHex("#86EFAC");
|
||||
PermissionTopBannerIcon.Text = "\uE73E";
|
||||
PermissionTopBannerIcon.Foreground = activeColor;
|
||||
PermissionTopBannerTitle.Text = "현재 권한 모드 · 적극 활용";
|
||||
PermissionTopBannerTitle.Text = "현재 권한 모드 · 편집 자동 승인";
|
||||
PermissionTopBannerTitle.Foreground = BrushFromHex("#166534");
|
||||
PermissionTopBannerText.Text = "파일 편집 도구는 자동 승인하고, 명령 실행은 계속 확인합니다.";
|
||||
PermissionTopBanner.Visibility = _permissionTopBannerDismissed ? Visibility.Collapsed : Visibility.Visible;
|
||||
PermissionTopBannerText.Text = "모든 파일 편집을 자동 승인합니다. 명령 실행은 계속 확인합니다.";
|
||||
PermissionTopBanner.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
else if (perm == PermissionModeCatalog.Deny)
|
||||
@@ -2198,10 +2205,10 @@ public partial class ChatWindow : Window
|
||||
PermissionTopBanner.BorderBrush = BrushFromHex("#86EFAC");
|
||||
PermissionTopBannerIcon.Text = "\uE73E";
|
||||
PermissionTopBannerIcon.Foreground = denyColor;
|
||||
PermissionTopBannerTitle.Text = "현재 권한 모드 · 활용하지 않음";
|
||||
PermissionTopBannerTitle.Text = "현재 권한 모드 · 읽기 전용";
|
||||
PermissionTopBannerTitle.Foreground = denyColor;
|
||||
PermissionTopBannerText.Text = "파일 읽기만 허용하고 생성/수정/삭제는 차단합니다.";
|
||||
PermissionTopBanner.Visibility = _permissionTopBannerDismissed ? Visibility.Collapsed : Visibility.Visible;
|
||||
PermissionTopBanner.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
else if (perm == PermissionModeCatalog.BypassPermissions)
|
||||
@@ -2214,10 +2221,10 @@ public partial class ChatWindow : Window
|
||||
PermissionTopBanner.BorderBrush = BrushFromHex("#FDBA74");
|
||||
PermissionTopBannerIcon.Text = "\uE814";
|
||||
PermissionTopBannerIcon.Foreground = autoColor;
|
||||
PermissionTopBannerTitle.Text = "현재 권한 모드 · 완전 자동";
|
||||
PermissionTopBannerTitle.Text = "현재 권한 모드 · 권한 건너뛰기";
|
||||
PermissionTopBannerTitle.Foreground = autoColor;
|
||||
PermissionTopBannerText.Text = "권한 확인을 대부분 생략합니다. 민감한 작업 전에는 설정을 다시 확인하세요.";
|
||||
PermissionTopBanner.Visibility = _permissionTopBannerDismissed ? Visibility.Collapsed : Visibility.Visible;
|
||||
PermissionTopBannerText.Text = "모든 권한을 허용합니다. 민감한 작업 전에는 설정을 다시 확인하세요.";
|
||||
PermissionTopBanner.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
else if (perm == PermissionModeCatalog.DontAsk)
|
||||
@@ -2233,7 +2240,7 @@ public partial class ChatWindow : Window
|
||||
PermissionTopBannerTitle.Text = "현재 권한 모드 · 질문 없이 진행";
|
||||
PermissionTopBannerTitle.Foreground = dangerColor;
|
||||
PermissionTopBannerText.Text = "권한 확인을 거의 생략합니다. 민감한 작업 전에는 설정을 다시 확인하세요.";
|
||||
PermissionTopBanner.Visibility = _permissionTopBannerDismissed ? Visibility.Collapsed : Visibility.Visible;
|
||||
PermissionTopBanner.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2253,20 +2260,20 @@ public partial class ChatWindow : Window
|
||||
PermissionTopBanner.BorderBrush = BrushFromHex("#C7D2FE");
|
||||
PermissionTopBannerIcon.Text = "\uE7C3";
|
||||
PermissionTopBannerIcon.Foreground = BrushFromHex("#4338CA");
|
||||
PermissionTopBannerTitle.Text = "현재 권한 모드 · 계획 중심";
|
||||
PermissionTopBannerTitle.Text = "현재 권한 모드 · 계획 모드";
|
||||
PermissionTopBannerTitle.Foreground = BrushFromHex("#4338CA");
|
||||
PermissionTopBannerText.Text = "쓰기 작업은 제한하고, 먼저 계획과 승인 흐름을 우선합니다.";
|
||||
PermissionTopBanner.Visibility = _permissionTopBannerDismissed ? Visibility.Collapsed : Visibility.Visible;
|
||||
PermissionTopBannerText.Text = "변경 전에 계획을 먼저 만들고 승인 흐름을 우선합니다.";
|
||||
PermissionTopBanner.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
else if (perm == PermissionModeCatalog.Default)
|
||||
{
|
||||
PermissionTopBanner.BorderBrush = BrushFromHex("#BFDBFE");
|
||||
PermissionTopBannerIcon.Text = "\uE8D7";
|
||||
PermissionTopBannerIcon.Foreground = BrushFromHex("#1D4ED8");
|
||||
PermissionTopBannerTitle.Text = "현재 권한 모드 · 소극 활용";
|
||||
PermissionTopBannerTitle.Text = "현재 권한 모드 · 권한 요청";
|
||||
PermissionTopBannerTitle.Foreground = BrushFromHex("#1D4ED8");
|
||||
PermissionTopBannerText.Text = "변경 전 확인하고, 필요한 경우에만 파일 접근을 진행합니다.";
|
||||
PermissionTopBanner.Visibility = _permissionTopBannerDismissed ? Visibility.Collapsed : Visibility.Visible;
|
||||
PermissionTopBannerText.Text = "변경하기 전에 항상 확인합니다.";
|
||||
PermissionTopBanner.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -5667,8 +5674,20 @@ public partial class ChatWindow : Window
|
||||
if (!_slashVisibleItemByAbsoluteIndex.TryGetValue(_slashPalette.SelectedIndex, out var item))
|
||||
return;
|
||||
|
||||
var bounds = item.TransformToAncestor(SlashScrollViewer)
|
||||
.TransformBounds(new Rect(0, 0, item.ActualWidth, item.ActualHeight));
|
||||
if (!IsVisualDescendantOf(item, SlashScrollViewer))
|
||||
return;
|
||||
|
||||
Rect bounds;
|
||||
try
|
||||
{
|
||||
bounds = item.TransformToAncestor(SlashScrollViewer)
|
||||
.TransformBounds(new Rect(0, 0, item.ActualWidth, item.ActualHeight));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 렌더 트리 갱신 중에는 transform이 실패할 수 있어 조용히 무시.
|
||||
return;
|
||||
}
|
||||
|
||||
if (bounds.Top < 0)
|
||||
SlashScrollViewer.ScrollToVerticalOffset(SlashScrollViewer.VerticalOffset + bounds.Top - 8);
|
||||
@@ -5676,6 +5695,22 @@ public partial class ChatWindow : Window
|
||||
SlashScrollViewer.ScrollToVerticalOffset(SlashScrollViewer.VerticalOffset + (bounds.Bottom - SlashScrollViewer.ViewportHeight) + 8);
|
||||
}
|
||||
|
||||
private static bool IsVisualDescendantOf(DependencyObject? child, DependencyObject? parent)
|
||||
{
|
||||
if (child == null || parent == null)
|
||||
return false;
|
||||
|
||||
var current = child;
|
||||
while (current != null)
|
||||
{
|
||||
if (ReferenceEquals(current, parent))
|
||||
return true;
|
||||
current = VisualTreeHelper.GetParent(current);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UpdateSlashSelectionVisualState()
|
||||
{
|
||||
if (_slashVisibleItemByAbsoluteIndex.Count == 0)
|
||||
@@ -12997,66 +13032,17 @@ public partial class ChatWindow : Window
|
||||
|
||||
private void OpenAgentSettingsWindow()
|
||||
{
|
||||
if (_agentSettingsWindow != null)
|
||||
RefreshOverlaySettingsPanel();
|
||||
AgentSettingsOverlay.Visibility = Visibility.Visible;
|
||||
InlineSettingsPanel.Visibility = Visibility.Collapsed;
|
||||
SetOverlaySection("common");
|
||||
Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_agentSettingsWindow.IsVisible)
|
||||
{
|
||||
_agentSettingsWindow.Activate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore stale window instance
|
||||
}
|
||||
_agentSettingsWindow = null;
|
||||
}
|
||||
|
||||
var win = new AgentSettingsWindow(_settings);
|
||||
if (IsLoaded && IsVisible)
|
||||
win.Owner = this;
|
||||
_agentSettingsWindow = win;
|
||||
win.Closed += (_, _) => _agentSettingsWindow = null;
|
||||
try
|
||||
{
|
||||
win.Resources.MergedDictionaries.Insert(0, new ResourceDictionary
|
||||
{
|
||||
Source = BuildAgentThemeDictionaryUri(),
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 테마 사전 로드 실패 시에도 설정창 자체는 열리도록 유지
|
||||
}
|
||||
|
||||
bool changed;
|
||||
try
|
||||
{
|
||||
changed = win.ShowDialog() == true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 모달 창 오픈에 실패하면 일반 창으로라도 설정 접근을 보장
|
||||
try
|
||||
{
|
||||
win.Show();
|
||||
win.Activate();
|
||||
}
|
||||
catch { }
|
||||
return;
|
||||
}
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
_appState.LoadFromSettings(_settings);
|
||||
ApplyAgentThemeResources();
|
||||
UpdateSidebarModeMenu();
|
||||
UpdateModelLabel();
|
||||
RefreshInlineSettingsPanel();
|
||||
UpdateTabUI();
|
||||
ShowToast("AX Agent 설정이 저장되었습니다.");
|
||||
if (CmbOverlayModel.Items.Count > 0)
|
||||
CmbOverlayModel.Focus();
|
||||
else
|
||||
InputBox.Focus();
|
||||
}, DispatcherPriority.Input);
|
||||
}
|
||||
|
||||
public void OpenAgentSettingsFromExternal()
|
||||
@@ -13069,6 +13055,515 @@ public partial class ChatWindow : Window
|
||||
}, DispatcherPriority.Input);
|
||||
}
|
||||
|
||||
private void BtnOverlaySettingsClose_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AgentSettingsOverlay.Visibility = Visibility.Collapsed;
|
||||
InputBox.Focus();
|
||||
}
|
||||
|
||||
private void OverlayNav_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not RadioButton rb || rb.Tag is not string tag)
|
||||
return;
|
||||
|
||||
SetOverlaySection(tag);
|
||||
}
|
||||
|
||||
private void SetOverlaySection(string tag)
|
||||
{
|
||||
if (OverlaySectionService == null || OverlaySectionQuick == null || OverlaySectionDetail == null)
|
||||
return;
|
||||
|
||||
var section = string.IsNullOrWhiteSpace(tag) ? "common" : tag.Trim().ToLowerInvariant();
|
||||
|
||||
OverlaySectionService.Visibility = section == "service" ? Visibility.Visible : Visibility.Collapsed;
|
||||
OverlaySectionQuick.Visibility = section == "permission" ? Visibility.Visible : Visibility.Collapsed;
|
||||
OverlaySectionDetail.Visibility = section == "service" ? Visibility.Collapsed : Visibility.Visible;
|
||||
|
||||
var showCommon = section == "common";
|
||||
var showPermission = section == "permission";
|
||||
var showAdvanced = section == "advanced";
|
||||
|
||||
if (OverlayAiEnabledRow != null)
|
||||
OverlayAiEnabledRow.Visibility = showCommon ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (OverlayThemePanel != null)
|
||||
OverlayThemePanel.Visibility = showCommon ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (OverlayModelEditorPanel != null)
|
||||
OverlayModelEditorPanel.Visibility = showCommon ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (OverlayAnchorPermission != null)
|
||||
OverlayAnchorPermission.Visibility = showPermission ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (OverlayFolderDataUsageRow != null)
|
||||
OverlayFolderDataUsageRow.Visibility = showCommon || showPermission ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (OverlayTlsRow != null)
|
||||
OverlayTlsRow.Visibility = showCommon || showPermission ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (OverlayAnchorAdvanced != null)
|
||||
OverlayAnchorAdvanced.Visibility = showAdvanced ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (OverlayMaxContextTokensRow != null)
|
||||
OverlayMaxContextTokensRow.Visibility = showAdvanced ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (OverlayMaxRetryRow != null)
|
||||
OverlayMaxRetryRow.Visibility = showAdvanced ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (OverlayAdvancedTogglePanel != null)
|
||||
OverlayAdvancedTogglePanel.Visibility = showAdvanced ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void RefreshOverlaySettingsPanel()
|
||||
{
|
||||
if (CmbOverlayService == null || CmbOverlayModel == null)
|
||||
return;
|
||||
|
||||
var llm = _settings.Settings.Llm;
|
||||
var service = (llm.Service ?? "ollama").ToLowerInvariant();
|
||||
var models = GetModelCandidates(service);
|
||||
|
||||
_isOverlaySettingsSyncing = true;
|
||||
try
|
||||
{
|
||||
CmbOverlayService.Items.Clear();
|
||||
foreach (var svc in new[] { "ollama", "vllm", "gemini", "claude" })
|
||||
{
|
||||
CmbOverlayService.Items.Add(new ComboBoxItem
|
||||
{
|
||||
Content = ServiceLabel(svc),
|
||||
Tag = svc
|
||||
});
|
||||
}
|
||||
CmbOverlayService.SelectedItem = CmbOverlayService.Items
|
||||
.OfType<ComboBoxItem>()
|
||||
.FirstOrDefault(i => string.Equals(i.Tag as string, service, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
CmbOverlayModel.Items.Clear();
|
||||
foreach (var model in models)
|
||||
{
|
||||
CmbOverlayModel.Items.Add(new ComboBoxItem
|
||||
{
|
||||
Content = model.Label,
|
||||
Tag = model.Id
|
||||
});
|
||||
}
|
||||
CmbOverlayModel.SelectedItem = CmbOverlayModel.Items
|
||||
.OfType<ComboBoxItem>()
|
||||
.FirstOrDefault(i => string.Equals(i.Tag as string, llm.Model, StringComparison.OrdinalIgnoreCase));
|
||||
if (CmbOverlayModel.SelectedItem == null && CmbOverlayModel.Items.Count > 0)
|
||||
CmbOverlayModel.SelectedIndex = 0;
|
||||
|
||||
if (TxtOverlayModelInput != null)
|
||||
TxtOverlayModelInput.Text = llm.Model ?? "";
|
||||
if (ChkOverlayAiEnabled != null)
|
||||
ChkOverlayAiEnabled.IsChecked = _settings.Settings.AiEnabled;
|
||||
if (TxtOverlayServiceEndpoint != null)
|
||||
TxtOverlayServiceEndpoint.Text = GetOverlayServiceEndpoint(service);
|
||||
if (TxtOverlayServiceApiKey != null)
|
||||
TxtOverlayServiceApiKey.Password = GetOverlayServiceApiKey(service);
|
||||
if (ChkOverlayVllmAllowInsecureTls != null)
|
||||
ChkOverlayVllmAllowInsecureTls.IsChecked = llm.VllmAllowInsecureTls;
|
||||
if (TxtOverlayContextCompactTriggerPercent != null)
|
||||
TxtOverlayContextCompactTriggerPercent.Text = Math.Clamp(llm.ContextCompactTriggerPercent, 10, 95).ToString();
|
||||
if (TxtOverlayMaxContextTokens != null)
|
||||
TxtOverlayMaxContextTokens.Text = Math.Max(1024, llm.MaxContextTokens).ToString();
|
||||
if (TxtOverlayMaxRetryOnError != null)
|
||||
TxtOverlayMaxRetryOnError.Text = Math.Clamp(llm.MaxRetryOnError, 0, 10).ToString();
|
||||
if (ChkOverlayEnableProactiveCompact != null)
|
||||
ChkOverlayEnableProactiveCompact.IsChecked = llm.EnableProactiveContextCompact;
|
||||
if (ChkOverlayEnableSkillSystem != null)
|
||||
ChkOverlayEnableSkillSystem.IsChecked = llm.EnableSkillSystem;
|
||||
if (ChkOverlayEnableToolHooks != null)
|
||||
ChkOverlayEnableToolHooks.IsChecked = llm.EnableToolHooks;
|
||||
if (ChkOverlayEnableHookInputMutation != null)
|
||||
ChkOverlayEnableHookInputMutation.IsChecked = llm.EnableHookInputMutation;
|
||||
if (ChkOverlayEnableHookPermissionUpdate != null)
|
||||
ChkOverlayEnableHookPermissionUpdate.IsChecked = llm.EnableHookPermissionUpdate;
|
||||
if (ChkOverlayEnableCoworkVerification != null)
|
||||
ChkOverlayEnableCoworkVerification.IsChecked = llm.EnableCoworkVerification;
|
||||
if (ChkOverlayEnableCodeVerification != null)
|
||||
ChkOverlayEnableCodeVerification.IsChecked = llm.Code.EnableCodeVerification;
|
||||
if (ChkOverlayEnableParallelTools != null)
|
||||
ChkOverlayEnableParallelTools.IsChecked = llm.EnableParallelTools;
|
||||
|
||||
RefreshOverlayThemeCards();
|
||||
RefreshOverlayServiceCards();
|
||||
RefreshOverlayModeButtons();
|
||||
RefreshOverlayServiceFieldLabels(service);
|
||||
BuildOverlayModelChips(service);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isOverlaySettingsSyncing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void CmbOverlayService_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (_isOverlaySettingsSyncing || CmbOverlayService.SelectedItem is not ComboBoxItem serviceItem || serviceItem.Tag is not string service)
|
||||
return;
|
||||
|
||||
var llm = _settings.Settings.Llm;
|
||||
llm.Service = service;
|
||||
var candidates = GetModelCandidates(service);
|
||||
if (candidates.Count > 0 && !candidates.Any(m => m.Id == llm.Model))
|
||||
llm.Model = candidates[0].Id;
|
||||
|
||||
_settings.Save();
|
||||
_appState.LoadFromSettings(_settings);
|
||||
UpdateModelLabel();
|
||||
RefreshInlineSettingsPanel();
|
||||
RefreshOverlaySettingsPanel();
|
||||
}
|
||||
|
||||
private void CmbOverlayModel_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (_isOverlaySettingsSyncing || CmbOverlayModel.SelectedItem is not ComboBoxItem modelItem || modelItem.Tag is not string modelId)
|
||||
return;
|
||||
|
||||
_settings.Settings.Llm.Model = modelId;
|
||||
_settings.Save();
|
||||
_appState.LoadFromSettings(_settings);
|
||||
UpdateModelLabel();
|
||||
RefreshInlineSettingsPanel();
|
||||
}
|
||||
|
||||
private void RefreshOverlayThemeCards()
|
||||
{
|
||||
var selected = (_settings.Settings.Llm.AgentTheme ?? "system").ToLowerInvariant();
|
||||
SetOverlayCardSelection(OverlayThemeSystemCard, selected == "system");
|
||||
SetOverlayCardSelection(OverlayThemeLightCard, selected == "light");
|
||||
SetOverlayCardSelection(OverlayThemeDarkCard, selected == "dark");
|
||||
}
|
||||
|
||||
private void RefreshOverlayServiceCards()
|
||||
{
|
||||
var service = (_settings.Settings.Llm.Service ?? "ollama").ToLowerInvariant();
|
||||
SetOverlayCardSelection(OverlaySvcOllamaCard, service == "ollama");
|
||||
SetOverlayCardSelection(OverlaySvcVllmCard, service == "vllm");
|
||||
SetOverlayCardSelection(OverlaySvcGeminiCard, service == "gemini");
|
||||
SetOverlayCardSelection(OverlaySvcClaudeCard, service is "claude" or "sigmoid");
|
||||
}
|
||||
|
||||
private void RefreshOverlayModeButtons()
|
||||
{
|
||||
var llm = _settings.Settings.Llm;
|
||||
BtnOverlayOperationMode.Content = OperationModePolicy.Normalize(_settings.Settings.OperationMode) == OperationModePolicy.ExternalMode
|
||||
? "사외 모드"
|
||||
: "사내 모드";
|
||||
BtnOverlayFolderDataUsage.Content = _folderDataUsage switch
|
||||
{
|
||||
"active" => "적극 활용",
|
||||
"passive" => "소극 활용",
|
||||
_ => "활용하지 않음",
|
||||
};
|
||||
BtnOverlayPermission.Content = PermissionModeCatalog.ToDisplayLabel(PermissionModeCatalog.NormalizeGlobalMode(llm.FilePermission));
|
||||
BtnOverlayPlanMode.Content = PlanModeLabel(llm.PlanMode);
|
||||
BtnOverlayReasoning.Content = ReasoningLabel(llm.AgentDecisionLevel);
|
||||
}
|
||||
|
||||
private void RefreshOverlayServiceFieldLabels(string service)
|
||||
{
|
||||
if (OverlayEndpointLabel == null || OverlayEndpointHint == null || OverlayApiKeyLabel == null || OverlayApiKeyHint == null)
|
||||
return;
|
||||
|
||||
switch (service)
|
||||
{
|
||||
case "ollama":
|
||||
OverlayEndpointLabel.Text = "Ollama 서버 주소";
|
||||
OverlayEndpointHint.Text = "사내 로컬 Ollama 기본 주소를 입력합니다.";
|
||||
OverlayApiKeyLabel.Text = "Ollama API 키";
|
||||
OverlayApiKeyHint.Text = "사내 게이트웨이를 쓰는 경우에만 입력합니다.";
|
||||
break;
|
||||
case "vllm":
|
||||
OverlayEndpointLabel.Text = "vLLM 서버 주소";
|
||||
OverlayEndpointHint.Text = "OpenAI 호환 엔드포인트 주소를 입력합니다.";
|
||||
OverlayApiKeyLabel.Text = "vLLM API 키";
|
||||
OverlayApiKeyHint.Text = "사내 인증 게이트웨이를 쓰는 경우에만 입력합니다.";
|
||||
break;
|
||||
case "gemini":
|
||||
OverlayEndpointLabel.Text = "기본 서버 주소";
|
||||
OverlayEndpointHint.Text = "Gemini는 기본 주소를 사용합니다. 비워두면 기본값을 사용합니다.";
|
||||
OverlayApiKeyLabel.Text = "Gemini API 키";
|
||||
OverlayApiKeyHint.Text = "외부 호출에 필요한 키를 입력합니다.";
|
||||
break;
|
||||
default:
|
||||
OverlayEndpointLabel.Text = "기본 서버 주소";
|
||||
OverlayEndpointHint.Text = "Claude는 기본 주소를 사용합니다. 비워두면 기본값을 사용합니다.";
|
||||
OverlayApiKeyLabel.Text = "Claude API 키";
|
||||
OverlayApiKeyHint.Text = "외부 호출에 필요한 키를 입력합니다.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetOverlayServiceEndpoint(string service)
|
||||
{
|
||||
var llm = _settings.Settings.Llm;
|
||||
return service switch
|
||||
{
|
||||
"ollama" => llm.OllamaEndpoint ?? "",
|
||||
"vllm" => llm.VllmEndpoint ?? "",
|
||||
"gemini" => llm.Endpoint ?? "",
|
||||
"claude" or "sigmoid" => llm.Endpoint ?? "",
|
||||
_ => llm.Endpoint ?? ""
|
||||
};
|
||||
}
|
||||
|
||||
private string GetOverlayServiceApiKey(string service)
|
||||
{
|
||||
var llm = _settings.Settings.Llm;
|
||||
return service switch
|
||||
{
|
||||
"ollama" => llm.OllamaApiKey ?? "",
|
||||
"vllm" => llm.VllmApiKey ?? "",
|
||||
"gemini" => llm.GeminiApiKey ?? "",
|
||||
"claude" or "sigmoid" => llm.ClaudeApiKey ?? "",
|
||||
_ => llm.ApiKey ?? ""
|
||||
};
|
||||
}
|
||||
|
||||
private void BuildOverlayModelChips(string service)
|
||||
{
|
||||
if (OverlayModelChipPanel == null)
|
||||
return;
|
||||
|
||||
OverlayModelChipPanel.Children.Clear();
|
||||
foreach (var model in GetModelCandidates(service))
|
||||
{
|
||||
var captured = model.Id;
|
||||
var border = new Border
|
||||
{
|
||||
Cursor = Cursors.Hand,
|
||||
CornerRadius = new CornerRadius(10),
|
||||
BorderThickness = new Thickness(1),
|
||||
BorderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray,
|
||||
Background = Brushes.Transparent,
|
||||
Padding = new Thickness(10, 6, 10, 6),
|
||||
Margin = new Thickness(0, 0, 8, 8),
|
||||
Child = new TextBlock
|
||||
{
|
||||
Text = model.Label,
|
||||
FontSize = 11,
|
||||
Foreground = TryFindResource("PrimaryText") as Brush ?? Brushes.Black,
|
||||
}
|
||||
};
|
||||
border.MouseLeftButtonUp += (_, _) =>
|
||||
{
|
||||
if (TxtOverlayModelInput != null)
|
||||
TxtOverlayModelInput.Text = captured;
|
||||
};
|
||||
OverlayModelChipPanel.Children.Add(border);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetOverlayCardSelection(Border border, bool selected)
|
||||
{
|
||||
var accent = TryFindResource("AccentColor") as Brush ?? Brushes.DodgerBlue;
|
||||
var normal = TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
||||
border.BorderBrush = selected ? accent : normal;
|
||||
border.Background = selected
|
||||
? (TryFindResource("HintBackground") as Brush ?? Brushes.Transparent)
|
||||
: Brushes.Transparent;
|
||||
}
|
||||
|
||||
private void OverlayThemeSystemCard_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
_settings.Settings.Llm.AgentTheme = "system";
|
||||
RefreshOverlayThemeCards();
|
||||
}
|
||||
|
||||
private void OverlayThemeLightCard_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
_settings.Settings.Llm.AgentTheme = "light";
|
||||
RefreshOverlayThemeCards();
|
||||
}
|
||||
|
||||
private void OverlayThemeDarkCard_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
_settings.Settings.Llm.AgentTheme = "dark";
|
||||
RefreshOverlayThemeCards();
|
||||
}
|
||||
|
||||
private void OverlaySvcOllamaCard_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) => SetOverlayService("ollama");
|
||||
private void OverlaySvcVllmCard_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) => SetOverlayService("vllm");
|
||||
private void OverlaySvcGeminiCard_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) => SetOverlayService("gemini");
|
||||
private void OverlaySvcClaudeCard_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) => SetOverlayService("claude");
|
||||
|
||||
private void SetOverlayService(string service)
|
||||
{
|
||||
_settings.Settings.Llm.Service = service;
|
||||
RefreshOverlaySettingsPanel();
|
||||
}
|
||||
|
||||
private void BtnOverlayOperationMode_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var next = OperationModePolicy.Normalize(_settings.Settings.OperationMode) == OperationModePolicy.ExternalMode
|
||||
? OperationModePolicy.InternalMode
|
||||
: OperationModePolicy.ExternalMode;
|
||||
|
||||
if (!PromptOverlayPasswordDialog("운영 모드 변경", "사내/사외 모드 변경", "비밀번호를 입력하세요."))
|
||||
return;
|
||||
|
||||
_settings.Settings.OperationMode = next;
|
||||
RefreshOverlayModeButtons();
|
||||
}
|
||||
|
||||
private void BtnOverlayFolderDataUsage_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_folderDataUsage = _folderDataUsage switch
|
||||
{
|
||||
"none" => "passive",
|
||||
"passive" => "active",
|
||||
_ => "none",
|
||||
};
|
||||
RefreshOverlayModeButtons();
|
||||
}
|
||||
|
||||
private void BtnOverlaySave_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var llm = _settings.Settings.Llm;
|
||||
var service = (llm.Service ?? "ollama").Trim().ToLowerInvariant();
|
||||
var endpoint = TxtOverlayServiceEndpoint?.Text.Trim() ?? "";
|
||||
var apiKey = TxtOverlayServiceApiKey?.Password ?? "";
|
||||
|
||||
_settings.Settings.AiEnabled = ChkOverlayAiEnabled?.IsChecked == true;
|
||||
llm.Model = TxtOverlayModelInput?.Text.Trim() ?? llm.Model;
|
||||
llm.VllmAllowInsecureTls = ChkOverlayVllmAllowInsecureTls?.IsChecked == true;
|
||||
llm.EnableProactiveContextCompact = ChkOverlayEnableProactiveCompact?.IsChecked == true;
|
||||
llm.ContextCompactTriggerPercent = ParseOverlayInt(TxtOverlayContextCompactTriggerPercent?.Text, 80, 10, 95);
|
||||
llm.MaxContextTokens = ParseOverlayInt(TxtOverlayMaxContextTokens?.Text, 4096, 1024, 200000);
|
||||
llm.MaxRetryOnError = ParseOverlayInt(TxtOverlayMaxRetryOnError?.Text, 3, 0, 10);
|
||||
llm.EnableSkillSystem = ChkOverlayEnableSkillSystem?.IsChecked == true;
|
||||
llm.EnableToolHooks = ChkOverlayEnableToolHooks?.IsChecked == true;
|
||||
llm.EnableHookInputMutation = ChkOverlayEnableHookInputMutation?.IsChecked == true;
|
||||
llm.EnableHookPermissionUpdate = ChkOverlayEnableHookPermissionUpdate?.IsChecked == true;
|
||||
llm.EnableCoworkVerification = ChkOverlayEnableCoworkVerification?.IsChecked == true;
|
||||
llm.Code.EnableCodeVerification = ChkOverlayEnableCodeVerification?.IsChecked == true;
|
||||
llm.EnableParallelTools = ChkOverlayEnableParallelTools?.IsChecked == true;
|
||||
llm.FolderDataUsage = _folderDataUsage;
|
||||
llm.AgentUiExpressionLevel = "rich";
|
||||
|
||||
switch (service)
|
||||
{
|
||||
case "ollama":
|
||||
llm.OllamaEndpoint = endpoint;
|
||||
llm.OllamaApiKey = apiKey;
|
||||
llm.Endpoint = endpoint;
|
||||
llm.ApiKey = apiKey;
|
||||
llm.OllamaModel = llm.Model;
|
||||
break;
|
||||
case "vllm":
|
||||
llm.VllmEndpoint = endpoint;
|
||||
llm.VllmApiKey = apiKey;
|
||||
llm.Endpoint = endpoint;
|
||||
llm.ApiKey = apiKey;
|
||||
llm.VllmModel = llm.Model;
|
||||
break;
|
||||
case "gemini":
|
||||
llm.Endpoint = endpoint;
|
||||
llm.GeminiApiKey = apiKey;
|
||||
llm.ApiKey = apiKey;
|
||||
llm.GeminiModel = llm.Model;
|
||||
break;
|
||||
case "claude":
|
||||
case "sigmoid":
|
||||
llm.Endpoint = endpoint;
|
||||
llm.ClaudeApiKey = apiKey;
|
||||
llm.ApiKey = apiKey;
|
||||
llm.ClaudeModel = llm.Model;
|
||||
break;
|
||||
}
|
||||
|
||||
_settings.Save();
|
||||
_appState.LoadFromSettings(_settings);
|
||||
ApplyAgentThemeResources();
|
||||
UpdatePermissionUI();
|
||||
UpdateDataUsageUI();
|
||||
SaveConversationSettings();
|
||||
RefreshInlineSettingsPanel();
|
||||
UpdateModelLabel();
|
||||
UpdateTabUI();
|
||||
AgentSettingsOverlay.Visibility = Visibility.Collapsed;
|
||||
ShowToast("AX Agent 설정이 저장되었습니다.");
|
||||
}
|
||||
|
||||
private void BtnOpenFullSettings_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (System.Windows.Application.Current is App app)
|
||||
app.OpenSettingsFromChat();
|
||||
}
|
||||
|
||||
private bool PromptOverlayPasswordDialog(string title, string header, string message)
|
||||
{
|
||||
var bgBrush = TryFindResource("LauncherBackground") as Brush ?? Brushes.White;
|
||||
var fgBrush = TryFindResource("PrimaryText") as Brush ?? Brushes.Black;
|
||||
var subFgBrush = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
||||
var borderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
||||
var itemBg = TryFindResource("ItemBackground") as Brush ?? Brushes.WhiteSmoke;
|
||||
|
||||
var dlg = new Window
|
||||
{
|
||||
Title = title,
|
||||
Width = 340,
|
||||
SizeToContent = SizeToContent.Height,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
Owner = this,
|
||||
ResizeMode = ResizeMode.NoResize,
|
||||
WindowStyle = WindowStyle.None,
|
||||
AllowsTransparency = true,
|
||||
Background = Brushes.Transparent,
|
||||
ShowInTaskbar = false,
|
||||
};
|
||||
|
||||
var border = new Border
|
||||
{
|
||||
Background = bgBrush,
|
||||
CornerRadius = new CornerRadius(12),
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
Padding = new Thickness(20),
|
||||
};
|
||||
|
||||
var stack = new StackPanel();
|
||||
stack.Children.Add(new TextBlock { Text = header, FontSize = 15, FontWeight = FontWeights.SemiBold, Foreground = fgBrush, Margin = new Thickness(0, 0, 0, 12) });
|
||||
stack.Children.Add(new TextBlock { Text = message, FontSize = 12, Foreground = subFgBrush, Margin = new Thickness(0, 0, 0, 6) });
|
||||
|
||||
var pwBox = new PasswordBox
|
||||
{
|
||||
FontSize = 14,
|
||||
Padding = new Thickness(8, 6, 8, 6),
|
||||
Background = itemBg,
|
||||
Foreground = fgBrush,
|
||||
BorderBrush = borderBrush,
|
||||
PasswordChar = '*',
|
||||
};
|
||||
stack.Children.Add(pwBox);
|
||||
|
||||
var btnRow = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right, Margin = new Thickness(0, 16, 0, 0) };
|
||||
var cancelBtn = new Button { Content = "취소", Padding = new Thickness(16, 6, 16, 6), Margin = new Thickness(0, 0, 8, 0) };
|
||||
cancelBtn.Click += (_, _) => dlg.DialogResult = false;
|
||||
btnRow.Children.Add(cancelBtn);
|
||||
|
||||
var okBtn = new Button { Content = "확인", Padding = new Thickness(16, 6, 16, 6), IsDefault = true };
|
||||
okBtn.Click += (_, _) =>
|
||||
{
|
||||
if (pwBox.Password == UnifiedAdminPassword)
|
||||
dlg.DialogResult = true;
|
||||
else
|
||||
{
|
||||
pwBox.Clear();
|
||||
pwBox.Focus();
|
||||
}
|
||||
};
|
||||
btnRow.Children.Add(okBtn);
|
||||
stack.Children.Add(btnRow);
|
||||
|
||||
border.Child = stack;
|
||||
dlg.Content = border;
|
||||
dlg.Loaded += (_, _) => pwBox.Focus();
|
||||
return dlg.ShowDialog() == true;
|
||||
}
|
||||
|
||||
private static int ParseOverlayInt(string? text, int fallback, int min, int max)
|
||||
{
|
||||
if (!int.TryParse(text, out var value))
|
||||
value = fallback;
|
||||
return Math.Clamp(value, min, max);
|
||||
}
|
||||
|
||||
private void BtnInlineSettingsClose_Click(object sender, RoutedEventArgs e)
|
||||
=> InlineSettingsPanel.Visibility = Visibility.Collapsed;
|
||||
|
||||
@@ -13108,6 +13603,7 @@ public partial class ChatWindow : Window
|
||||
_settings.Save();
|
||||
_appState.LoadFromSettings(_settings);
|
||||
RefreshInlineSettingsPanel();
|
||||
RefreshOverlaySettingsPanel();
|
||||
}
|
||||
|
||||
private void BtnInlineReasoning_Click(object sender, RoutedEventArgs e)
|
||||
@@ -13117,6 +13613,7 @@ public partial class ChatWindow : Window
|
||||
_settings.Save();
|
||||
_appState.LoadFromSettings(_settings);
|
||||
RefreshInlineSettingsPanel();
|
||||
RefreshOverlaySettingsPanel();
|
||||
}
|
||||
|
||||
private void BtnInlinePlanMode_Click(object sender, RoutedEventArgs e)
|
||||
@@ -13126,6 +13623,7 @@ public partial class ChatWindow : Window
|
||||
_settings.Save();
|
||||
_appState.LoadFromSettings(_settings);
|
||||
RefreshInlineSettingsPanel();
|
||||
RefreshOverlaySettingsPanel();
|
||||
}
|
||||
|
||||
private void BtnInlinePermission_Click(object sender, RoutedEventArgs e)
|
||||
@@ -13137,6 +13635,7 @@ public partial class ChatWindow : Window
|
||||
UpdatePermissionUI();
|
||||
SaveConversationSettings();
|
||||
RefreshInlineSettingsPanel();
|
||||
RefreshOverlaySettingsPanel();
|
||||
}
|
||||
|
||||
private void BtnInlineSkill_Click(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -3,9 +3,12 @@ using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using AxCopilot.Services;
|
||||
using FormsCursor = System.Windows.Forms.Cursor;
|
||||
using FormsScreen = System.Windows.Forms.Screen;
|
||||
|
||||
namespace AxCopilot.Views;
|
||||
|
||||
@@ -48,14 +51,19 @@ public partial class DockBarWindow : Window
|
||||
private DispatcherTimer? _glowTimer;
|
||||
|
||||
/// <summary>설정 저장 콜백 (위치 저장용).</summary>
|
||||
public Action<double, double>? OnPositionChanged { get; set; }
|
||||
public Action<string, double, double>? OnPositionChanged { get; set; }
|
||||
|
||||
public DockBarWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
MouseLeftButtonDown += (_, e) => { if (e.LeftButton == MouseButtonState.Pressed) try { DragMove(); } catch { } };
|
||||
LocationChanged += (_, _) => OnPositionChanged?.Invoke(Left, Top);
|
||||
LocationChanged += (_, _) =>
|
||||
{
|
||||
var deviceName = GetCurrentMonitorDeviceName();
|
||||
if (!string.IsNullOrWhiteSpace(deviceName))
|
||||
OnPositionChanged?.Invoke(deviceName, Left, Top);
|
||||
};
|
||||
Loaded += (_, _) => PositionDock();
|
||||
Closed += (_, _) => { _timer?.Stop(); _glowTimer?.Stop(); _cpuCounter?.Dispose(); };
|
||||
}
|
||||
@@ -65,7 +73,7 @@ public partial class DockBarWindow : Window
|
||||
{
|
||||
Opacity = Math.Clamp(opacity, 0.3, 1.0);
|
||||
|
||||
if (left >= 0 && top >= 0)
|
||||
if (left >= 0 && top >= 0 && IsWithinConnectedScreen(left, top))
|
||||
{
|
||||
Left = left;
|
||||
Top = top;
|
||||
@@ -269,11 +277,34 @@ public partial class DockBarWindow : Window
|
||||
|
||||
private void PositionDock()
|
||||
{
|
||||
var screen = SystemParameters.WorkArea;
|
||||
Left = (screen.Width - ActualWidth) / 2 + screen.Left;
|
||||
var screen = FormsScreen.FromPoint(FormsCursor.Position).WorkingArea;
|
||||
Left = screen.Left + (screen.Width - ActualWidth) / 2;
|
||||
Top = screen.Bottom - ActualHeight - 8;
|
||||
}
|
||||
|
||||
private string GetCurrentMonitorDeviceName()
|
||||
{
|
||||
var handle = new WindowInteropHelper(this).Handle;
|
||||
return handle != IntPtr.Zero
|
||||
? FormsScreen.FromHandle(handle).DeviceName
|
||||
: FormsScreen.FromPoint(FormsCursor.Position).DeviceName;
|
||||
}
|
||||
|
||||
private bool IsWithinConnectedScreen(double left, double top)
|
||||
{
|
||||
var width = ActualWidth > 0 ? ActualWidth : (Width > 0 ? Width : 420);
|
||||
var height = ActualHeight > 0 ? ActualHeight : (Height > 0 ? Height : 56);
|
||||
|
||||
foreach (var screen in FormsScreen.AllScreens)
|
||||
{
|
||||
var area = screen.WorkingArea;
|
||||
if (left >= area.Left && top >= area.Top && left + width <= area.Right && top + height <= area.Bottom)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnTick(object? sender, EventArgs e)
|
||||
{
|
||||
if (_clockText != null)
|
||||
|
||||
@@ -13,6 +13,7 @@ internal sealed class ModelRegistrationDialog : Window
|
||||
private readonly TextBox _modelBox;
|
||||
private readonly TextBox _endpointBox;
|
||||
private readonly TextBox _apiKeyBox;
|
||||
private readonly CheckBox _allowInsecureTlsCheck;
|
||||
|
||||
// CP4D 인증 필드
|
||||
private readonly ComboBox _authTypeBox;
|
||||
@@ -25,13 +26,14 @@ internal sealed class ModelRegistrationDialog : Window
|
||||
public string ModelName => _modelBox.Text.Trim();
|
||||
public string Endpoint => _endpointBox.Text.Trim();
|
||||
public string ApiKey => _apiKeyBox.Text.Trim();
|
||||
public bool AllowInsecureTls => _allowInsecureTlsCheck.IsChecked == true;
|
||||
public string AuthType => (_authTypeBox.SelectedItem as ComboBoxItem)?.Tag?.ToString() ?? "bearer";
|
||||
public string Cp4dUrl => _cp4dUrlBox.Text.Trim();
|
||||
public string Cp4dUsername => _cp4dUsernameBox.Text.Trim();
|
||||
public string Cp4dPassword => _cp4dPasswordBox.Password.Trim();
|
||||
|
||||
public ModelRegistrationDialog(string service, string existingAlias = "", string existingModel = "",
|
||||
string existingEndpoint = "", string existingApiKey = "",
|
||||
string existingEndpoint = "", string existingApiKey = "", bool existingAllowInsecureTls = false,
|
||||
string existingAuthType = "bearer", string existingCp4dUrl = "",
|
||||
string existingCp4dUsername = "", string existingCp4dPassword = "")
|
||||
{
|
||||
@@ -211,6 +213,48 @@ internal sealed class ModelRegistrationDialog : Window
|
||||
};
|
||||
stack.Children.Add(new Border { CornerRadius = new CornerRadius(8), ClipToBounds = true, Child = _endpointBox });
|
||||
|
||||
var tlsPanel = new Border
|
||||
{
|
||||
Background = Brushes.Transparent,
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Padding = new Thickness(10, 8, 10, 8),
|
||||
Margin = new Thickness(0, 10, 0, 0),
|
||||
};
|
||||
var tlsGrid = new Grid();
|
||||
tlsGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
tlsGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
||||
var tlsLeft = new StackPanel { VerticalAlignment = VerticalAlignment.Center };
|
||||
tlsLeft.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "SSL 인증서 검증 생략",
|
||||
FontSize = 12,
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
Foreground = primaryText,
|
||||
});
|
||||
tlsLeft.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "사내 자체 인증서 환경에서만 사용하세요.",
|
||||
FontSize = 11,
|
||||
Foreground = secondaryText,
|
||||
Margin = new Thickness(0, 2, 0, 0),
|
||||
});
|
||||
Grid.SetColumn(tlsLeft, 0);
|
||||
tlsGrid.Children.Add(tlsLeft);
|
||||
_allowInsecureTlsCheck = new CheckBox
|
||||
{
|
||||
IsChecked = existingAllowInsecureTls,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
Margin = new Thickness(12, 0, 0, 0),
|
||||
};
|
||||
_allowInsecureTlsCheck.Style = Application.Current.TryFindResource("ToggleSwitch") as Style ?? _allowInsecureTlsCheck.Style;
|
||||
Grid.SetColumn(_allowInsecureTlsCheck, 1);
|
||||
tlsGrid.Children.Add(_allowInsecureTlsCheck);
|
||||
tlsPanel.Child = tlsGrid;
|
||||
stack.Children.Add(tlsPanel);
|
||||
|
||||
// ── 인증 방식 선택 ──────────────────────────────────────────────────
|
||||
stack.Children.Add(new TextBlock
|
||||
{
|
||||
|
||||
@@ -47,9 +47,12 @@ internal sealed class PlanViewerWindow : Window
|
||||
private List<string> _steps = new();
|
||||
private int _currentStep = -1;
|
||||
private bool _isExecuting;
|
||||
private readonly string _uiExpressionLevel;
|
||||
|
||||
public PlanViewerWindow()
|
||||
{
|
||||
_uiExpressionLevel = ResolveUiExpressionLevel();
|
||||
|
||||
Width = 640;
|
||||
Height = 520;
|
||||
MinWidth = 480;
|
||||
@@ -156,6 +159,8 @@ internal sealed class PlanViewerWindow : Window
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
Margin = new Thickness(20, 6, 20, 0),
|
||||
};
|
||||
if (_uiExpressionLevel == "simple")
|
||||
toolBar.Visibility = Visibility.Collapsed;
|
||||
|
||||
var expandAllBtn = MakeToolbarButton("\uE70D", "모두 열기", secondaryText, hoverBgTb);
|
||||
expandAllBtn.MouseLeftButtonUp += (_, _) =>
|
||||
@@ -281,6 +286,11 @@ internal sealed class PlanViewerWindow : Window
|
||||
_currentStep = -1;
|
||||
_isExecuting = false;
|
||||
_expandedSteps.Clear(); // 새 계획 표시 시 모두 접힌 상태로 시작
|
||||
if (_uiExpressionLevel == "rich")
|
||||
{
|
||||
for (int i = 0; i < _steps.Count; i++)
|
||||
_expandedSteps.Add(i);
|
||||
}
|
||||
|
||||
RenderSteps();
|
||||
BuildApprovalButtons();
|
||||
@@ -361,6 +371,56 @@ internal sealed class PlanViewerWindow : Window
|
||||
|
||||
var canEdit = !_isExecuting && _currentStep < 0; // 승인 대기 중에만 편집/순서변경 가능
|
||||
|
||||
var summaryText = _uiExpressionLevel switch
|
||||
{
|
||||
"simple" => _isExecuting
|
||||
? "진행률을 간단히 표시합니다."
|
||||
: $"{_steps.Count}단계 계획입니다. 핵심 단계만 확인하고 승인하세요.",
|
||||
"rich" => _isExecuting
|
||||
? "현재 단계를 기준으로 진행률을 표시합니다. 필요 시 단계를 펼쳐 세부 내용을 확인할 수 있습니다."
|
||||
: $"총 {_steps.Count}단계입니다. 단계별 내용을 열어 우선순위/의존성을 검토한 뒤 승인 또는 수정 요청을 선택하세요.",
|
||||
_ => _isExecuting
|
||||
? "현재 단계를 기준으로 진행률을 표시합니다."
|
||||
: $"총 {_steps.Count}단계입니다. 단계를 펼쳐 검토한 후 승인 또는 수정 요청을 선택하세요.",
|
||||
};
|
||||
|
||||
_stepsPanel.Children.Add(new Border
|
||||
{
|
||||
Background = new SolidColorBrush(Color.FromArgb(0x12,
|
||||
((SolidColorBrush)accentBrush).Color.R,
|
||||
((SolidColorBrush)accentBrush).Color.G,
|
||||
((SolidColorBrush)accentBrush).Color.B)),
|
||||
BorderBrush = new SolidColorBrush(Color.FromArgb(0x40,
|
||||
((SolidColorBrush)accentBrush).Color.R,
|
||||
((SolidColorBrush)accentBrush).Color.G,
|
||||
((SolidColorBrush)accentBrush).Color.B)),
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(10),
|
||||
Padding = new Thickness(10, 8, 10, 8),
|
||||
Margin = new Thickness(0, 0, 0, 8),
|
||||
Child = new StackPanel
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new TextBlock
|
||||
{
|
||||
Text = _isExecuting ? "계획 실행 중" : "계획 승인 대기",
|
||||
FontSize = 12,
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
Foreground = accentBrush,
|
||||
},
|
||||
new TextBlock
|
||||
{
|
||||
Text = summaryText,
|
||||
FontSize = 11.5,
|
||||
Foreground = secondaryText,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Margin = new Thickness(0, 3, 0, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i < _steps.Count; i++)
|
||||
{
|
||||
var step = _steps[i];
|
||||
@@ -768,7 +828,11 @@ internal sealed class PlanViewerWindow : Window
|
||||
var accentBrush = Application.Current.TryFindResource("AccentColor") as Brush
|
||||
?? new SolidColorBrush(Color.FromRgb(0x4B, 0x5E, 0xFC));
|
||||
|
||||
var approveBtn = CreateActionButton("\uE73E", "승인", accentBrush, Brushes.White, true);
|
||||
var approveLabel = _uiExpressionLevel == "simple" ? "승인" : "승인 후 실행";
|
||||
var editLabel = _uiExpressionLevel == "simple" ? "수정" : "수정 피드백";
|
||||
var rejectLabel = _uiExpressionLevel == "simple" ? "취소" : "거부";
|
||||
|
||||
var approveBtn = CreateActionButton("\uE73E", approveLabel, accentBrush, Brushes.White, true);
|
||||
approveBtn.MouseLeftButtonUp += (_, _) =>
|
||||
{
|
||||
_tcs?.TrySetResult(null);
|
||||
@@ -776,19 +840,12 @@ internal sealed class PlanViewerWindow : Window
|
||||
};
|
||||
_btnPanel.Children.Add(approveBtn);
|
||||
|
||||
var editBtn = CreateActionButton("\uE70F", "수정 요청", accentBrush, accentBrush, false);
|
||||
var editBtn = CreateActionButton("\uE70F", editLabel, accentBrush, accentBrush, false);
|
||||
editBtn.MouseLeftButtonUp += (_, _) => ShowEditInput();
|
||||
_btnPanel.Children.Add(editBtn);
|
||||
|
||||
var reconfirmBtn = CreateActionButton("\uE72C", "재확인",
|
||||
Application.Current.TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
|
||||
Application.Current.TryFindResource("PrimaryText") as Brush ?? Brushes.White, false);
|
||||
reconfirmBtn.MouseLeftButtonUp += (_, _) =>
|
||||
_tcs?.TrySetResult("계획을 다시 검토하고 더 구체적으로 수정해주세요.");
|
||||
_btnPanel.Children.Add(reconfirmBtn);
|
||||
|
||||
var cancelBrush = new SolidColorBrush(Color.FromRgb(0xDC, 0x26, 0x26));
|
||||
var cancelBtn = CreateActionButton("\uE711", "취소", cancelBrush, cancelBrush, false);
|
||||
var cancelBtn = CreateActionButton("\uE711", rejectLabel, cancelBrush, cancelBrush, false);
|
||||
cancelBtn.MouseLeftButtonUp += (_, _) => { _tcs?.TrySetResult("취소"); Hide(); };
|
||||
_btnPanel.Children.Add(cancelBtn);
|
||||
}
|
||||
@@ -948,4 +1005,22 @@ internal sealed class PlanViewerWindow : Window
|
||||
btn.MouseLeave += (s, _) => ((Border)s).Opacity = 1.0;
|
||||
return btn;
|
||||
}
|
||||
|
||||
private static string ResolveUiExpressionLevel()
|
||||
{
|
||||
if (Application.Current is App app)
|
||||
return NormalizeUiExpressionLevel(app.SettingsService?.Settings?.Llm.AgentUiExpressionLevel);
|
||||
|
||||
return "balanced";
|
||||
}
|
||||
|
||||
private static string NormalizeUiExpressionLevel(string? value)
|
||||
{
|
||||
return (value ?? "balanced").Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"rich" => "rich",
|
||||
"simple" => "simple",
|
||||
_ => "balanced",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,7 +538,7 @@
|
||||
<!-- ══════════════════════════════════════════════════════════════════ -->
|
||||
<!-- 탭 컨트롤 (좌측 사이드바) -->
|
||||
<!-- ══════════════════════════════════════════════════════════════════ -->
|
||||
<TabControl Grid.Row="0" TabStripPlacement="Left"
|
||||
<TabControl x:Name="MainSettingsTab" Grid.Row="0" TabStripPlacement="Left"
|
||||
Background="Transparent" BorderThickness="0">
|
||||
<TabControl.Template>
|
||||
<ControlTemplate TargetType="TabControl">
|
||||
@@ -574,6 +574,35 @@
|
||||
</Border>
|
||||
<Rectangle DockPanel.Dock="Top" Height="1"
|
||||
Fill="{DynamicResource BorderColor}" Margin="12,0,12,8"/>
|
||||
<Border DockPanel.Dock="Bottom" Margin="12,0,12,8"
|
||||
Visibility="Collapsed"
|
||||
Background="{DynamicResource ItemBackground}"
|
||||
BorderBrush="{DynamicResource BorderColor}" BorderThickness="1"
|
||||
CornerRadius="10" Padding="8,8">
|
||||
<StackPanel>
|
||||
<TextBlock Text="설정 표현"
|
||||
FontSize="11" FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource SecondaryText}"
|
||||
Margin="2,0,0,6"/>
|
||||
<WrapPanel>
|
||||
<RadioButton x:Name="DisplayModeRich"
|
||||
Content="풍부하게"
|
||||
GroupName="DisplayMode"
|
||||
Style="{StaticResource AgentSubTabStyle}"
|
||||
Checked="DisplayMode_Checked"/>
|
||||
<RadioButton x:Name="DisplayModeBalanced"
|
||||
Content="적절하게"
|
||||
GroupName="DisplayMode"
|
||||
Style="{StaticResource AgentSubTabStyle}"
|
||||
Checked="DisplayMode_Checked"/>
|
||||
<RadioButton x:Name="DisplayModeSimple"
|
||||
Content="간단하게"
|
||||
GroupName="DisplayMode"
|
||||
Style="{StaticResource AgentSubTabStyle}"
|
||||
Checked="DisplayMode_Checked"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<!-- 탭 목록 — 창 높이가 충분히 크므로 스크롤 없음 -->
|
||||
<ScrollViewer VerticalScrollBarVisibility="Disabled"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
@@ -621,7 +650,7 @@
|
||||
<StackPanel>
|
||||
|
||||
<TextBlock Text="AI 기능" Style="{StaticResource SectionHeader}"/>
|
||||
<Border Style="{StaticResource SettingsRow}">
|
||||
<Border Style="{StaticResource SettingsRow}" Visibility="Collapsed">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
@@ -3258,7 +3287,7 @@
|
||||
<!-- ═════════════════════════════════════════════════════════════ -->
|
||||
<!-- AX Agent 탭 -->
|
||||
<!-- ═════════════════════════════════════════════════════════════ -->
|
||||
<TabItem x:Name="AgentTabItem" Header="AX Agent" Tag="" Style="{StaticResource SideNavItem}">
|
||||
<TabItem x:Name="AgentTabItem" Header="AX Agent" Tag="" Style="{StaticResource SideNavItem}" Visibility="Collapsed">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
@@ -3540,6 +3569,16 @@
|
||||
Text="{Binding VllmApiKey, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border Style="{StaticResource SettingsRow}">
|
||||
<Grid>
|
||||
<StackPanel HorizontalAlignment="Left" Margin="0,0,60,0">
|
||||
<TextBlock Style="{StaticResource RowLabel}" Text="SSL 인증서 검증 생략"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="사내 자체 인증서 환경에서만 사용하세요. vLLM HTTPS 인증서 오류를 무시합니다."/>
|
||||
</StackPanel>
|
||||
<CheckBox Style="{StaticResource ToggleSwitch}" HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
IsChecked="{Binding VllmAllowInsecureTls, Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Gemini 패널 -->
|
||||
@@ -4019,6 +4058,7 @@
|
||||
<StackPanel HorizontalAlignment="Left">
|
||||
<TextBlock Style="{StaticResource RowLabel}" Text="슬래시 팝업 표시 개수"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="/ 입력 시 한 번에 보여줄 명령어 개수입니다. (최소 3 ~ 최대 20)"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="저장 후 즉시 반영"/>
|
||||
</StackPanel>
|
||||
<StackPanel HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
@@ -4035,6 +4075,50 @@
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Border Style="{StaticResource SettingsRow}">
|
||||
<Grid>
|
||||
<StackPanel HorizontalAlignment="Left">
|
||||
<TextBlock Style="{StaticResource RowLabel}" Text="슬래시 핀 최대 개수"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="핀 고정 가능한 슬래시 명령어 최대 개수입니다. (최소 1 ~ 최대 30)"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="저장 후 즉시 반영"/>
|
||||
</StackPanel>
|
||||
<StackPanel HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding MaxFavoriteSlashCommands}"
|
||||
FontSize="13" FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource AccentColor}"
|
||||
VerticalAlignment="Center"
|
||||
MinWidth="24" TextAlignment="Right"
|
||||
Margin="0,0,8,0"/>
|
||||
<Slider Width="140" Minimum="1" Maximum="30" TickFrequency="1"
|
||||
IsSnapToTickEnabled="True"
|
||||
Value="{Binding MaxFavoriteSlashCommands, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Border Style="{StaticResource SettingsRow}">
|
||||
<Grid>
|
||||
<StackPanel HorizontalAlignment="Left">
|
||||
<TextBlock Style="{StaticResource RowLabel}" Text="슬래시 최근 최대 개수"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="최근 사용(MRU)으로 기억할 슬래시 명령어 최대 개수입니다. (최소 5 ~ 최대 50)"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="저장 후 즉시 반영"/>
|
||||
</StackPanel>
|
||||
<StackPanel HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding MaxRecentSlashCommands}"
|
||||
FontSize="13" FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource AccentColor}"
|
||||
VerticalAlignment="Center"
|
||||
MinWidth="24" TextAlignment="Right"
|
||||
Margin="0,0,8,0"/>
|
||||
<Slider Width="140" Minimum="5" Maximum="50" TickFrequency="1"
|
||||
IsSnapToTickEnabled="True"
|
||||
Value="{Binding MaxRecentSlashCommands, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- ── 드래그 앤 드롭 AI ── -->
|
||||
<TextBlock Style="{StaticResource SectionHeader}" Text="드래그 앤 드롭 AI"/>
|
||||
<Border Style="{StaticResource SettingsRow}">
|
||||
@@ -4373,24 +4457,6 @@
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border Style="{StaticResource SettingsRow}">
|
||||
<Grid>
|
||||
<StackPanel HorizontalAlignment="Left">
|
||||
<TextBlock Style="{StaticResource RowLabel}" Text="오류 시 최대 재시도"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="도구 실행 실패 시 자동으로 재시도할 횟수입니다. (0~10)"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Slider Width="100" Minimum="0" Maximum="10" TickFrequency="1"
|
||||
IsSnapToTickEnabled="True"
|
||||
Value="{Binding MaxRetryOnError, Mode=TwoWay}"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding MaxRetryOnError}"
|
||||
FontSize="13" FontWeight="SemiBold" Foreground="{DynamicResource AccentColor}"
|
||||
Width="24" TextAlignment="Center" VerticalAlignment="Center"
|
||||
Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border Style="{StaticResource SettingsRow}">
|
||||
<Grid>
|
||||
<StackPanel HorizontalAlignment="Left" Margin="0,0,180,0">
|
||||
@@ -4454,6 +4520,32 @@
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border Style="{StaticResource SettingsRow}">
|
||||
<Grid>
|
||||
<StackPanel HorizontalAlignment="Left" Margin="0,0,60,0">
|
||||
<TextBlock Style="{StaticResource RowLabel}" Text="사전 컨텍스트 압축"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="토큰 한도 전에 이전 대화를 요약/정리해 컨텍스트를 안정적으로 유지합니다."/>
|
||||
</StackPanel>
|
||||
<CheckBox Style="{StaticResource ToggleSwitch}" HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
IsChecked="{Binding EnableProactiveContextCompact, Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border Style="{StaticResource SettingsRow}">
|
||||
<Grid>
|
||||
<StackPanel HorizontalAlignment="Left">
|
||||
<TextBlock Style="{StaticResource RowLabel}" Text="압축 시작 임계치"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="최대 컨텍스트 토큰 대비 사용률이 이 값에 도달하면 사전 압축을 수행합니다."/>
|
||||
</StackPanel>
|
||||
<StackPanel HorizontalAlignment="Right" VerticalAlignment="Center" Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding ContextCompactTriggerPercent, StringFormat={}{0}%}"
|
||||
FontSize="13" FontWeight="SemiBold" Foreground="{DynamicResource AccentColor}"
|
||||
VerticalAlignment="Center" MinWidth="42" TextAlignment="Right" Margin="0,0,8,0"/>
|
||||
<Slider Width="140" Minimum="50" Maximum="95" TickFrequency="5"
|
||||
IsSnapToTickEnabled="True"
|
||||
Value="{Binding ContextCompactTriggerPercent, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border Style="{StaticResource SettingsRow}">
|
||||
<Grid>
|
||||
<StackPanel HorizontalAlignment="Left" Margin="0,0,60,0">
|
||||
@@ -4698,9 +4790,9 @@
|
||||
<ComboBox HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Width="180" SelectedValue="{Binding FolderDataUsage, Mode=TwoWay}"
|
||||
SelectedValuePath="Tag">
|
||||
<ComboBoxItem Content="적극 활용 (자동 탐색)" Tag="active"/>
|
||||
<ComboBoxItem Content="소극 활용 (요청 시)" Tag="passive"/>
|
||||
<ComboBoxItem Content="활용하지 않음" Tag="none"/>
|
||||
<ComboBoxItem Content="소극 활용 (요청 시)" Tag="passive"/>
|
||||
<ComboBoxItem Content="적극 활용 (자동 탐색)" Tag="active"/>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
@@ -5211,6 +5303,24 @@
|
||||
HorizontalAlignment="Center"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border Style="{StaticResource SettingsRow}">
|
||||
<Grid>
|
||||
<StackPanel HorizontalAlignment="Left">
|
||||
<TextBlock Style="{StaticResource RowLabel}" Text="오류 재시도 횟수"/>
|
||||
<TextBlock Style="{StaticResource RowHint}" Text="도구 실행 실패 시 자동 복구를 위해 재시도할 최대 횟수입니다. (0~10)"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Slider Width="100" Minimum="0" Maximum="10" TickFrequency="1"
|
||||
IsSnapToTickEnabled="True"
|
||||
Value="{Binding MaxRetryOnError, Mode=TwoWay}"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding MaxRetryOnError}"
|
||||
FontSize="13" FontWeight="SemiBold" Foreground="{DynamicResource AccentColor}"
|
||||
Width="24" TextAlignment="Center" VerticalAlignment="Center"
|
||||
Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- ── 모델 폴백 ── -->
|
||||
<TextBlock Style="{StaticResource SectionHeader}" Text="모델 폴백"/>
|
||||
|
||||
@@ -15,6 +15,7 @@ public partial class SettingsWindow : Window
|
||||
private readonly Action<string> _previewCallback;
|
||||
private readonly Action _revertCallback;
|
||||
private bool _saved;
|
||||
private bool _isDisplayModeSyncing;
|
||||
|
||||
/// <summary>
|
||||
/// 핫키 녹화 시작/종료를 외부(App.xaml.cs)에 알리는 콜백.
|
||||
@@ -81,9 +82,153 @@ public partial class SettingsWindow : Window
|
||||
// AI 기능 토글 초기화
|
||||
ApplyAiEnabledState(app?.SettingsService?.Settings.AiEnabled ?? false, init: true);
|
||||
ApplyOperationModeState(app?.SettingsService?.Settings.OperationMode);
|
||||
InitializeDisplayModeUi();
|
||||
};
|
||||
}
|
||||
|
||||
private void InitializeDisplayModeUi()
|
||||
{
|
||||
var app = System.Windows.Application.Current as App;
|
||||
if (app?.SettingsService?.Settings?.Llm != null)
|
||||
app.SettingsService.Settings.Llm.AgentUiExpressionLevel = "rich";
|
||||
SetDisplayMode("rich", persist: false);
|
||||
}
|
||||
|
||||
private void DisplayMode_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_isDisplayModeSyncing) return;
|
||||
var rb = sender as RadioButton;
|
||||
var next = rb?.Name switch
|
||||
{
|
||||
"DisplayModeRich" => "rich",
|
||||
"DisplayModeSimple" => "simple",
|
||||
_ => "balanced",
|
||||
};
|
||||
SetDisplayMode(next, persist: true);
|
||||
}
|
||||
|
||||
private void SetDisplayMode(string mode, bool persist)
|
||||
{
|
||||
mode = NormalizeDisplayMode(mode);
|
||||
|
||||
_isDisplayModeSyncing = true;
|
||||
try
|
||||
{
|
||||
var rich = GetDisplayModeRadio("DisplayModeRich");
|
||||
var balanced = GetDisplayModeRadio("DisplayModeBalanced");
|
||||
var simple = GetDisplayModeRadio("DisplayModeSimple");
|
||||
if (rich != null) rich.IsChecked = mode == "rich";
|
||||
if (balanced != null) balanced.IsChecked = mode == "balanced";
|
||||
if (simple != null) simple.IsChecked = mode == "simple";
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isDisplayModeSyncing = false;
|
||||
}
|
||||
|
||||
ApplyMainTabVisibility(mode);
|
||||
ApplyAgentSubTabVisibility(mode);
|
||||
|
||||
if (!persist) return;
|
||||
var app = System.Windows.Application.Current as App;
|
||||
if (app?.SettingsService?.Settings?.Llm == null) return;
|
||||
app.SettingsService.Settings.Llm.AgentUiExpressionLevel = mode;
|
||||
app.SettingsService.Save();
|
||||
}
|
||||
|
||||
private void ApplyMainTabVisibility(string mode)
|
||||
{
|
||||
if (MainSettingsTab == null) return;
|
||||
|
||||
var simpleKeep = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"일반", "테마", "기능", "AX Agent"
|
||||
};
|
||||
var balancedKeep = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"일반", "테마", "클립보드", "캡처", "시스템", "기능", "AX Agent"
|
||||
};
|
||||
|
||||
foreach (var item in MainSettingsTab.Items.OfType<TabItem>())
|
||||
{
|
||||
var header = item.Header?.ToString() ?? "";
|
||||
var visible = mode switch
|
||||
{
|
||||
"simple" => simpleKeep.Contains(header),
|
||||
"balanced" => balancedKeep.Contains(header),
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if (string.Equals(header, "알림", StringComparison.OrdinalIgnoreCase))
|
||||
visible = false;
|
||||
if (string.Equals(header, "AX Agent", StringComparison.OrdinalIgnoreCase)
|
||||
&& !((System.Windows.Application.Current as App)?.SettingsService?.Settings.AiEnabled ?? false))
|
||||
{
|
||||
visible = false;
|
||||
}
|
||||
|
||||
item.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
if (MainSettingsTab.SelectedItem is TabItem selected && selected.Visibility == Visibility.Visible)
|
||||
return;
|
||||
|
||||
var firstVisible = MainSettingsTab.Items.OfType<TabItem>().FirstOrDefault(t => t.Visibility == Visibility.Visible);
|
||||
if (firstVisible != null)
|
||||
MainSettingsTab.SelectedItem = firstVisible;
|
||||
}
|
||||
|
||||
private void ApplyAgentSubTabVisibility(string mode)
|
||||
{
|
||||
if (AgentTabCommon == null) return;
|
||||
|
||||
SetSubTabVisible(AgentTabCommon, true);
|
||||
SetSubTabVisible(AgentTabChat, mode != "simple");
|
||||
SetSubTabVisible(AgentTabCoworkCode, true);
|
||||
SetSubTabVisible(AgentTabCowork, mode == "rich");
|
||||
SetSubTabVisible(AgentTabCode, mode == "rich");
|
||||
SetSubTabVisible(AgentTabTools, mode != "simple");
|
||||
SetSubTabVisible(AgentTabEtc, mode != "simple");
|
||||
SetSubTabVisible(AgentTabDev, mode == "rich");
|
||||
|
||||
if (AgentTabCommon.IsChecked != true
|
||||
&& AgentTabChat.IsChecked != true
|
||||
&& AgentTabCoworkCode.IsChecked != true
|
||||
&& AgentTabCowork.IsChecked != true
|
||||
&& AgentTabCode.IsChecked != true
|
||||
&& AgentTabTools.IsChecked != true
|
||||
&& AgentTabEtc.IsChecked != true
|
||||
&& AgentTabDev.IsChecked != true)
|
||||
{
|
||||
AgentTabCommon.IsChecked = true;
|
||||
}
|
||||
|
||||
AgentSubTab_Checked(this, new RoutedEventArgs());
|
||||
}
|
||||
|
||||
private static void SetSubTabVisible(RadioButton? tab, bool visible)
|
||||
{
|
||||
if (tab == null) return;
|
||||
tab.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (!visible) tab.IsChecked = false;
|
||||
}
|
||||
|
||||
private static string NormalizeDisplayMode(string? mode)
|
||||
{
|
||||
return (mode ?? "balanced").Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"rich" => "rich",
|
||||
"simple" => "simple",
|
||||
_ => "balanced",
|
||||
};
|
||||
}
|
||||
|
||||
private RadioButton? GetDisplayModeRadio(string name)
|
||||
{
|
||||
if (MainSettingsTab?.Template == null) return null;
|
||||
return MainSettingsTab.Template.FindName(name, MainSettingsTab) as RadioButton;
|
||||
}
|
||||
|
||||
// ─── 에이전트 차단 섹션 → 기타 탭 이동 ──────────────────────────────────────
|
||||
|
||||
private void MoveBlockSectionToEtc()
|
||||
@@ -1558,6 +1703,7 @@ public partial class SettingsWindow : Window
|
||||
Service = currentService,
|
||||
Endpoint = dlg.Endpoint,
|
||||
ApiKey = dlg.ApiKey,
|
||||
AllowInsecureTls = dlg.AllowInsecureTls,
|
||||
AuthType = dlg.AuthType,
|
||||
Cp4dUrl = dlg.Cp4dUrl,
|
||||
Cp4dUsername = dlg.Cp4dUsername,
|
||||
@@ -1576,7 +1722,7 @@ public partial class SettingsWindow : Window
|
||||
|
||||
var currentService = GetCurrentServiceSubTab();
|
||||
var dlg = new ModelRegistrationDialog(currentService, row.Alias, currentModel,
|
||||
row.Endpoint, row.ApiKey,
|
||||
row.Endpoint, row.ApiKey, row.AllowInsecureTls,
|
||||
row.AuthType ?? "bearer", row.Cp4dUrl ?? "", row.Cp4dUsername ?? "", cp4dPw);
|
||||
dlg.Owner = this;
|
||||
if (dlg.ShowDialog() == true)
|
||||
@@ -1586,6 +1732,7 @@ public partial class SettingsWindow : Window
|
||||
row.Service = currentService;
|
||||
row.Endpoint = dlg.Endpoint;
|
||||
row.ApiKey = dlg.ApiKey;
|
||||
row.AllowInsecureTls = dlg.AllowInsecureTls;
|
||||
row.AuthType = dlg.AuthType;
|
||||
row.Cp4dUrl = dlg.Cp4dUrl;
|
||||
row.Cp4dUsername = dlg.Cp4dUsername;
|
||||
@@ -2051,9 +2198,9 @@ public partial class SettingsWindow : Window
|
||||
{
|
||||
AiEnabledToggle.IsChecked = enabled;
|
||||
}
|
||||
// AX Agent 탭 가시성
|
||||
// AX Agent 상세 설정은 전용 창으로 분리 (탭은 숨김)
|
||||
if (AgentTabItem != null)
|
||||
AgentTabItem.Visibility = enabled ? Visibility.Visible : Visibility.Collapsed;
|
||||
AgentTabItem.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void ApplyOperationModeState(string? mode)
|
||||
|
||||
Reference in New Issue
Block a user