using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; using AxCopilot.Models; using AxCopilot.Services; using AxCopilot.Services.Agent; namespace AxCopilot.Views; public partial class ChatWindow { // ─── 커스텀 프리셋 관리 ───────────────────────────────────────────── /// 커스텀 프리셋 추가 다이얼로그를 표시합니다. private void ShowCustomPresetDialog(Models.CustomPresetEntry? existing = null) { bool isEdit = existing != null; var dlg = new CustomPresetDialog( existingName: existing?.Label ?? "", existingDesc: existing?.Description ?? "", existingPrompt: existing?.SystemPrompt ?? "", existingColor: existing?.Color ?? "#6366F1", existingSymbol: existing?.Symbol ?? "\uE713", existingTab: existing?.Tab ?? _activeTab) { Owner = this, }; if (dlg.ShowDialog() == true) { if (isEdit) { existing!.Label = dlg.PresetName; existing.Description = dlg.PresetDescription; existing.SystemPrompt = dlg.PresetSystemPrompt; existing.Color = dlg.PresetColor; existing.Symbol = dlg.PresetSymbol; existing.Tab = dlg.PresetTab; } else { Llm.CustomPresets.Add(new Models.CustomPresetEntry { Label = dlg.PresetName, Description = dlg.PresetDescription, SystemPrompt = dlg.PresetSystemPrompt, Color = dlg.PresetColor, Symbol = dlg.PresetSymbol, Tab = dlg.PresetTab, }); } _settings.Save(); BuildTopicButtons(); } } /// 커스텀 프리셋 우클릭 컨텍스트 메뉴를 표시합니다. private void ShowCustomPresetContextMenu(Border? anchor, Services.TopicPreset preset) { if (anchor == null || preset.CustomId == null) return; var popup = new System.Windows.Controls.Primitives.Popup { PlacementTarget = anchor, Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom, StaysOpen = false, AllowsTransparency = true, }; var menuBg = ThemeResourceHelper.Background(this); var primaryText = ThemeResourceHelper.Primary(this); var secondaryText = ThemeResourceHelper.Secondary(this); var borderBrush = ThemeResourceHelper.Border(this); var menuBorder = new Border { Background = menuBg, CornerRadius = new CornerRadius(10), BorderBrush = borderBrush, BorderThickness = new Thickness(1), Padding = new Thickness(4), MinWidth = 120, Effect = new System.Windows.Media.Effects.DropShadowEffect { BlurRadius = 12, ShadowDepth = 2, Opacity = 0.3, Color = Colors.Black, }, }; var stack = new StackPanel(); // 편집 버튼 var editItem = CreateContextMenuItem("\uE70F", "편집", primaryText, secondaryText); editItem.MouseLeftButtonDown += (_, _) => { popup.IsOpen = false; var entry = Llm.CustomPresets.FirstOrDefault(c => c.Id == preset.CustomId); if (entry != null) ShowCustomPresetDialog(entry); }; stack.Children.Add(editItem); // 삭제 버튼 var deleteItem = CreateContextMenuItem("\uE74D", "삭제", new SolidColorBrush(Color.FromRgb(0xEF, 0x44, 0x44)), secondaryText); deleteItem.MouseLeftButtonDown += (_, _) => { popup.IsOpen = false; var result = CustomMessageBox.Show( $"'{preset.Label}' 프리셋을 삭제하시겠습니까?", "프리셋 삭제", MessageBoxButton.YesNo, MessageBoxImage.Question); if (result == MessageBoxResult.Yes) { Llm.CustomPresets.RemoveAll(c => c.Id == preset.CustomId); _settings.Save(); BuildTopicButtons(); } }; stack.Children.Add(deleteItem); menuBorder.Child = stack; popup.Child = menuBorder; popup.IsOpen = true; } /// 컨텍스트 메뉴 항목을 생성합니다. private Border CreateContextMenuItem(string icon, string label, Brush fg, Brush secondaryFg) { var item = new Border { Background = Brushes.Transparent, CornerRadius = new CornerRadius(6), Padding = new Thickness(10, 6, 14, 6), Cursor = Cursors.Hand, }; var sp = new StackPanel { Orientation = Orientation.Horizontal }; sp.Children.Add(new TextBlock { Text = icon, FontFamily = ThemeResourceHelper.SegoeMdl2, FontSize = 13, Foreground = fg, VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 8, 0), }); sp.Children.Add(new TextBlock { Text = label, FontSize = 13, Foreground = fg, VerticalAlignment = VerticalAlignment.Center, }); item.Child = sp; var hoverBg = ThemeResourceHelper.HoverBg(this); item.MouseEnter += (s, _) => { if (s is Border b) b.Background = hoverBg; }; item.MouseLeave += (s, _) => { if (s is Border b) b.Background = Brushes.Transparent; }; return item; } /// 대화 주제 선택 — 프리셋 시스템 프롬프트 + 카테고리 적용. private void SelectTopic(Services.TopicPreset preset) { bool hasMessages; lock (_convLock) hasMessages = _currentConversation?.Messages.Count > 0; // 입력란에 텍스트가 있으면 기존 대화를 유지 (입력 내용 보존) bool hasInput = !string.IsNullOrEmpty(InputBox.Text); bool keepConversation = hasMessages || hasInput; if (!keepConversation) { // 메시지도 입력 텍스트도 없으면 새 대화 시작 StartNewConversation(); } // 프리셋 적용 (기존 대화에도 프리셋 변경 가능) lock (_convLock) { if (_currentConversation != null) { _currentConversation.SystemCommand = preset.SystemPrompt; _currentConversation.Category = preset.Category; } } if (!keepConversation) EmptyState.Visibility = Visibility.Collapsed; InputBox.Focus(); if (!string.IsNullOrEmpty(preset.Placeholder)) { _promptCardPlaceholder = preset.Placeholder; if (!keepConversation) ShowPlaceholder(); } if (keepConversation) ShowToast($"프리셋 변경: {preset.Label}"); // Cowork 탭: 하단 바 갱신 if (_activeTab == "Cowork") BuildBottomBar(); } }