From 23d1534536a6f7cf1768c05ef6f39b472a70bb93 Mon Sep 17 00:00:00 2001 From: lacvet Date: Sat, 4 Apr 2026 08:38:51 +0900 Subject: [PATCH] =?UTF-8?q?[v2.0.0]=20AgentSettingsPanel=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=99=84=EC=84=B1=20=E2=80=94=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=C2=B7=EB=AA=A8=EB=8D=B8=C2=B7=EC=9E=91=EC=97=85?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=C2=B7MCP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AgentSettingsPanel.xaml.cs: - BtnServiceSelector_Click: ServiceSelectorRequested 콜백 실행 (ChatWindow 위임) - BtnModelSelector_Click: ModelSelectorRequested 콜백 실행 (ChatWindow 위임) - ServiceSelectorRequested / ModelSelectorRequested / WorkFolderBrowseRequested Action 추가 - LoadFromSettings: UpdateWorkFolder() + BuildMcpServerList() 호출 추가 - LoadFromSettings: PanelWorkFolder 가시성 제어 추가 - UpdateActiveTab: PanelWorkFolder 가시성 제어 추가 - BtnWorkFolderBrowse_Click, UpdateWorkFolder, BuildMcpServerList, McpToggle_Changed 추가 - using System.Linq / using AxCopilot.Models 추가 AgentSettingsPanel.xaml: - PanelWorkFolder 섹션 추가 (Code 탭 전용 — 폴더 표시 + 탐색 버튼) - PanelMcpServers 섹션 추가 (도구 관리 뒤, 고급 앞) ChatWindow.MoodMenu.cs: - ToggleSettingsPanel(): 패널 열 때 ServiceSelector/ModelSelector/WorkFolder 콜백 연결 빌드: 경고 0, 오류 0 Co-Authored-By: Claude Sonnet 4.6 --- src/AxCopilot/Views/ChatWindow.MoodMenu.cs | 4 + .../Views/Controls/AgentSettingsPanel.xaml | 48 +++++++ .../Views/Controls/AgentSettingsPanel.xaml.cs | 117 +++++++++++++++++- 3 files changed, 166 insertions(+), 3 deletions(-) diff --git a/src/AxCopilot/Views/ChatWindow.MoodMenu.cs b/src/AxCopilot/Views/ChatWindow.MoodMenu.cs index db80eaf..71d1b1e 100644 --- a/src/AxCopilot/Views/ChatWindow.MoodMenu.cs +++ b/src/AxCopilot/Views/ChatWindow.MoodMenu.cs @@ -447,6 +447,10 @@ public partial class ChatWindow SettingsPanel.CloseRequested += OnSettingsPanelClose; SettingsPanel.SettingsChanged -= OnSettingsPanelChanged; SettingsPanel.SettingsChanged += OnSettingsPanelChanged; + // 콜백 연결 — ChatWindow 기존 구현에 위임 + SettingsPanel.ServiceSelectorRequested = () => BtnModelSelector_Click(BtnModelSelector, new System.Windows.RoutedEventArgs()); + SettingsPanel.ModelSelectorRequested = () => BtnModelSelector_Click(BtnModelSelector, new System.Windows.RoutedEventArgs()); + SettingsPanel.WorkFolderBrowseRequested = () => BrowseWorkFolder(); SettingsPanel.IsOpen = true; } } diff --git a/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml b/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml index e2dfaa9..0f0297d 100644 --- a/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml +++ b/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml @@ -291,6 +291,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -303,6 +341,16 @@ + + + + + + + + + diff --git a/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml.cs b/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml.cs index a476fe9..223bc16 100644 --- a/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml.cs +++ b/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml.cs @@ -1,7 +1,9 @@ +using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; +using AxCopilot.Models; using AxCopilot.Services; namespace AxCopilot.Views.Controls; @@ -18,6 +20,15 @@ public partial class AgentSettingsPanel : UserControl private bool _isOpen; private bool _isLoading = true; // 초기 로드 중 이벤트 발생 방지 + /// 서비스 선택 요청 콜백 (ChatWindow.BtnModelSelector_Click으로 위임). + public Action? ServiceSelectorRequested; + + /// 모델 선택 요청 콜백 (ChatWindow.BtnModelSelector_Click으로 위임). + public Action? ModelSelectorRequested; + + /// 작업 폴더 탐색 요청 콜백 (ChatWindow.BrowseWorkFolder로 위임). + public Action? WorkFolderBrowseRequested; + /// 설정 패널 열림/닫힘 상태. public bool IsOpen { @@ -78,6 +89,7 @@ public partial class AgentSettingsPanel : UserControl // 탭별 설정 표시 PanelCoworkSettings.Visibility = activeTab == "Cowork" ? Visibility.Visible : Visibility.Collapsed; PanelCodeSettings.Visibility = activeTab == "Code" ? Visibility.Visible : Visibility.Collapsed; + PanelWorkFolder.Visibility = activeTab == "Code" ? Visibility.Visible : Visibility.Collapsed; if (activeTab == "Cowork") { @@ -92,6 +104,11 @@ public partial class AgentSettingsPanel : UserControl // 도구 토글 동적 생성 BuildToolToggles(settings, toolRegistry); + // 작업 폴더 표시 (Code 탭) + UpdateWorkFolder(settings); + // MCP 서버 목록 표시 + BuildMcpServerList(settings); + _isLoading = false; } @@ -100,7 +117,8 @@ public partial class AgentSettingsPanel : UserControl { TxtCurrentTab.Text = tab; PanelCoworkSettings.Visibility = tab == "Cowork" ? Visibility.Visible : Visibility.Collapsed; - PanelCodeSettings.Visibility = tab == "Code" ? Visibility.Visible : Visibility.Collapsed; + PanelCodeSettings.Visibility = tab == "Code" ? Visibility.Visible : Visibility.Collapsed; + PanelWorkFolder.Visibility = tab == "Code" ? Visibility.Visible : Visibility.Collapsed; } private void BuildToolToggles(SettingsService settings, @@ -159,12 +177,105 @@ public partial class AgentSettingsPanel : UserControl private void BtnServiceSelector_Click(object sender, System.Windows.Input.MouseButtonEventArgs e) { - // TODO: 서비스 선택 팝업 (Ollama/vLLM/Gemini/Claude) + ServiceSelectorRequested?.Invoke(); } private void BtnModelSelector_Click(object sender, System.Windows.Input.MouseButtonEventArgs e) { - // TODO: 모델 선택 팝업 — ChatWindow의 기존 모델 선택 로직 재사용 + ModelSelectorRequested?.Invoke(); + } + + private void BtnWorkFolderBrowse_Click(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + WorkFolderBrowseRequested?.Invoke(); + } + + private void UpdateWorkFolder(SettingsService settings) + { + var folder = settings.Settings.Llm.WorkFolder; + if (TxtWorkFolder == null) return; + TxtWorkFolder.Text = string.IsNullOrWhiteSpace(folder) + ? "(미지정)" + : System.IO.Path.GetFileName(folder) is { Length: > 0 } name ? name : folder; + TxtWorkFolder.ToolTip = folder; + } + + private void BuildMcpServerList(SettingsService settings) + { + if (PanelMcpServers == null) return; + PanelMcpServers.Children.Clear(); + + var servers = settings.Settings.Llm.McpServers; + if (servers == null || servers.Count == 0) + { + PanelMcpServers.Children.Add(new TextBlock + { + Text = "등록된 MCP 서버 없음", + FontSize = 11, + Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray, + Margin = new Thickness(0, 0, 0, 4), + }); + return; + } + + foreach (var server in servers) + { + var grid = new System.Windows.Controls.Grid { Margin = new Thickness(0, 0, 0, 6) }; + grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(8) }); + grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); + grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); + + var dot = new System.Windows.Shapes.Ellipse + { + Width = 6, Height = 6, + Fill = server.Enabled + ? new SolidColorBrush(Color.FromRgb(0x38, 0xA1, 0x69)) + : new SolidColorBrush(Color.FromRgb(0x9E, 0x9E, 0x9E)), + VerticalAlignment = VerticalAlignment.Center, + }; + Grid.SetColumn(dot, 0); + + var nameLabel = new TextBlock + { + Text = server.Name, + FontSize = 11, + Foreground = TryFindResource("PrimaryText") as Brush ?? Brushes.White, + VerticalAlignment = VerticalAlignment.Center, + Margin = new Thickness(6, 0, 0, 0), + TextTrimming = System.Windows.TextTrimming.CharacterEllipsis, + }; + Grid.SetColumn(nameLabel, 1); + + var chk = new CheckBox + { + IsChecked = server.Enabled, + Tag = server.Name, + VerticalAlignment = VerticalAlignment.Center, + }; + if (TryFindResource("ToggleSwitch") is Style toggleStyle) + chk.Style = toggleStyle; + chk.Checked += McpToggle_Changed; + chk.Unchecked += McpToggle_Changed; + Grid.SetColumn(chk, 2); + + grid.Children.Add(dot); + grid.Children.Add(nameLabel); + grid.Children.Add(chk); + PanelMcpServers.Children.Add(grid); + } + } + + private void McpToggle_Changed(object sender, RoutedEventArgs e) + { + if (_isLoading || sender is not CheckBox chk || chk.Tag is not string name) return; + var app = System.Windows.Application.Current as App; + var settings = app?.SettingsService; + if (settings == null) return; + var server = settings.Settings.Llm.McpServers?.FirstOrDefault(s => s.Name == name); + if (server == null) return; + server.Enabled = chk.IsChecked == true; + settings.Save(); + SettingsChanged?.Invoke(this, EventArgs.Empty); } private void TxtApiEndpoint_LostFocus(object sender, RoutedEventArgs e)