From d67c378389265b289b43122e39b6928f0ff1252f Mon Sep 17 00:00:00 2001 From: lacvet Date: Sat, 4 Apr 2026 08:44:50 +0900 Subject: [PATCH] =?UTF-8?q?[v2.0.0]=20AgentSettingsPanel=20=ED=94=84?= =?UTF-8?q?=EB=A6=AC=EC=85=8B=20=EC=84=B9=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AgentSettingsPanel.xaml (+46줄): - 프리셋 섹션 신규 추가 (MCP 서버 ↔ 고급 사이) - "새 프리셋 저장" 버튼 (BtnPresetSave, 보라 아이콘) - "프리셋 관리 →" 버튼 (BtnPresetManage, 화살표 아이콘) - 호버 스타일 DynamicResource 적용 AgentSettingsPanel.xaml.cs (+10줄): - PresetSaveRequested / PresetManageRequested Action 콜백 프로퍼티 추가 - BtnPresetSave_Click / BtnPresetManage_Click 핸들러 추가 ChatWindow.CustomPresets.cs (+110줄): - ShowPresetManagePopup() 메서드 신규 구현 · 현재 탭의 커스텀 프리셋 목록을 Popup으로 표시 · 각 항목에 편집(✏)·삭제(🗑) 아이콘 인라인 배치 · "새 프리셋 추가" 단축 버튼 하단 고정 · PlacementMode.Left로 설정 패널 왼쪽에 앵커 ChatWindow.MoodMenu.cs (+2줄): - ToggleSettingsPanel() 내 PresetSaveRequested·PresetManageRequested 콜백 연결 빌드: 경고 0, 오류 0 --- .../Views/ChatWindow.CustomPresets.cs | 167 ++++++++++++++++++ src/AxCopilot/Views/ChatWindow.MoodMenu.cs | 6 +- .../Views/Controls/AgentSettingsPanel.xaml | 57 ++++++ .../Views/Controls/AgentSettingsPanel.xaml.cs | 12 ++ 4 files changed, 240 insertions(+), 2 deletions(-) diff --git a/src/AxCopilot/Views/ChatWindow.CustomPresets.cs b/src/AxCopilot/Views/ChatWindow.CustomPresets.cs index 6f31938..b45d834 100644 --- a/src/AxCopilot/Views/ChatWindow.CustomPresets.cs +++ b/src/AxCopilot/Views/ChatWindow.CustomPresets.cs @@ -156,6 +156,173 @@ public partial class ChatWindow return item; } + /// 현재 탭의 커스텀 프리셋 목록 팝업을 표시합니다. + internal void ShowPresetManagePopup() + { + var customPresets = Llm.CustomPresets + .Where(c => c.Tab.Equals(_activeTab, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + var primaryText = ThemeResourceHelper.Primary(this); + var secondaryText = ThemeResourceHelper.Secondary(this); + var menuBg = ThemeResourceHelper.Background(this); + var borderBrush = ThemeResourceHelper.Border(this); + var hoverBg = ThemeResourceHelper.HoverBg(this); + + var popup = new Popup + { + PlacementTarget = SettingsPanel, + Placement = PlacementMode.Left, + VerticalOffset = -40, + StaysOpen = false, + AllowsTransparency = true, + }; + + var outer = new Border + { + Background = menuBg, + CornerRadius = new CornerRadius(12), + BorderBrush = borderBrush, + BorderThickness = new Thickness(1), + Padding = new Thickness(4), + MinWidth = 230, + MaxHeight = 340, + Effect = new System.Windows.Media.Effects.DropShadowEffect + { + BlurRadius = 16, ShadowDepth = 2, Opacity = 0.35, Color = Colors.Black, + }, + }; + + var sv = new ScrollViewer + { + VerticalScrollBarVisibility = ScrollBarVisibility.Auto, + HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled, + }; + + var stack = new StackPanel { Margin = new Thickness(4) }; + + // 헤더 + stack.Children.Add(new TextBlock + { + Text = $"{_activeTab} 탭 프리셋 ({customPresets.Count}개)", + FontSize = 12, + FontWeight = FontWeights.SemiBold, + Foreground = primaryText, + Margin = new Thickness(8, 8, 8, 6), + }); + + if (customPresets.Count == 0) + { + stack.Children.Add(new TextBlock + { + Text = "커스텀 프리셋이 없습니다.", + FontSize = 12, + Foreground = secondaryText, + Margin = new Thickness(8, 0, 8, 8), + }); + } + else + { + foreach (var cp in customPresets) + { + var capturedCp = cp; + + var row = new Border + { + Background = Brushes.Transparent, + CornerRadius = new CornerRadius(6), + Padding = new Thickness(6, 5, 6, 5), + Cursor = Cursors.Hand, + }; + row.MouseEnter += (s, _) => { if (s is Border b) b.Background = hoverBg; }; + row.MouseLeave += (s, _) => { if (s is Border b) b.Background = Brushes.Transparent; }; + + var rowGrid = new Grid(); + rowGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); + rowGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); + rowGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); + + rowGrid.Children.Add(new TextBlock + { + Text = capturedCp.Label, + FontSize = 12, + Foreground = primaryText, + VerticalAlignment = VerticalAlignment.Center, + TextTrimming = TextTrimming.CharacterEllipsis, + }); + + var editIcon = new TextBlock + { + Text = "\uE70F", + FontFamily = ThemeResourceHelper.SegoeMdl2, + FontSize = 12, + Foreground = secondaryText, + VerticalAlignment = VerticalAlignment.Center, + Cursor = Cursors.Hand, + Margin = new Thickness(8, 0, 4, 0), + }; + Grid.SetColumn(editIcon, 1); + editIcon.MouseLeftButtonDown += (_, _) => + { + popup.IsOpen = false; + var entry = Llm.CustomPresets.FirstOrDefault(c => c.Id == capturedCp.Id); + if (entry != null) ShowCustomPresetDialog(entry); + }; + + var delIcon = new TextBlock + { + Text = "\uE74D", + FontFamily = ThemeResourceHelper.SegoeMdl2, + FontSize = 12, + Foreground = new SolidColorBrush(Color.FromRgb(0xEF, 0x44, 0x44)), + VerticalAlignment = VerticalAlignment.Center, + Cursor = Cursors.Hand, + Margin = new Thickness(4, 0, 0, 0), + }; + Grid.SetColumn(delIcon, 2); + delIcon.MouseLeftButtonDown += (_, _) => + { + popup.IsOpen = false; + var result = CustomMessageBox.Show( + $"'{capturedCp.Label}' 프리셋을 삭제하시겠습니까?", + "프리셋 삭제", MessageBoxButton.YesNo, MessageBoxImage.Question); + if (result == MessageBoxResult.Yes) + { + Llm.CustomPresets.RemoveAll(c => c.Id == capturedCp.Id); + _settings.Save(); + BuildTopicButtons(); + } + }; + + rowGrid.Children.Add(editIcon); + rowGrid.Children.Add(delIcon); + row.Child = rowGrid; + stack.Children.Add(row); + } + } + + // 구분선 + 새 프리셋 추가 + stack.Children.Add(new Border + { + Height = 1, + Background = borderBrush, + Margin = new Thickness(4, 4, 4, 4), + }); + + var addItem = CreateContextMenuItem("\uE710", "새 프리셋 추가", primaryText, secondaryText); + addItem.MouseLeftButtonDown += (_, _) => + { + popup.IsOpen = false; + ShowCustomPresetDialog(null); + }; + stack.Children.Add(addItem); + + sv.Content = stack; + outer.Child = sv; + popup.Child = outer; + popup.IsOpen = true; + } + /// 대화 주제 선택 — 프리셋 시스템 프롬프트 + 카테고리 적용. private void SelectTopic(Services.TopicPreset preset) { diff --git a/src/AxCopilot/Views/ChatWindow.MoodMenu.cs b/src/AxCopilot/Views/ChatWindow.MoodMenu.cs index 71d1b1e..7438448 100644 --- a/src/AxCopilot/Views/ChatWindow.MoodMenu.cs +++ b/src/AxCopilot/Views/ChatWindow.MoodMenu.cs @@ -448,9 +448,11 @@ public partial class ChatWindow 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.ServiceSelectorRequested = () => BtnModelSelector_Click(BtnModelSelector, new System.Windows.RoutedEventArgs()); + SettingsPanel.ModelSelectorRequested = () => BtnModelSelector_Click(BtnModelSelector, new System.Windows.RoutedEventArgs()); SettingsPanel.WorkFolderBrowseRequested = () => BrowseWorkFolder(); + SettingsPanel.PresetSaveRequested = () => ShowCustomPresetDialog(null); + SettingsPanel.PresetManageRequested = () => ShowPresetManagePopup(); SettingsPanel.IsOpen = true; } } diff --git a/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml b/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml index 0f0297d..2039713 100644 --- a/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml +++ b/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml @@ -351,6 +351,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml.cs b/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml.cs index 223bc16..8e33564 100644 --- a/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml.cs +++ b/src/AxCopilot/Views/Controls/AgentSettingsPanel.xaml.cs @@ -29,6 +29,12 @@ public partial class AgentSettingsPanel : UserControl /// 작업 폴더 탐색 요청 콜백 (ChatWindow.BrowseWorkFolder로 위임). public Action? WorkFolderBrowseRequested; + /// 새 프리셋 저장 요청 콜백 (ChatWindow.ShowCustomPresetDialog(null)로 위임). + public Action? PresetSaveRequested; + + /// 프리셋 관리 요청 콜백 (ChatWindow.ShowPresetManagePopup()으로 위임). + public Action? PresetManageRequested; + /// 설정 패널 열림/닫힘 상태. public bool IsOpen { @@ -190,6 +196,12 @@ public partial class AgentSettingsPanel : UserControl WorkFolderBrowseRequested?.Invoke(); } + private void BtnPresetSave_Click(object sender, System.Windows.Input.MouseButtonEventArgs e) + => PresetSaveRequested?.Invoke(); + + private void BtnPresetManage_Click(object sender, System.Windows.Input.MouseButtonEventArgs e) + => PresetManageRequested?.Invoke(); + private void UpdateWorkFolder(SettingsService settings) { var folder = settings.Settings.Llm.WorkFolder;