[v2.0.0] AgentSettingsPanel 프리셋 섹션 추가

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
This commit is contained in:
2026-04-04 08:44:50 +09:00
parent 23d1534536
commit d67c378389
4 changed files with 240 additions and 2 deletions

View File

@@ -156,6 +156,173 @@ public partial class ChatWindow
return item;
}
/// <summary>현재 탭의 커스텀 프리셋 목록 팝업을 표시합니다.</summary>
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;
}
/// <summary>대화 주제 선택 — 프리셋 시스템 프롬프트 + 카테고리 적용.</summary>
private void SelectTopic(Services.TopicPreset preset)
{

View File

@@ -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;
}
}

View File

@@ -351,6 +351,63 @@
<Border Height="1" Background="{DynamicResource BorderColor}" Margin="0,8,0,16"/>
<!-- ═══ 프리셋 ═══ -->
<TextBlock Text="프리셋" FontSize="13" FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryText}" Margin="0,0,0,8"/>
<!-- 새 프리셋 저장 -->
<Border x:Name="BtnPresetSave"
CornerRadius="8" Padding="10,8" Margin="0,0,0,6" Cursor="Hand"
MouseLeftButtonUp="BtnPresetSave_Click">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="{DynamicResource ItemBackground}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource ItemHoverBackground}"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel Orientation="Horizontal">
<TextBlock Text="&#xE74E;" FontFamily="Segoe MDL2 Assets" FontSize="13"
Foreground="#6366F1" VerticalAlignment="Center"/>
<TextBlock Text=" 새 프리셋 저장" FontSize="12"
Foreground="{DynamicResource PrimaryText}" VerticalAlignment="Center"
Margin="6,0,0,0"/>
</StackPanel>
</Border>
<!-- 프리셋 관리 -->
<Border x:Name="BtnPresetManage"
CornerRadius="8" Padding="10,8" Margin="0,0,0,8" Cursor="Hand"
MouseLeftButtonUp="BtnPresetManage_Click">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="{DynamicResource ItemBackground}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource ItemHoverBackground}"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="&#xE8A4;" FontFamily="Segoe MDL2 Assets" FontSize="13"
Foreground="{DynamicResource SecondaryText}" VerticalAlignment="Center"/>
<TextBlock Text=" 프리셋 관리" FontSize="12"
Foreground="{DynamicResource PrimaryText}" VerticalAlignment="Center"
Margin="6,0,0,0"/>
</StackPanel>
<TextBlock Text="&#xE76C;" FontFamily="Segoe MDL2 Assets" FontSize="11"
Foreground="{DynamicResource SecondaryText}"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
</Grid>
</Border>
<Border Height="1" Background="{DynamicResource BorderColor}" Margin="0,8,0,16"/>
<!-- ═══ 고급 ═══ -->
<TextBlock Text="고급" FontSize="13" FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryText}" Margin="0,0,0,8"/>

View File

@@ -29,6 +29,12 @@ public partial class AgentSettingsPanel : UserControl
/// <summary>작업 폴더 탐색 요청 콜백 (ChatWindow.BrowseWorkFolder로 위임).</summary>
public Action? WorkFolderBrowseRequested;
/// <summary>새 프리셋 저장 요청 콜백 (ChatWindow.ShowCustomPresetDialog(null)로 위임).</summary>
public Action? PresetSaveRequested;
/// <summary>프리셋 관리 요청 콜백 (ChatWindow.ShowPresetManagePopup()으로 위임).</summary>
public Action? PresetManageRequested;
/// <summary>설정 패널 열림/닫힘 상태.</summary>
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;