AX Agent 내부 설정 탭 분리와 테마 복구
Some checks failed
Release Gate / gate (push) Has been cancelled

- AX Agent 내부 설정에서 스킬/차단 탭을 도구, 스킬, 차단으로 분리하고 각 패널을 기능별로 재배치

- 공통 탭에 테마 스타일과 테마 모드를 실제 선택 카드 UI로 복구하고 기존 숨김 플레이스홀더를 제거

- 메인 설정 좌측의 AX Agent 이동 항목을 맨 아래로 재배치하고 README 및 DEVELOPMENT 문서 이력을 2026-04-05 15:06 (KST) 기준으로 갱신

- 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ / 경고 0 오류 0
This commit is contained in:
2026-04-05 13:39:44 +09:00
parent d102e17d47
commit d368ebf822
5 changed files with 231 additions and 84 deletions

View File

@@ -2481,22 +2481,30 @@
Margin="0,4,0,0"
Checked="OverlayNav_Checked"
Tag="dev"/>
<RadioButton x:Name="OverlayNavTools"
Content="도구"
<RadioButton x:Name="OverlayNavTools"
Content="도구"
GroupName="OverlayNav"
Style="{StaticResource SettingsNavBtn}"
HorizontalAlignment="Stretch"
Margin="0,4,0,0"
Checked="OverlayNav_Checked"
Tag="tools"/>
<RadioButton x:Name="OverlayNavSkill"
Content="스킬"
GroupName="OverlayNav"
Style="{StaticResource SettingsNavBtn}"
HorizontalAlignment="Stretch"
Margin="0,4,0,0"
Checked="OverlayNav_Checked"
Tag="tools"/>
<RadioButton x:Name="OverlayNavEtc"
Content="스킬/차단"
Tag="skill"/>
<RadioButton x:Name="OverlayNavBlock"
Content="차단"
GroupName="OverlayNav"
Style="{StaticResource SettingsNavBtn}"
HorizontalAlignment="Stretch"
Margin="0,4,0,0"
Checked="OverlayNav_Checked"
Tag="etc"/>
Tag="block"/>
</StackPanel>
</Border>
<ScrollViewer Grid.Row="0" Grid.RowSpan="2" Grid.Column="1"
@@ -2953,21 +2961,105 @@
</Grid>
<StackPanel x:Name="OverlayThemeStylePanel"
Visibility="Collapsed"
Height="0"
Margin="0"
IsHitTestVisible="False">
<Border x:Name="OverlayThemeStyleClawCard"/>
<Border x:Name="OverlayThemeStyleCodexCard"/>
<Border x:Name="OverlayThemeStyleSlateCard"/>
Margin="0,0,0,12">
<TextBlock Text="테마 스타일"
FontSize="12.5"
FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryText}"/>
<TextBlock Text="AX Agent 전용 시각 언어를 고릅니다. 채팅 본문과 설정 카드 모두 같은 스타일로 바뀝니다."
Margin="0,4,0,8"
FontSize="11"
TextWrapping="Wrap"
Foreground="{DynamicResource SecondaryText}"/>
<WrapPanel>
<Border x:Name="OverlayThemeStyleClawCard"
Cursor="Hand"
CornerRadius="8"
BorderThickness="1"
BorderBrush="{DynamicResource BorderColor}"
Padding="10,7"
Margin="0,0,8,8"
MouseLeftButtonUp="OverlayThemeStyleClawCard_MouseLeftButtonUp">
<TextBlock Text="Claw"
FontSize="12"
Foreground="{DynamicResource PrimaryText}"/>
</Border>
<Border x:Name="OverlayThemeStyleCodexCard"
Cursor="Hand"
CornerRadius="8"
BorderThickness="1"
BorderBrush="{DynamicResource BorderColor}"
Padding="10,7"
Margin="0,0,8,8"
MouseLeftButtonUp="OverlayThemeStyleCodexCard_MouseLeftButtonUp">
<TextBlock Text="Codex"
FontSize="12"
Foreground="{DynamicResource PrimaryText}"/>
</Border>
<Border x:Name="OverlayThemeStyleSlateCard"
Cursor="Hand"
CornerRadius="8"
BorderThickness="1"
BorderBrush="{DynamicResource BorderColor}"
Padding="10,7"
Margin="0,0,8,8"
MouseLeftButtonUp="OverlayThemeStyleSlateCard_MouseLeftButtonUp">
<TextBlock Text="Slate"
FontSize="12"
Foreground="{DynamicResource PrimaryText}"/>
</Border>
</WrapPanel>
</StackPanel>
<StackPanel x:Name="OverlayThemePanel"
Visibility="Collapsed"
Height="0"
Margin="0"
IsHitTestVisible="False">
<Border x:Name="OverlayThemeSystemCard"/>
<Border x:Name="OverlayThemeLightCard"/>
<Border x:Name="OverlayThemeDarkCard"/>
Margin="0,0,0,12">
<TextBlock Text="테마 모드"
FontSize="12.5"
FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryText}"/>
<TextBlock Text="시스템 테마를 따르거나, AX Agent만 별도로 라이트/다크 모드로 고정할 수 있습니다."
Margin="0,4,0,8"
FontSize="11"
TextWrapping="Wrap"
Foreground="{DynamicResource SecondaryText}"/>
<WrapPanel>
<Border x:Name="OverlayThemeSystemCard"
Cursor="Hand"
CornerRadius="8"
BorderThickness="1"
BorderBrush="{DynamicResource BorderColor}"
Padding="10,7"
Margin="0,0,8,8"
MouseLeftButtonUp="OverlayThemeSystemCard_MouseLeftButtonUp">
<TextBlock Text="System"
FontSize="12"
Foreground="{DynamicResource PrimaryText}"/>
</Border>
<Border x:Name="OverlayThemeLightCard"
Cursor="Hand"
CornerRadius="8"
BorderThickness="1"
BorderBrush="{DynamicResource BorderColor}"
Padding="10,7"
Margin="0,0,8,8"
MouseLeftButtonUp="OverlayThemeLightCard_MouseLeftButtonUp">
<TextBlock Text="Light"
FontSize="12"
Foreground="{DynamicResource PrimaryText}"/>
</Border>
<Border x:Name="OverlayThemeDarkCard"
Cursor="Hand"
CornerRadius="8"
BorderThickness="1"
BorderBrush="{DynamicResource BorderColor}"
Padding="10,7"
Margin="0,0,8,8"
MouseLeftButtonUp="OverlayThemeDarkCard_MouseLeftButtonUp">
<TextBlock Text="Dark"
FontSize="12"
Foreground="{DynamicResource PrimaryText}"/>
</Border>
</WrapPanel>
</StackPanel>
<Grid x:Name="OverlayDefaultOutputFormatRow" Margin="0,0,0,8">
<Grid.ColumnDefinitions>
@@ -4715,7 +4807,7 @@
</Grid>
<StackPanel x:Name="OverlayHookListPanel"/>
</StackPanel>
<Border x:Name="OverlayEtcInfoPanel"
<Border x:Name="OverlaySkillInfoPanel"
Visibility="Collapsed"
Background="{DynamicResource ItemBackground}"
BorderBrush="{DynamicResource BorderColor}"
@@ -4724,7 +4816,7 @@
Padding="14,12"
Margin="0,14,0,0">
<StackPanel>
<TextBlock Text="스킬/차단 설명"
<TextBlock Text="스킬 설명"
FontSize="12.5"
FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryText}"/>
@@ -4750,9 +4842,35 @@
</TextBlock>
</StackPanel>
</Border>
<StackPanel x:Name="OverlayEtcRuntimePanel"
Visibility="Collapsed"
Margin="0,14,0,0">
<Border x:Name="OverlayBlockInfoPanel"
Visibility="Collapsed"
Background="{DynamicResource ItemBackground}"
BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1"
CornerRadius="12"
Padding="14,12"
Margin="0,14,0,0">
<StackPanel>
<TextBlock Text="차단 설명"
FontSize="12.5"
FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryText}"/>
<TextBlock Text="차단 탭에서는 에이전트가 접근하거나 수정하면 안 되는 경로와 파일 형식을 확인합니다."
Margin="0,4,0,10"
FontSize="11"
TextWrapping="Wrap"
Foreground="{DynamicResource SecondaryText}"/>
<TextBlock FontSize="11.5"
TextWrapping="Wrap"
Foreground="{DynamicResource PrimaryText}">
시스템 보호 경로와 실행 위험이 높은 형식은 기본값으로 막아 둡니다.
<LineBreak/>운영 정책에 맞춰 차단 기준을 검토할 때 이 탭을 확인하면 됩니다.
</TextBlock>
</StackPanel>
</Border>
<StackPanel x:Name="OverlayBlockRuntimePanel"
Visibility="Collapsed"
Margin="0,14,0,0">
<TextBlock Text="에이전트 접근 차단"
FontSize="12.5"
FontWeight="SemiBold"
@@ -4794,6 +4912,10 @@
</StackPanel>
</Border>
</StackPanel>
<StackPanel x:Name="OverlaySkillRuntimePanel"
Visibility="Collapsed"
Margin="0,14,0,0">
<TextBlock Text="스킬 설정"
FontSize="12.5"
FontWeight="SemiBold"
@@ -5052,7 +5174,10 @@
Margin="0,8,0,14">
<StackPanel x:Name="OverlayMcpServerListPanel"/>
</Border>
</StackPanel>
<StackPanel x:Name="OverlayToolRegistrySection"
Visibility="Collapsed"
Margin="0,14,0,0">
<TextBlock Text="등록된 도구/커넥터"
FontSize="12.5"
FontWeight="SemiBold"

View File

@@ -15214,7 +15214,8 @@ public partial class ChatWindow : Window
var showCode = section == "code";
var showDev = section == "dev";
var showTools = section == "tools";
var showEtc = section == "etc";
var showSkill = section == "skill";
var showBlock = section == "block";
OverlaySectionService.Visibility = showBasic ? Visibility.Visible : Visibility.Collapsed;
OverlaySectionQuick.Visibility = showShared || showCowork || showCode ? Visibility.Visible : Visibility.Collapsed;
@@ -15228,7 +15229,8 @@ public partial class ChatWindow : Window
"code" => "코드 설정",
"dev" => "개발자 설정",
"tools" => "도구 설정",
"etc" => "스킬/차단 설정",
"skill" => "스킬 설정",
"block" => "차단 설정",
_ => "공통 설정"
};
var headingDescription = section switch
@@ -15239,7 +15241,8 @@ public partial class ChatWindow : Window
"code" => "코드 작업, 검증, 개발 도구 사용에 맞춘 설정입니다.",
"dev" => "실행 이력, 감사, 시각화 같은 개발자용 설정입니다.",
"tools" => "AX Agent가 사용할 도구와 훅 동작을 관리합니다.",
"etc" => "스킬 로드와 차단 규칙, 보조 연결을 관리합니다.",
"skill" => "슬래시 스킬, 스킬 폴더, 폴백 모델, MCP 연결을 관리합니다.",
"block" => "에이전트가 접근하거나 수정하면 안 되는 경로와 형식을 관리합니다.",
_ => "Chat, Cowork, Code에서 공통으로 쓰는 기본 설정입니다."
};
@@ -15289,19 +15292,25 @@ public partial class ChatWindow : Window
if (OverlayDeveloperExtraPanel != null)
OverlayDeveloperExtraPanel.Visibility = showDev ? Visibility.Visible : Visibility.Collapsed;
if (OverlayAdvancedTogglePanel != null)
OverlayAdvancedTogglePanel.Visibility = showDev || showCowork || showCode || showTools || showEtc ? Visibility.Visible : Visibility.Collapsed;
OverlayAdvancedTogglePanel.Visibility = showDev || showCowork || showCode || showTools || showSkill ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToolsInfoPanel != null)
OverlayToolsInfoPanel.Visibility = showTools ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToolsRuntimePanel != null)
OverlayToolsRuntimePanel.Visibility = showTools ? Visibility.Visible : Visibility.Collapsed;
if (OverlayEtcInfoPanel != null)
OverlayEtcInfoPanel.Visibility = showEtc ? Visibility.Visible : Visibility.Collapsed;
if (OverlayEtcRuntimePanel != null)
OverlayEtcRuntimePanel.Visibility = showEtc ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToolRegistrySection != null)
OverlayToolRegistrySection.Visibility = showTools ? Visibility.Visible : Visibility.Collapsed;
if (OverlaySkillInfoPanel != null)
OverlaySkillInfoPanel.Visibility = showSkill ? Visibility.Visible : Visibility.Collapsed;
if (OverlaySkillRuntimePanel != null)
OverlaySkillRuntimePanel.Visibility = showSkill ? Visibility.Visible : Visibility.Collapsed;
if (OverlayBlockInfoPanel != null)
OverlayBlockInfoPanel.Visibility = showBlock ? Visibility.Visible : Visibility.Collapsed;
if (OverlayBlockRuntimePanel != null)
OverlayBlockRuntimePanel.Visibility = showBlock ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleProactiveCompact != null)
OverlayToggleProactiveCompact.Visibility = showDev ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleSkillSystem != null)
OverlayToggleSkillSystem.Visibility = showEtc ? Visibility.Visible : Visibility.Collapsed;
OverlayToggleSkillSystem.Visibility = showSkill ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleToolHooks != null)
OverlayToggleToolHooks.Visibility = showTools ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleHookInputMutation != null)
@@ -15317,9 +15326,9 @@ public partial class ChatWindow : Window
if (OverlayToggleParallelTools != null)
OverlayToggleParallelTools.Visibility = showCode ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleProjectRules != null)
OverlayToggleProjectRules.Visibility = showEtc ? Visibility.Visible : Visibility.Collapsed;
OverlayToggleProjectRules.Visibility = showDev ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleAgentMemory != null)
OverlayToggleAgentMemory.Visibility = showEtc ? Visibility.Visible : Visibility.Collapsed;
OverlayToggleAgentMemory.Visibility = showDev ? Visibility.Visible : Visibility.Collapsed;
if (OverlayTogglePlanModeTools != null)
OverlayTogglePlanModeTools.Visibility = showCode ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleWorktreeTools != null)
@@ -15329,7 +15338,7 @@ public partial class ChatWindow : Window
if (OverlayToggleCronTools != null)
OverlayToggleCronTools.Visibility = showCode ? Visibility.Visible : Visibility.Collapsed;
if (showTools || showEtc)
if (showTools || showSkill || showBlock)
RefreshOverlayEtcPanels();
}

View File

@@ -34,6 +34,14 @@ public partial class SettingsWindow : Window
_revertCallback = revertCallback;
DataContext = vm;
if (MainSettingsTab != null && AgentTabItem != null && MainSettingsTab.Items.Contains(AgentTabItem))
MainSettingsTab.Items.Remove(AgentTabItem);
if (MainSettingsTab != null && AgentShortcutTabItem != null && MainSettingsTab.Items.Contains(AgentShortcutTabItem))
{
MainSettingsTab.Items.Remove(AgentShortcutTabItem);
MainSettingsTab.Items.Add(AgentShortcutTabItem);
}
vm.ThemePreviewRequested += (_, _) => _previewCallback(vm.SelectedThemeKey);
vm.SaveCompleted += (_, _) =>
{
@@ -60,8 +68,11 @@ public partial class SettingsWindow : Window
BuildQuoteCategoryCheckboxes();
BuildDockBarSettings();
BuildTextActionCommandsPanel();
MoveBlockSectionToEtc();
BuildServiceModelPanels();
if (HasLegacyAgentTab())
{
MoveBlockSectionToEtc();
BuildServiceModelPanels();
}
// 스킬이 아직 로드되지 않았으면 백그라운드에서 로드 후 UI 구성
var app = System.Windows.Application.Current as App;
@@ -76,8 +87,11 @@ public partial class SettingsWindow : Window
});
}
BuildToolRegistryPanel();
LoadAdvancedSettings();
if (HasLegacyAgentTab())
{
BuildToolRegistryPanel();
LoadAdvancedSettings();
}
RefreshStorageInfo();
// 개발자 모드는 저장된 설정 유지 (끄면 하위 기능 모두 비활성)
UpdateDevModeContentVisibility();
@@ -85,10 +99,14 @@ public partial class SettingsWindow : Window
ApplyAiEnabledState(app?.SettingsService?.Settings.AiEnabled ?? false, init: true);
ApplyOperationModeState(app?.SettingsService?.Settings.OperationMode);
InitializeDisplayModeUi();
SyncAgentSelectionCards();
if (HasLegacyAgentTab())
SyncAgentSelectionCards();
};
}
private bool HasLegacyAgentTab()
=> MainSettingsTab != null && AgentTabItem != null && MainSettingsTab.Items.Contains(AgentTabItem);
private void SyncAgentSelectionCards()
{
var service = (_vm.LlmService ?? "").Trim().ToLowerInvariant();
@@ -139,8 +157,9 @@ public partial class SettingsWindow : Window
private void InitializeDisplayModeUi()
{
var app = System.Windows.Application.Current as App;
var saved = app?.SettingsService?.Settings?.Llm?.AgentUiExpressionLevel;
SetDisplayMode(saved ?? "balanced", persist: false);
if (app?.SettingsService?.Settings?.Llm != null)
app.SettingsService.Settings.Llm.AgentUiExpressionLevel = "rich";
SetDisplayMode("rich", persist: false);
}
private void DisplayMode_Checked(object sender, RoutedEventArgs e)
@@ -159,14 +178,7 @@ public partial class SettingsWindow : Window
private void AgentDisplayMode_Checked(object sender, RoutedEventArgs e)
{
if (_isDisplayModeSyncing) return;
var rb = sender as RadioButton;
var next = rb?.Name switch
{
"AgentDisplayModeRich" => "rich",
"AgentDisplayModeSimple" => "simple",
_ => "balanced",
};
SetDisplayMode(next, persist: true);
SetDisplayMode("rich", persist: true);
}
private void SetDisplayMode(string mode, bool persist)
@@ -195,7 +207,8 @@ public partial class SettingsWindow : Window
}
ApplyMainTabVisibility(mode);
ApplyAgentSubTabVisibility(mode);
if (HasLegacyAgentTab())
ApplyAgentSubTabVisibility(mode);
if (!persist) return;
var app = System.Windows.Application.Current as App;
@@ -210,11 +223,11 @@ public partial class SettingsWindow : Window
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>())
@@ -229,12 +242,6 @@ public partial class SettingsWindow : Window
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;
}
@@ -2583,31 +2590,12 @@ public partial class SettingsWindow : Window
/// <summary>AI 기능 토글 상태를 UI와 설정에 반영합니다.</summary>
private void ApplyAiEnabledState(bool enabled, bool init = false)
{
// 토글 스위치 체크 상태 동기화 (init 시에는 이벤트 억제)
if (AiEnabledToggle != null && AiEnabledToggle.IsChecked != enabled)
{
AiEnabledToggle.IsChecked = enabled;
}
if (AgentAiEnabledToggle != null && AgentAiEnabledToggle.IsChecked != enabled)
{
AgentAiEnabledToggle.IsChecked = enabled;
}
if (AgentTabItem != null)
AgentTabItem.Visibility = enabled ? Visibility.Visible : Visibility.Collapsed;
ApplyMainTabVisibility(NormalizeDisplayMode((System.Windows.Application.Current as App)?.SettingsService?.Settings?.Llm?.AgentUiExpressionLevel));
}
public void SelectAgentSettingsTab()
{
if (AgentTabItem == null || MainSettingsTab == null || AgentTabItem.Visibility != Visibility.Visible)
return;
MainSettingsTab.SelectedItem = AgentTabItem;
if (AgentTabCommon != null)
AgentTabCommon.IsChecked = true;
AgentSubTab_Checked(this, new RoutedEventArgs());
Activate();
OpenAgentSettingsShortcut(closeAfterOpen: false);
}
private void BtnAgentSettingsBack_Click(object sender, RoutedEventArgs e)
@@ -2625,13 +2613,32 @@ public partial class SettingsWindow : Window
private void BtnAgentShortcut_Click(object sender, RoutedEventArgs e)
{
SelectAgentSettingsTab();
OpenAgentSettingsShortcut(closeAfterOpen: true);
}
private void MainSettingsTab_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (MainSettingsTab?.SelectedItem is not TabItem selected)
return;
if (!ReferenceEquals(selected, AgentShortcutTabItem))
return;
OpenAgentSettingsShortcut(closeAfterOpen: true);
}
private void OpenAgentSettingsShortcut(bool closeAfterOpen)
{
var app = System.Windows.Application.Current as App;
app?.OpenAgentSettingsInChat();
if (closeAfterOpen)
Close();
}
private void ApplyOperationModeState(string? mode)
{
var normalized = OperationModePolicy.Normalize(mode);
SyncOperationModeCombo(OperationModeCombo, normalized);
if (AgentOperationModeInternal != null) AgentOperationModeInternal.IsChecked = normalized == OperationModePolicy.InternalMode;
if (AgentOperationModeExternal != null) AgentOperationModeExternal.IsChecked = normalized == OperationModePolicy.ExternalMode;
}
@@ -2833,7 +2840,6 @@ public partial class SettingsWindow : Window
else
{
// 취소/실패 ? 토글 원상복구
if (AiEnabledToggle != null) AiEnabledToggle.IsChecked = false;
if (AgentAiEnabledToggle != null) AgentAiEnabledToggle.IsChecked = false;
}
}