Files
AX-Copilot-Codex/src/AxCopilot/Views/CommandPaletteWindow.xaml.cs

148 lines
5.8 KiB
C#

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace AxCopilot.Views;
/// <summary>
/// Ctrl+Shift+P로 호출되는 커맨드 팔레트.
/// 모든 기능에 빠르게 접근할 수 있는 통합 명령 검색 창입니다.
/// </summary>
public partial class CommandPaletteWindow : Window
{
private readonly Action<string>? _onExecute;
private readonly List<CommandEntry> _commands = new();
// 향후 키보드 탐색용
// private int _selectedIndex = -1;
public string? SelectedCommand { get; private set; }
public CommandPaletteWindow(Action<string>? onExecute = null)
{
InitializeComponent();
_onExecute = onExecute;
RegisterCommands();
RenderItems("");
Loaded += (_, _) => { SearchBox.Focus(); };
}
private void RegisterCommands()
{
// 탭 전환
_commands.Add(new("Chat 탭으로 전환", "탭 전환", "\uE8BD", "tab:chat"));
_commands.Add(new("Cowork 탭으로 전환", "탭 전환", "\uE8BD", "tab:cowork"));
_commands.Add(new("Code 탭으로 전환", "탭 전환", "\uE8BD", "tab:code"));
// 대화 관리
_commands.Add(new("새 대화 시작", "대화", "\uE710", "new_conversation"));
_commands.Add(new("대화 검색", "대화", "\uE721", "search_conversation"));
// 모델 변경
_commands.Add(new("모델 변경", "설정", "\uEA86", "change_model"));
// 프리셋
_commands.Add(new("프리셋 선택", "프리셋", "\uE71D", "select_preset"));
// 설정
_commands.Add(new("설정 열기", "앱", "\uE713", "open_settings"));
_commands.Add(new("테마 변경", "앱", "\uE790", "change_theme"));
_commands.Add(new("통계 보기", "앱", "\uE9F9", "open_statistics"));
// 파일
_commands.Add(new("작업 폴더 변경", "파일", "\uED25", "change_folder"));
_commands.Add(new("파일 탐색기 열기", "파일", "\uE8B7", "open_file_explorer"));
// 에이전트
_commands.Add(new("개발자 모드 토글", "에이전트", "\uE71C", "toggle_devmode"));
_commands.Add(new("감사 로그 보기", "에이전트", "\uE9D9", "open_audit_log"));
// 도구
_commands.Add(new("클립보드에서 붙여넣기", "도구", "\uE77F", "paste_clipboard"));
_commands.Add(new("대화 내보내기", "도구", "\uE78C", "export_conversation"));
}
private void RenderItems(string query)
{
ResultPanel.Children.Clear();
var filtered = string.IsNullOrWhiteSpace(query)
? _commands
: _commands.Where(c =>
c.Label.Contains(query, StringComparison.OrdinalIgnoreCase) ||
c.Category.Contains(query, StringComparison.OrdinalIgnoreCase) ||
(Core.FuzzyEngine.IsChosung(query) && Core.FuzzyEngine.ContainsChosung(c.Label, query))).ToList();
for (int i = 0; i < filtered.Count; i++)
{
var cmd = filtered[i];
var idx = i;
var sp = new StackPanel { Orientation = Orientation.Horizontal };
sp.Children.Add(new TextBlock
{
Text = cmd.Icon, FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 14, Foreground = FindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue,
VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 10, 0),
});
var textSp = new StackPanel();
textSp.Children.Add(new TextBlock
{
Text = cmd.Label, FontSize = 13,
Foreground = FindResource("PrimaryText") as Brush ?? Brushes.White,
});
textSp.Children.Add(new TextBlock
{
Text = cmd.Category, FontSize = 10.5,
Foreground = FindResource("SecondaryText") as Brush ?? Brushes.Gray,
});
sp.Children.Add(textSp);
var item = new Border
{
Child = sp, Background = Brushes.Transparent,
CornerRadius = new CornerRadius(8), Cursor = Cursors.Hand,
Padding = new Thickness(10, 8, 14, 8), Margin = new Thickness(0, 1, 0, 1),
};
var hoverBg = FindResource("HintBackground") as Brush ?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
item.MouseEnter += (s, _) => { if (s is Border b) b.Background = hoverBg; };
item.MouseLeave += (s, _) => { if (s is Border b) b.Background = Brushes.Transparent; };
item.MouseLeftButtonUp += (_, _) => ExecuteCommand(cmd.CommandId);
ResultPanel.Children.Add(item);
}
}
private void ExecuteCommand(string commandId)
{
SelectedCommand = commandId;
_onExecute?.Invoke(commandId);
Close();
}
private void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
{
RenderItems(SearchBox.Text);
}
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape) { Close(); e.Handled = true; }
else if (e.Key == Key.Enter && ResultPanel.Children.Count > 0)
{
// 첫 번째 항목 실행
var filtered = string.IsNullOrWhiteSpace(SearchBox.Text)
? _commands : _commands.Where(c =>
c.Label.Contains(SearchBox.Text, StringComparison.OrdinalIgnoreCase) ||
c.Category.Contains(SearchBox.Text, StringComparison.OrdinalIgnoreCase)).ToList();
if (filtered.Count > 0) ExecuteCommand(filtered[0].CommandId);
e.Handled = true;
}
}
private void Window_Deactivated(object sender, EventArgs e) => Close();
private record CommandEntry(string Label, string Category, string Icon, string CommandId);
}