using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Forms; using System.Windows.Input; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Effects; using AxCopilot.Services.Agent; using Microsoft.Win32; namespace AxCopilot.Views; public class SkillGalleryWindow : Window, IComponentConnector { private string _selectedCategory = "전체"; internal Border BtnCloseGallery; internal StackPanel CategoryFilterBar; internal StackPanel SkillListPanel; internal TextBlock GalleryStatus; private bool _contentLoaded; public SkillGalleryWindow() { InitializeComponent(); base.Loaded += delegate { BuildCategoryFilter(); RenderSkills(); }; } private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) Point position = e.GetPosition(this); if (!(((Point)(ref position)).X > base.ActualWidth - 160.0)) { if (e.ClickCount == 2) { base.WindowState = ((base.WindowState != WindowState.Maximized) ? WindowState.Maximized : WindowState.Normal); } else { DragMove(); } } } private void BtnClose_Click(object sender, MouseButtonEventArgs e) { Close(); } private void BtnAddSkill_Click(object sender, MouseButtonEventArgs e) { SkillEditorWindow skillEditorWindow = new SkillEditorWindow { Owner = this }; if (skillEditorWindow.ShowDialog() == true) { BuildCategoryFilter(); RenderSkills(); } } private void BtnImport_Click(object sender, MouseButtonEventArgs e) { Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog { Filter = "스킬 패키지 (*.zip)|*.zip", Title = "가져올 스킬 zip 파일을 선택하세요" }; if (openFileDialog.ShowDialog() == true) { int num = SkillService.ImportSkills(openFileDialog.FileName); if (num > 0) { CustomMessageBox.Show($"스킬 {num}개를 성공적으로 가져왔습니다.", "스킬 가져오기"); BuildCategoryFilter(); RenderSkills(); } else { CustomMessageBox.Show("스킬 가져오기에 실패했습니다.", "스킬 가져오기"); } } } private void BuildCategoryFilter() { CategoryFilterBar.Children.Clear(); IReadOnlyList skills = SkillService.Skills; List list = new string[1] { "전체" }.Concat(skills.Select((SkillDefinition s) => string.IsNullOrEmpty(s.Requires) ? "내장" : "고급 (런타임)").Distinct()).ToList(); string userFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "skills"); if (skills.Any((SkillDefinition s) => s.FilePath.StartsWith(userFolder, StringComparison.OrdinalIgnoreCase)) && !list.Contains("사용자")) { list.Add("사용자"); } foreach (string item in list) { Border border = new Border(); border.Tag = item; border.CornerRadius = new CornerRadius(8.0); border.Padding = new Thickness(14.0, 5.0, 14.0, 5.0); border.Margin = new Thickness(0.0, 0.0, 6.0, 0.0); border.Background = ((item == _selectedCategory) ? new SolidColorBrush(Color.FromRgb(75, 94, 252)) : ((TryFindResource("ItemBackground") as Brush) ?? Brushes.Transparent)); border.Cursor = System.Windows.Input.Cursors.Hand; Border border2 = border; border2.Child = new TextBlock { Text = item, FontSize = 12.0, FontWeight = FontWeights.SemiBold, Foreground = ((item == _selectedCategory) ? Brushes.White : ((TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray)) }; border2.MouseLeftButtonUp += delegate(object s, MouseButtonEventArgs _) { if (s is Border { Tag: string tag }) { _selectedCategory = tag; BuildCategoryFilter(); RenderSkills(); } }; CategoryFilterBar.Children.Add(border2); } } private void RenderSkills() { SkillListPanel.Children.Clear(); List list = FilterSkills(SkillService.Skills); string userFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "skills"); foreach (SkillDefinition item in list) { SkillListPanel.Children.Add(BuildSkillCard(item, userFolder)); } if (list.Count == 0) { SkillListPanel.Children.Add(new TextBlock { Text = "표시할 스킬이 없습니다.", FontSize = 13.0, Foreground = ((TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray), Margin = new Thickness(0.0, 20.0, 0.0, 0.0), HorizontalAlignment = System.Windows.HorizontalAlignment.Center }); } int count = SkillService.Skills.Count; int num = SkillService.Skills.Count((SkillDefinition s) => s.IsAvailable); GalleryStatus.Text = $"총 {count}개 스킬 | 사용 가능 {num}개 | 런타임 필요 {count - num}개"; } private List FilterSkills(IReadOnlyList skills) { string userFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "skills"); string selectedCategory = _selectedCategory; if (1 == 0) { } List result = selectedCategory switch { "내장" => skills.Where((SkillDefinition s) => string.IsNullOrEmpty(s.Requires) && !s.FilePath.StartsWith(userFolder, StringComparison.OrdinalIgnoreCase)).ToList(), "고급 (런타임)" => skills.Where((SkillDefinition s) => !string.IsNullOrEmpty(s.Requires)).ToList(), "사용자" => skills.Where((SkillDefinition s) => s.FilePath.StartsWith(userFolder, StringComparison.OrdinalIgnoreCase)).ToList(), _ => skills.ToList(), }; if (1 == 0) { } return result; } private Border BuildSkillCard(SkillDefinition skill, string userFolder) { Brush bgBrush = (TryFindResource("ItemBackground") as Brush) ?? Brushes.Transparent; Brush foreground = (TryFindResource("PrimaryText") as Brush) ?? Brushes.White; Brush brush = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray; bool isUser = skill.FilePath.StartsWith(userFolder, StringComparison.OrdinalIgnoreCase); bool flag = !string.IsNullOrEmpty(skill.Requires); Border card = new Border { Background = bgBrush, CornerRadius = new CornerRadius(10.0), Padding = new Thickness(14.0, 10.0, 14.0, 10.0), Margin = new Thickness(0.0, 0.0, 0.0, 6.0) }; Grid grid = new Grid(); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0, GridUnitType.Auto) }); grid.ColumnDefinitions.Add(new ColumnDefinition()); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0, GridUnitType.Auto) }); Border border = new Border { Width = 38.0, Height = 38.0, CornerRadius = new CornerRadius(10.0), Background = new SolidColorBrush(Color.FromArgb(48, 75, 94, 252)), Margin = new Thickness(0.0, 0.0, 12.0, 0.0), VerticalAlignment = VerticalAlignment.Center }; border.Child = new TextBlock { Text = skill.Icon, FontFamily = new FontFamily("Segoe MDL2 Assets"), FontSize = 16.0, Foreground = (skill.IsAvailable ? new SolidColorBrush(Color.FromRgb(75, 94, 252)) : brush), HorizontalAlignment = System.Windows.HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center }; Grid.SetColumn(border, 0); StackPanel stackPanel = new StackPanel { VerticalAlignment = VerticalAlignment.Center }; StackPanel stackPanel2 = new StackPanel { Orientation = System.Windows.Controls.Orientation.Horizontal, Margin = new Thickness(0.0, 0.0, 0.0, 3.0) }; stackPanel2.Children.Add(new TextBlock { Text = "/" + skill.Name, FontSize = 13.0, FontWeight = FontWeights.SemiBold, FontFamily = new FontFamily("Consolas"), Foreground = (skill.IsAvailable ? new SolidColorBrush(Color.FromRgb(75, 94, 252)) : brush), Opacity = (skill.IsAvailable ? 1.0 : 0.6), VerticalAlignment = VerticalAlignment.Center }); stackPanel2.Children.Add(new TextBlock { Text = " " + skill.Label, FontSize = 12.5, Foreground = foreground, VerticalAlignment = VerticalAlignment.Center }); if (isUser) { stackPanel2.Children.Add(MakeBadge("사용자", "#34D399")); } else if (flag) { stackPanel2.Children.Add(MakeBadge("고급", "#A78BFA")); } else { stackPanel2.Children.Add(MakeBadge("내장", "#9CA3AF")); } if (!skill.IsAvailable) { stackPanel2.Children.Add(MakeBadge(skill.UnavailableHint, "#F87171")); } stackPanel.Children.Add(stackPanel2); stackPanel.Children.Add(new TextBlock { Text = skill.Description, FontSize = 11.5, Foreground = brush, TextWrapping = TextWrapping.Wrap, Opacity = (skill.IsAvailable ? 1.0 : 0.6) }); Grid.SetColumn(stackPanel, 1); StackPanel actions = new StackPanel { Orientation = System.Windows.Controls.Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(8.0, 0.0, 0.0, 0.0) }; actions.Children.Add(MakeActionBtn("\ue70f", "#3B82F6", isUser ? "편집 (시각적 편집기)" : "편집 (파일 열기)", delegate { if (isUser) { SkillEditorWindow skillEditorWindow = new SkillEditorWindow(skill) { Owner = this }; if (skillEditorWindow.ShowDialog() == true) { SkillService.LoadSkills(); BuildCategoryFilter(); RenderSkills(); } return; } try { Process.Start(new ProcessStartInfo(skill.FilePath) { UseShellExecute = true }); } catch (Exception ex) { CustomMessageBox.Show("파일을 열 수 없습니다: " + ex.Message, "편집"); } })); actions.Children.Add(MakeActionBtn("\ue8c8", "#10B981", "복제", delegate { try { string text = Path.Combine(userFolder); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(skill.FilePath); string text2 = Path.Combine(text, fileNameWithoutExtension + "_copy.skill.md"); int num = 2; while (File.Exists(text2)) { text2 = Path.Combine(text, $"{fileNameWithoutExtension}_copy{num++}.skill.md"); } File.Copy(skill.FilePath, text2); SkillService.LoadSkills(); BuildCategoryFilter(); RenderSkills(); } catch (Exception ex) { CustomMessageBox.Show("복제 실패: " + ex.Message, "복제"); } })); actions.Children.Add(MakeActionBtn("\uede1", "#F59E0B", "내보내기 (.zip)", delegate { FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog { Description = "내보낼 폴더를 선택하세요" }; if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { string text = SkillService.ExportSkill(skill, folderBrowserDialog.SelectedPath); if (text != null) { CustomMessageBox.Show("내보내기 완료:\n" + text, "내보내기"); } else { CustomMessageBox.Show("내보내기에 실패했습니다.", "내보내기"); } } })); if (isUser) { actions.Children.Add(MakeActionBtn("\ue74d", "#F87171", "삭제", delegate { MessageBoxResult messageBoxResult = CustomMessageBox.Show("스킬 '/" + skill.Name + "'을 삭제하시겠습니까?", "스킬 삭제", MessageBoxButton.YesNo, MessageBoxImage.Question); if (messageBoxResult != MessageBoxResult.Yes) { return; } try { File.Delete(skill.FilePath); SkillService.LoadSkills(); BuildCategoryFilter(); RenderSkills(); } catch (Exception ex) { CustomMessageBox.Show("삭제 실패: " + ex.Message, "삭제"); } })); } Grid.SetColumn(actions, 2); grid.Children.Add(border); grid.Children.Add(stackPanel); grid.Children.Add(actions); card.Child = grid; card.Cursor = System.Windows.Input.Cursors.Hand; card.MouseEnter += delegate { card.Background = new SolidColorBrush(Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue)); }; card.MouseLeave += delegate { card.Background = bgBrush; }; card.MouseLeftButtonUp += delegate(object _, MouseButtonEventArgs e) { if (e.OriginalSource is FrameworkElement frameworkElement) { for (FrameworkElement frameworkElement2 = frameworkElement; frameworkElement2 != null; frameworkElement2 = VisualTreeHelper.GetParent((DependencyObject)(object)frameworkElement2) as FrameworkElement) { if (frameworkElement2 == actions) { return; } } } ShowSkillDetail(skill); }; return card; } private Border MakeBadge(string text, string colorHex) { Color color = (Color)ColorConverter.ConvertFromString(colorHex); return new Border { Background = new SolidColorBrush(Color.FromArgb(37, color.R, color.G, color.B)), CornerRadius = new CornerRadius(4.0), Padding = new Thickness(5.0, 1.0, 5.0, 1.0), Margin = new Thickness(6.0, 0.0, 0.0, 0.0), VerticalAlignment = VerticalAlignment.Center, Child = new TextBlock { Text = text, FontSize = 9.5, FontWeight = FontWeights.SemiBold, Foreground = new SolidColorBrush(color) } }; } private Border MakeActionBtn(string icon, string colorHex, string tooltip, Action action) { Color col = (Color)ColorConverter.ConvertFromString(colorHex); Border btn = new Border { Width = 28.0, Height = 28.0, CornerRadius = new CornerRadius(6.0), Background = Brushes.Transparent, Cursor = System.Windows.Input.Cursors.Hand, Margin = new Thickness(2.0, 0.0, 0.0, 0.0), ToolTip = tooltip, Child = new TextBlock { Text = icon, FontFamily = new FontFamily("Segoe MDL2 Assets"), FontSize = 12.0, Foreground = new SolidColorBrush(col), HorizontalAlignment = System.Windows.HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center } }; btn.MouseEnter += delegate { btn.Background = new SolidColorBrush(Color.FromArgb(32, col.R, col.G, col.B)); }; btn.MouseLeave += delegate { btn.Background = Brushes.Transparent; }; btn.MouseLeftButtonUp += delegate { action(); }; return btn; } private void ShowSkillDetail(SkillDefinition skill) { Brush background = (TryFindResource("LauncherBackground") as Brush) ?? Brushes.Black; Brush fgBrush = (TryFindResource("PrimaryText") as Brush) ?? Brushes.White; Brush subBrush = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray; Brush background2 = (TryFindResource("ItemBackground") as Brush) ?? Brushes.Transparent; Brush brush = (TryFindResource("BorderColor") as Brush) ?? Brushes.Gray; Window popup = new Window { Title = "/" + skill.Name, Width = 580.0, Height = 480.0, WindowStyle = WindowStyle.None, AllowsTransparency = true, Background = Brushes.Transparent, WindowStartupLocation = WindowStartupLocation.CenterOwner, Owner = this }; Border border = new Border { Background = background, CornerRadius = new CornerRadius(12.0), BorderBrush = brush, BorderThickness = new Thickness(1.0, 1.0, 1.0, 1.0), Effect = new DropShadowEffect { BlurRadius = 20.0, ShadowDepth = 4.0, Opacity = 0.3, Color = Colors.Black } }; Grid grid = new Grid(); grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(44.0) }); grid.RowDefinitions.Add(new RowDefinition()); Border border2 = new Border { CornerRadius = new CornerRadius(12.0, 12.0, 0.0, 0.0), Background = background2 }; border2.MouseLeftButtonDown += delegate(object _, MouseButtonEventArgs e) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) Point position = e.GetPosition(popup); if (!(((Point)(ref position)).X > popup.ActualWidth - 50.0)) { popup.DragMove(); } }; Grid grid2 = new Grid { Margin = new Thickness(16.0, 0.0, 12.0, 0.0) }; StackPanel stackPanel = new StackPanel { Orientation = System.Windows.Controls.Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center }; stackPanel.Children.Add(new TextBlock { Text = skill.Icon, FontFamily = new FontFamily("Segoe MDL2 Assets"), FontSize = 14.0, Foreground = new SolidColorBrush(Color.FromRgb(75, 94, 252)), VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0.0, 1.0, 8.0, 0.0) }); stackPanel.Children.Add(new TextBlock { Text = "/" + skill.Name + " " + skill.Label, FontSize = 14.0, FontWeight = FontWeights.SemiBold, Foreground = fgBrush, VerticalAlignment = VerticalAlignment.Center }); grid2.Children.Add(stackPanel); Border border3 = new Border { Width = 28.0, Height = 28.0, CornerRadius = new CornerRadius(6.0), Cursor = System.Windows.Input.Cursors.Hand, HorizontalAlignment = System.Windows.HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center }; border3.Child = new TextBlock { Text = "\ue8bb", FontFamily = new FontFamily("Segoe MDL2 Assets"), FontSize = 10.0, Foreground = subBrush, HorizontalAlignment = System.Windows.HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center }; border3.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _) { ((Border)s).Background = new SolidColorBrush(Color.FromArgb(51, byte.MaxValue, 68, 68)); }; border3.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _) { ((Border)s).Background = Brushes.Transparent; }; border3.MouseLeftButtonUp += delegate { popup.Close(); }; grid2.Children.Add(border3); border2.Child = grid2; Grid.SetRow(border2, 0); ScrollViewer scrollViewer = new ScrollViewer { VerticalScrollBarVisibility = ScrollBarVisibility.Auto, Padding = new Thickness(20.0, 16.0, 20.0, 16.0) }; StackPanel stackPanel2 = new StackPanel(); Grid metaGrid = new Grid { Margin = new Thickness(0.0, 0.0, 0.0, 14.0) }; metaGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80.0) }); metaGrid.ColumnDefinitions.Add(new ColumnDefinition()); int num = 0; AddMetaRow("명령어", "/" + skill.Name, num++); AddMetaRow("라벨", skill.Label, num++); AddMetaRow("설명", skill.Description, num++); if (!string.IsNullOrEmpty(skill.Requires)) { AddMetaRow("런타임", skill.Requires, num++); } if (!string.IsNullOrEmpty(skill.AllowedTools)) { AddMetaRow("허용 도구", skill.AllowedTools, num++); } AddMetaRow("상태", skill.IsAvailable ? "✓ 사용 가능" : ("✗ " + skill.UnavailableHint), num++); AddMetaRow("경로", skill.FilePath, num++); stackPanel2.Children.Add(metaGrid); stackPanel2.Children.Add(new Border { Height = 1.0, Background = brush, Margin = new Thickness(0.0, 4.0, 0.0, 12.0) }); stackPanel2.Children.Add(new TextBlock { Text = "시스템 프롬프트 (미리보기)", FontSize = 11.0, FontWeight = FontWeights.SemiBold, Foreground = subBrush, Margin = new Thickness(0.0, 0.0, 0.0, 6.0) }); Border border4 = new Border { Background = background2, CornerRadius = new CornerRadius(8.0), Padding = new Thickness(12.0, 10.0, 12.0, 10.0) }; string text = skill.SystemPrompt; if (text.Length > 2000) { text = text.Substring(0, 2000) + "\n\n... (이하 생략)"; } border4.Child = new TextBlock { Text = text, FontSize = 11.5, FontFamily = new FontFamily("Consolas, Cascadia Code, Segoe UI"), Foreground = fgBrush, TextWrapping = TextWrapping.Wrap, Opacity = 0.85 }; stackPanel2.Children.Add(border4); scrollViewer.Content = stackPanel2; Grid.SetRow(scrollViewer, 1); grid.Children.Add(border2); grid.Children.Add(scrollViewer); border.Child = grid; popup.Content = border; popup.ShowDialog(); void AddMetaRow(string label, string value, int row) { if (!string.IsNullOrEmpty(value)) { metaGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto }); TextBlock element = new TextBlock { Text = label, FontSize = 11.5, Foreground = subBrush, Margin = new Thickness(0.0, 2.0, 0.0, 2.0) }; Grid.SetRow(element, row); Grid.SetColumn(element, 0); metaGrid.Children.Add(element); TextBlock element2 = new TextBlock { Text = value, FontSize = 11.5, Foreground = fgBrush, TextWrapping = TextWrapping.Wrap, Margin = new Thickness(0.0, 2.0, 0.0, 2.0) }; Grid.SetRow(element2, row); Grid.SetColumn(element2, 1); metaGrid.Children.Add(element2); } } } [DebuggerNonUserCode] [GeneratedCode("PresentationBuildTasks", "10.0.5.0")] public void InitializeComponent() { if (!_contentLoaded) { _contentLoaded = true; Uri resourceLocator = new Uri("/AxCopilot;component/views/skillgallerywindow.xaml", UriKind.Relative); System.Windows.Application.LoadComponent(this, resourceLocator); } } [DebuggerNonUserCode] [GeneratedCode("PresentationBuildTasks", "10.0.5.0")] [EditorBrowsable(EditorBrowsableState.Never)] void IComponentConnector.Connect(int connectionId, object target) { switch (connectionId) { case 1: ((Border)target).MouseLeftButtonDown += TitleBar_MouseLeftButtonDown; break; case 2: ((Border)target).MouseLeftButtonUp += BtnAddSkill_Click; break; case 3: ((Border)target).MouseLeftButtonUp += BtnImport_Click; break; case 4: BtnCloseGallery = (Border)target; BtnCloseGallery.MouseLeftButtonUp += BtnClose_Click; break; case 5: CategoryFilterBar = (StackPanel)target; break; case 6: SkillListPanel = (StackPanel)target; break; case 7: GalleryStatus = (TextBlock)target; break; default: _contentLoaded = true; break; } } }