636 lines
25 KiB
C#
636 lines
25 KiB
C#
using System.IO;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Input;
|
|
using System.Windows.Media;
|
|
using AxCopilot.Services.Agent;
|
|
|
|
namespace AxCopilot.Views;
|
|
|
|
/// <summary>스킬 갤러리 창. 설치된 모든 스킬을 카드 형태로 표시하고 관리합니다.</summary>
|
|
public partial class SkillGalleryWindow : Window
|
|
{
|
|
private string _selectedCategory = "전체";
|
|
|
|
public SkillGalleryWindow()
|
|
{
|
|
InitializeComponent();
|
|
Loaded += (_, _) => { BuildCategoryFilter(); RenderSkills(); };
|
|
}
|
|
|
|
// ─── 타이틀바 ─────────────────────────────────────────────────────────
|
|
|
|
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
|
{
|
|
// 타이틀바 우측 버튼 영역에서는 DragMove 실행하지 않음
|
|
var pos = e.GetPosition(this);
|
|
if (pos.X > ActualWidth - 160) return; // 우측 버튼 영역 보호
|
|
|
|
if (e.ClickCount == 2)
|
|
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
|
|
else DragMove();
|
|
}
|
|
|
|
private void BtnClose_Click(object sender, MouseButtonEventArgs e) => Close();
|
|
|
|
// ─── 버튼 ─────────────────────────────────────────────────────────────
|
|
|
|
private void BtnAddSkill_Click(object sender, MouseButtonEventArgs e)
|
|
{
|
|
var editor = new SkillEditorWindow { Owner = this };
|
|
if (editor.ShowDialog() == true)
|
|
{
|
|
BuildCategoryFilter();
|
|
RenderSkills();
|
|
}
|
|
}
|
|
|
|
private void BtnImport_Click(object sender, MouseButtonEventArgs e)
|
|
{
|
|
var dlg = new Microsoft.Win32.OpenFileDialog
|
|
{
|
|
Filter = "스킬 패키지 (*.zip)|*.zip",
|
|
Title = "가져올 스킬 zip 파일을 선택하세요",
|
|
};
|
|
if (dlg.ShowDialog() != true) return;
|
|
|
|
var count = SkillService.ImportSkills(dlg.FileName);
|
|
if (count > 0)
|
|
{
|
|
CustomMessageBox.Show($"스킬 {count}개를 성공적으로 가져왔습니다.", "스킬 가져오기");
|
|
BuildCategoryFilter();
|
|
RenderSkills();
|
|
}
|
|
else
|
|
CustomMessageBox.Show("스킬 가져오기에 실패했습니다.", "스킬 가져오기");
|
|
}
|
|
|
|
// ─── 카테고리 필터 ─────────────────────────────────────────────────────
|
|
|
|
private void BuildCategoryFilter()
|
|
{
|
|
CategoryFilterBar.Children.Clear();
|
|
var skills = SkillService.Skills;
|
|
|
|
var categories = new[] { "전체" }
|
|
.Concat(skills
|
|
.Select(s => string.IsNullOrEmpty(s.Requires) ? "내장" : "고급 (런타임)")
|
|
.Distinct())
|
|
.ToList();
|
|
|
|
// 사용자 스킬이 있으면 추가
|
|
var userFolder = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
"AxCopilot", "skills");
|
|
var hasUser = skills.Any(s =>
|
|
s.FilePath.StartsWith(userFolder, StringComparison.OrdinalIgnoreCase));
|
|
if (hasUser && !categories.Contains("사용자"))
|
|
categories.Add("사용자");
|
|
|
|
foreach (var cat in categories)
|
|
{
|
|
var btn = new Border
|
|
{
|
|
Tag = cat,
|
|
CornerRadius = new CornerRadius(8),
|
|
Padding = new Thickness(14, 5, 14, 5),
|
|
Margin = new Thickness(0, 0, 6, 0),
|
|
Background = cat == _selectedCategory
|
|
? new SolidColorBrush(Color.FromRgb(0x4B, 0x5E, 0xFC))
|
|
: TryFindResource("ItemBackground") as Brush ?? Brushes.Transparent,
|
|
Cursor = Cursors.Hand,
|
|
};
|
|
btn.Child = new TextBlock
|
|
{
|
|
Text = cat,
|
|
FontSize = 12,
|
|
FontWeight = FontWeights.SemiBold,
|
|
Foreground = cat == _selectedCategory
|
|
? Brushes.White
|
|
: TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
|
|
};
|
|
btn.MouseLeftButtonUp += (s, _) =>
|
|
{
|
|
if (s is Border b && b.Tag is string tag)
|
|
{
|
|
_selectedCategory = tag;
|
|
BuildCategoryFilter();
|
|
RenderSkills();
|
|
}
|
|
};
|
|
CategoryFilterBar.Children.Add(btn);
|
|
}
|
|
}
|
|
|
|
// ─── 스킬 목록 렌더링 ──────────────────────────────────────────────────
|
|
|
|
private void RenderSkills()
|
|
{
|
|
SkillListPanel.Children.Clear();
|
|
var skills = FilterSkills(SkillService.Skills);
|
|
|
|
var userFolder = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
"AxCopilot", "skills");
|
|
|
|
foreach (var skill in skills)
|
|
SkillListPanel.Children.Add(BuildSkillCard(skill, userFolder));
|
|
|
|
if (skills.Count == 0)
|
|
SkillListPanel.Children.Add(new TextBlock
|
|
{
|
|
Text = "표시할 스킬이 없습니다.",
|
|
FontSize = 13,
|
|
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
|
|
Margin = new Thickness(0, 20, 0, 0),
|
|
HorizontalAlignment = HorizontalAlignment.Center,
|
|
});
|
|
|
|
var total = SkillService.Skills.Count;
|
|
var avail = SkillService.Skills.Count(s => s.IsAvailable);
|
|
GalleryStatus.Text = $"총 {total}개 스킬 | 사용 가능 {avail}개 | 런타임 필요 {total - avail}개";
|
|
}
|
|
|
|
private List<SkillDefinition> FilterSkills(IReadOnlyList<SkillDefinition> skills)
|
|
{
|
|
var userFolder = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
"AxCopilot", "skills");
|
|
|
|
return _selectedCategory switch
|
|
{
|
|
"내장" => skills.Where(s => string.IsNullOrEmpty(s.Requires)
|
|
&& !s.FilePath.StartsWith(userFolder, StringComparison.OrdinalIgnoreCase)).ToList(),
|
|
"고급 (런타임)" => skills.Where(s => !string.IsNullOrEmpty(s.Requires)).ToList(),
|
|
"사용자" => skills.Where(s => s.FilePath.StartsWith(userFolder, StringComparison.OrdinalIgnoreCase)).ToList(),
|
|
_ => skills.ToList(),
|
|
};
|
|
}
|
|
|
|
private Border BuildSkillCard(SkillDefinition skill, string userFolder)
|
|
{
|
|
var bgBrush = TryFindResource("ItemBackground") as Brush ?? Brushes.Transparent;
|
|
var fgBrush = TryFindResource("PrimaryText") as Brush ?? Brushes.White;
|
|
var subBrush = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
|
|
|
var isUser = skill.FilePath.StartsWith(userFolder, StringComparison.OrdinalIgnoreCase);
|
|
var isAdvanced = !string.IsNullOrEmpty(skill.Requires);
|
|
|
|
var card = new Border
|
|
{
|
|
Background = bgBrush,
|
|
CornerRadius = new CornerRadius(10),
|
|
Padding = new Thickness(14, 10, 14, 10),
|
|
Margin = new Thickness(0, 0, 0, 6),
|
|
};
|
|
|
|
var grid = new Grid();
|
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) });
|
|
grid.ColumnDefinitions.Add(new ColumnDefinition());
|
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) });
|
|
|
|
// ── 아이콘 원 ──
|
|
var iconBorder = new Border
|
|
{
|
|
Width = 38,
|
|
Height = 38,
|
|
CornerRadius = new CornerRadius(10),
|
|
Background = new SolidColorBrush(Color.FromArgb(0x30, 0x4B, 0x5E, 0xFC)),
|
|
Margin = new Thickness(0, 0, 12, 0),
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
};
|
|
iconBorder.Child = new TextBlock
|
|
{
|
|
Text = skill.Icon,
|
|
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
|
FontSize = 16,
|
|
Foreground = skill.IsAvailable
|
|
? new SolidColorBrush(Color.FromRgb(0x4B, 0x5E, 0xFC))
|
|
: subBrush,
|
|
HorizontalAlignment = HorizontalAlignment.Center,
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
};
|
|
Grid.SetColumn(iconBorder, 0);
|
|
|
|
// ── 정보 ──
|
|
var infoPanel = new StackPanel { VerticalAlignment = VerticalAlignment.Center };
|
|
|
|
// 이름 + 뱃지들
|
|
var nameRow = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 0, 0, 3) };
|
|
nameRow.Children.Add(new TextBlock
|
|
{
|
|
Text = $"/{skill.Name}",
|
|
FontSize = 13,
|
|
FontWeight = FontWeights.SemiBold,
|
|
FontFamily = new FontFamily("Consolas"),
|
|
Foreground = skill.IsAvailable
|
|
? new SolidColorBrush(Color.FromRgb(0x4B, 0x5E, 0xFC))
|
|
: subBrush,
|
|
Opacity = skill.IsAvailable ? 1.0 : 0.6,
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
});
|
|
nameRow.Children.Add(new TextBlock
|
|
{
|
|
Text = $" {skill.Label}",
|
|
FontSize = 12.5,
|
|
Foreground = fgBrush,
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
});
|
|
|
|
// 소스/유형 뱃지
|
|
if (isUser)
|
|
nameRow.Children.Add(MakeBadge("사용자", "#34D399"));
|
|
else if (isAdvanced)
|
|
nameRow.Children.Add(MakeBadge("고급", "#A78BFA"));
|
|
else
|
|
nameRow.Children.Add(MakeBadge("내장", "#9CA3AF"));
|
|
if (skill.IsSample)
|
|
nameRow.Children.Add(MakeBadge("예제", "#F59E0B"));
|
|
|
|
// 비가용 뱃지
|
|
if (!skill.IsAvailable)
|
|
nameRow.Children.Add(MakeBadge(skill.UnavailableHint, "#F87171"));
|
|
|
|
infoPanel.Children.Add(nameRow);
|
|
infoPanel.Children.Add(new TextBlock
|
|
{
|
|
Text = skill.Description,
|
|
FontSize = 11.5,
|
|
Foreground = subBrush,
|
|
TextWrapping = TextWrapping.Wrap,
|
|
Opacity = skill.IsAvailable ? 1.0 : 0.6,
|
|
});
|
|
Grid.SetColumn(infoPanel, 1);
|
|
|
|
// ── 액션 버튼들 ──
|
|
var actions = new StackPanel
|
|
{
|
|
Orientation = Orientation.Horizontal,
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
Margin = new Thickness(8, 0, 0, 0),
|
|
};
|
|
|
|
// 편집
|
|
actions.Children.Add(MakeActionBtn("\uE70F", "#3B82F6", isUser ? "편집 (시각적 편집기)" : "편집 (파일 열기)",
|
|
() =>
|
|
{
|
|
if (isUser)
|
|
{
|
|
var editor = new SkillEditorWindow(skill) { Owner = this };
|
|
if (editor.ShowDialog() == true)
|
|
{
|
|
SkillService.LoadSkills();
|
|
BuildCategoryFilter();
|
|
RenderSkills();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
try { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(skill.FilePath) { UseShellExecute = true }); }
|
|
catch (Exception ex) { CustomMessageBox.Show($"파일을 열 수 없습니다: {ex.Message}", "편집"); }
|
|
}
|
|
}));
|
|
|
|
// 복제 (사용자 스킬/폴더 스킬만)
|
|
actions.Children.Add(MakeActionBtn("\uE8C8", "#10B981", "복제",
|
|
() =>
|
|
{
|
|
try
|
|
{
|
|
var destFolder = Path.Combine(userFolder);
|
|
if (!Directory.Exists(destFolder)) Directory.CreateDirectory(destFolder);
|
|
|
|
var srcName = Path.GetFileNameWithoutExtension(skill.FilePath);
|
|
var destPath = Path.Combine(destFolder, $"{srcName}_copy.skill.md");
|
|
var counter = 2;
|
|
while (File.Exists(destPath))
|
|
destPath = Path.Combine(destFolder, $"{srcName}_copy{counter++}.skill.md");
|
|
|
|
File.Copy(skill.FilePath, destPath);
|
|
SkillService.LoadSkills();
|
|
BuildCategoryFilter();
|
|
RenderSkills();
|
|
}
|
|
catch (Exception ex) { CustomMessageBox.Show($"복제 실패: {ex.Message}", "복제"); }
|
|
}));
|
|
|
|
// 내보내기
|
|
actions.Children.Add(MakeActionBtn("\uEDE1", "#F59E0B", "내보내기 (.zip)",
|
|
() =>
|
|
{
|
|
var folderDlg = new System.Windows.Forms.FolderBrowserDialog
|
|
{ Description = "내보낼 폴더를 선택하세요" };
|
|
if (folderDlg.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
|
|
var result = SkillService.ExportSkill(skill, folderDlg.SelectedPath);
|
|
if (result != null)
|
|
CustomMessageBox.Show($"내보내기 완료:\n{result}", "내보내기");
|
|
else
|
|
CustomMessageBox.Show("내보내기에 실패했습니다.", "내보내기");
|
|
}));
|
|
|
|
// 삭제 (사용자 스킬만)
|
|
if (isUser)
|
|
{
|
|
actions.Children.Add(MakeActionBtn("\uE74D", "#F87171", "삭제",
|
|
() =>
|
|
{
|
|
var confirm = CustomMessageBox.Show(
|
|
$"스킬 '/{skill.Name}'을 삭제하시겠습니까?",
|
|
"스킬 삭제",
|
|
MessageBoxButton.YesNo, MessageBoxImage.Question);
|
|
if (confirm != 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(iconBorder);
|
|
grid.Children.Add(infoPanel);
|
|
grid.Children.Add(actions);
|
|
card.Child = grid;
|
|
|
|
// 호버 효과 + 카드 클릭 → 상세 보기
|
|
card.Cursor = Cursors.Hand;
|
|
card.MouseEnter += (_, _) =>
|
|
{
|
|
card.Background = new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
|
|
};
|
|
card.MouseLeave += (_, _) => card.Background = bgBrush;
|
|
card.MouseLeftButtonUp += (_, e) =>
|
|
{
|
|
// 액션 버튼 클릭은 무시 (버블링 방지)
|
|
if (e.OriginalSource is FrameworkElement src)
|
|
{
|
|
var parent = src;
|
|
while (parent != null)
|
|
{
|
|
if (parent == actions) return;
|
|
parent = VisualTreeHelper.GetParent(parent) as FrameworkElement;
|
|
}
|
|
}
|
|
ShowSkillDetail(skill);
|
|
};
|
|
|
|
return card;
|
|
}
|
|
|
|
private Border MakeBadge(string text, string colorHex)
|
|
{
|
|
var col = (Color)ColorConverter.ConvertFromString(colorHex);
|
|
return new Border
|
|
{
|
|
Background = new SolidColorBrush(Color.FromArgb(0x25, col.R, col.G, col.B)),
|
|
CornerRadius = new CornerRadius(4),
|
|
Padding = new Thickness(5, 1, 5, 1),
|
|
Margin = new Thickness(6, 0, 0, 0),
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
Child = new TextBlock
|
|
{
|
|
Text = text,
|
|
FontSize = 9.5,
|
|
FontWeight = FontWeights.SemiBold,
|
|
Foreground = new SolidColorBrush(col),
|
|
},
|
|
};
|
|
}
|
|
|
|
private Border MakeActionBtn(string icon, string colorHex, string tooltip, Action action)
|
|
{
|
|
var col = (Color)ColorConverter.ConvertFromString(colorHex);
|
|
var btn = new Border
|
|
{
|
|
Width = 28,
|
|
Height = 28,
|
|
CornerRadius = new CornerRadius(6),
|
|
Background = Brushes.Transparent,
|
|
Cursor = Cursors.Hand,
|
|
Margin = new Thickness(2, 0, 0, 0),
|
|
ToolTip = tooltip,
|
|
Child = new TextBlock
|
|
{
|
|
Text = icon,
|
|
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
|
FontSize = 12,
|
|
Foreground = new SolidColorBrush(col),
|
|
HorizontalAlignment = HorizontalAlignment.Center,
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
},
|
|
};
|
|
btn.MouseEnter += (_, _) =>
|
|
btn.Background = new SolidColorBrush(Color.FromArgb(0x20, col.R, col.G, col.B));
|
|
btn.MouseLeave += (_, _) => btn.Background = Brushes.Transparent;
|
|
btn.MouseLeftButtonUp += (_, _) => action();
|
|
return btn;
|
|
}
|
|
|
|
// ─── 스킬 상세 보기 팝업 ───────────────────────────────────────────────
|
|
|
|
private void ShowSkillDetail(SkillDefinition skill)
|
|
{
|
|
var bgBrush = TryFindResource("LauncherBackground") as Brush ?? Brushes.Black;
|
|
var fgBrush = TryFindResource("PrimaryText") as Brush ?? Brushes.White;
|
|
var subBrush = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
|
var itemBg = TryFindResource("ItemBackground") as Brush ?? Brushes.Transparent;
|
|
var borderBr = TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
|
|
|
var popup = new Window
|
|
{
|
|
Title = $"/{skill.Name}",
|
|
Width = 580,
|
|
Height = 480,
|
|
WindowStyle = WindowStyle.None,
|
|
AllowsTransparency = true,
|
|
Background = Brushes.Transparent,
|
|
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
|
Owner = this,
|
|
};
|
|
|
|
var outerBorder = new Border
|
|
{
|
|
Background = bgBrush,
|
|
CornerRadius = new CornerRadius(12),
|
|
BorderBrush = borderBr,
|
|
BorderThickness = new Thickness(1, 1, 1, 1),
|
|
Effect = new System.Windows.Media.Effects.DropShadowEffect
|
|
{
|
|
BlurRadius = 20,
|
|
ShadowDepth = 4,
|
|
Opacity = 0.3,
|
|
Color = Colors.Black,
|
|
},
|
|
};
|
|
|
|
var mainGrid = new Grid();
|
|
mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(44) });
|
|
mainGrid.RowDefinitions.Add(new RowDefinition());
|
|
|
|
// ── 타이틀바 ──
|
|
var titleBar = new Border
|
|
{
|
|
CornerRadius = new CornerRadius(12, 12, 0, 0),
|
|
Background = itemBg,
|
|
};
|
|
titleBar.MouseLeftButtonDown += (_, e) =>
|
|
{
|
|
var pos = e.GetPosition(popup);
|
|
if (pos.X > popup.ActualWidth - 50) return;
|
|
popup.DragMove();
|
|
};
|
|
|
|
var titleGrid = new Grid { Margin = new Thickness(16, 0, 12, 0) };
|
|
var titleLeft = new StackPanel { Orientation = Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center };
|
|
titleLeft.Children.Add(new TextBlock
|
|
{
|
|
Text = skill.Icon,
|
|
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
|
FontSize = 14,
|
|
Foreground = new SolidColorBrush(Color.FromRgb(0x4B, 0x5E, 0xFC)),
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
Margin = new Thickness(0, 1, 8, 0),
|
|
});
|
|
titleLeft.Children.Add(new TextBlock
|
|
{
|
|
Text = $"/{skill.Name} {skill.Label}",
|
|
FontSize = 14,
|
|
FontWeight = FontWeights.SemiBold,
|
|
Foreground = fgBrush,
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
});
|
|
titleGrid.Children.Add(titleLeft);
|
|
|
|
var closeBtn = new Border
|
|
{
|
|
Width = 28, Height = 28,
|
|
CornerRadius = new CornerRadius(6),
|
|
Cursor = Cursors.Hand,
|
|
HorizontalAlignment = HorizontalAlignment.Right,
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
};
|
|
closeBtn.Child = new TextBlock
|
|
{
|
|
Text = "\uE8BB",
|
|
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
|
FontSize = 10,
|
|
Foreground = subBrush,
|
|
HorizontalAlignment = HorizontalAlignment.Center,
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
};
|
|
closeBtn.MouseEnter += (s, _) => ((Border)s).Background =
|
|
new SolidColorBrush(Color.FromArgb(0x33, 0xFF, 0x44, 0x44));
|
|
closeBtn.MouseLeave += (s, _) => ((Border)s).Background = Brushes.Transparent;
|
|
closeBtn.MouseLeftButtonUp += (_, _) => popup.Close();
|
|
titleGrid.Children.Add(closeBtn);
|
|
|
|
titleBar.Child = titleGrid;
|
|
Grid.SetRow(titleBar, 0);
|
|
|
|
// ── 본문: 스킬 정보 + 프롬프트 미리보기 ──
|
|
var body = new ScrollViewer
|
|
{
|
|
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
|
|
Padding = new Thickness(20, 16, 20, 16),
|
|
};
|
|
|
|
var bodyPanel = new StackPanel();
|
|
|
|
// 메타 정보
|
|
var metaGrid = new Grid { Margin = new Thickness(0, 0, 0, 14) };
|
|
metaGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80) });
|
|
metaGrid.ColumnDefinitions.Add(new ColumnDefinition());
|
|
|
|
void AddMetaRow(string label, string value, int row)
|
|
{
|
|
if (string.IsNullOrEmpty(value)) return;
|
|
metaGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
|
var lb = new TextBlock
|
|
{
|
|
Text = label, FontSize = 11.5, Foreground = subBrush,
|
|
Margin = new Thickness(0, 2, 0, 2),
|
|
};
|
|
Grid.SetRow(lb, row); Grid.SetColumn(lb, 0);
|
|
metaGrid.Children.Add(lb);
|
|
|
|
var vb = new TextBlock
|
|
{
|
|
Text = value, FontSize = 11.5, Foreground = fgBrush,
|
|
TextWrapping = TextWrapping.Wrap,
|
|
Margin = new Thickness(0, 2, 0, 2),
|
|
};
|
|
Grid.SetRow(vb, row); Grid.SetColumn(vb, 1);
|
|
metaGrid.Children.Add(vb);
|
|
}
|
|
|
|
var metaRow = 0;
|
|
AddMetaRow("명령어", $"/{skill.Name}", metaRow++);
|
|
if (skill.IsSample)
|
|
AddMetaRow("유형", "예제", metaRow++);
|
|
AddMetaRow("라벨", skill.Label, metaRow++);
|
|
AddMetaRow("설명", skill.Description, metaRow++);
|
|
if (!string.IsNullOrEmpty(skill.Requires))
|
|
AddMetaRow("런타임", skill.Requires, metaRow++);
|
|
if (!string.IsNullOrEmpty(skill.AllowedTools))
|
|
AddMetaRow("허용 도구", skill.AllowedTools, metaRow++);
|
|
AddMetaRow("상태", skill.IsAvailable ? "✓ 사용 가능" : $"✗ {skill.UnavailableHint}", metaRow++);
|
|
AddMetaRow("경로", skill.FilePath, metaRow++);
|
|
|
|
bodyPanel.Children.Add(metaGrid);
|
|
|
|
// 구분선
|
|
bodyPanel.Children.Add(new Border
|
|
{
|
|
Height = 1,
|
|
Background = borderBr,
|
|
Margin = new Thickness(0, 4, 0, 12),
|
|
});
|
|
|
|
// 프롬프트 내용 미리보기
|
|
bodyPanel.Children.Add(new TextBlock
|
|
{
|
|
Text = "시스템 프롬프트 (미리보기)",
|
|
FontSize = 11,
|
|
FontWeight = FontWeights.SemiBold,
|
|
Foreground = subBrush,
|
|
Margin = new Thickness(0, 0, 0, 6),
|
|
});
|
|
|
|
var promptBorder = new Border
|
|
{
|
|
Background = itemBg,
|
|
CornerRadius = new CornerRadius(8),
|
|
Padding = new Thickness(12, 10, 12, 10),
|
|
};
|
|
var promptText = skill.SystemPrompt;
|
|
if (promptText.Length > 2000)
|
|
promptText = promptText[..2000] + "\n\n... (이하 생략)";
|
|
|
|
promptBorder.Child = new TextBlock
|
|
{
|
|
Text = promptText,
|
|
FontSize = 11.5,
|
|
FontFamily = new FontFamily("Consolas, Cascadia Code, Segoe UI"),
|
|
Foreground = fgBrush,
|
|
TextWrapping = TextWrapping.Wrap,
|
|
Opacity = 0.85,
|
|
};
|
|
bodyPanel.Children.Add(promptBorder);
|
|
|
|
body.Content = bodyPanel;
|
|
Grid.SetRow(body, 1);
|
|
|
|
mainGrid.Children.Add(titleBar);
|
|
mainGrid.Children.Add(body);
|
|
outerBorder.Child = mainGrid;
|
|
popup.Content = outerBorder;
|
|
|
|
popup.ShowDialog();
|
|
}
|
|
}
|