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;