Files
AX-Copilot-Codex/.decompiledproj/AxCopilot/Views/SkillEditorWindow.cs

630 lines
18 KiB
C#

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Effects;
using AxCopilot.Services;
using AxCopilot.Services.Agent;
namespace AxCopilot.Views;
public class SkillEditorWindow : Window, IComponentConnector
{
private static readonly string[] IconCandidates = new string[20]
{
"\ue70f", "\ue8a5", "\ue943", "\ue74c", "\ue8b7", "\ue896", "\ue713", "\ue753", "\ue774", "\ue8d6",
"\ue8f1", "\ue7c3", "\ueca7", "\ue71e", "\ue8c8", "\ue8f6", "\ue81e", "\uebd2", "\ue9d9", "\ue77b"
};
private string _selectedIcon = "\ue70f";
private SkillDefinition? _editingSkill;
private readonly ToolRegistry _toolRegistry;
internal TextBlock TitleText;
internal TextBox TxtName;
internal TextBox TxtLabel;
internal TextBox TxtDescription;
internal StackPanel IconSelectorPanel;
internal ComboBox CmbRequires;
internal Border BtnInsertToolList;
internal Border BtnInsertFormat;
internal TextBox TxtInstructions;
internal StackPanel ToolCheckListPanel;
internal TextBlock PreviewTokens;
internal TextBlock PreviewFileName;
internal TextBlock StatusText;
private bool _contentLoaded;
public SkillEditorWindow()
{
InitializeComponent();
_toolRegistry = ToolRegistry.CreateDefault();
base.Loaded += delegate
{
BuildIconSelector();
BuildToolChecklist();
UpdatePreview();
};
}
public SkillEditorWindow(SkillDefinition skill)
: this()
{
SkillEditorWindow skillEditorWindow = this;
_editingSkill = skill;
base.Loaded += delegate
{
skillEditorWindow.LoadSkill(skill);
};
}
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 - 50.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 BtnCancel_Click(object sender, MouseButtonEventArgs e)
{
Close();
}
private void BuildIconSelector()
{
IconSelectorPanel.Children.Clear();
SolidColorBrush solidColorBrush = new SolidColorBrush(Color.FromRgb(75, 94, 252));
Brush brush = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray;
Brush bgBrush = (TryFindResource("ItemBackground") as Brush) ?? Brushes.Transparent;
string[] iconCandidates = IconCandidates;
foreach (string text in iconCandidates)
{
bool flag = text == _selectedIcon;
Border border = new Border
{
Width = 34.0,
Height = 34.0,
CornerRadius = new CornerRadius(8.0),
Margin = new Thickness(0.0, 0.0, 4.0, 4.0),
Cursor = Cursors.Hand,
Background = (flag ? solidColorBrush : bgBrush),
BorderBrush = (flag ? solidColorBrush : Brushes.Transparent),
BorderThickness = new Thickness(1.5),
Tag = text
};
border.Child = new TextBlock
{
Text = text,
FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 14.0,
Foreground = (flag ? Brushes.White : brush),
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
border.MouseLeftButtonUp += delegate(object s, MouseButtonEventArgs _)
{
if (s is Border { Tag: string tag })
{
_selectedIcon = tag;
BuildIconSelector();
}
};
string capturedIcon = text;
border.MouseEnter += delegate(object s, MouseEventArgs _)
{
if (s is Border border2 && capturedIcon != _selectedIcon)
{
border2.Background = new SolidColorBrush(Color.FromArgb(48, 75, 94, 252));
}
};
border.MouseLeave += delegate(object s, MouseEventArgs _)
{
if (s is Border border2 && capturedIcon != _selectedIcon)
{
border2.Background = bgBrush;
}
};
IconSelectorPanel.Children.Add(border);
}
}
private void BuildToolChecklist()
{
ToolCheckListPanel.Children.Clear();
List<IAgentTool> list = _toolRegistry.All.OrderBy((IAgentTool t) => t.Name).ToList();
Brush foreground = (TryFindResource("PrimaryText") as Brush) ?? Brushes.White;
Brush brush = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray;
HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (_editingSkill != null && !string.IsNullOrWhiteSpace(_editingSkill.AllowedTools))
{
string[] array = _editingSkill.AllowedTools.Split(',', StringSplitOptions.RemoveEmptyEntries);
foreach (string text in array)
{
hashSet.Add(text.Trim());
}
}
foreach (IAgentTool item in list)
{
CheckBox checkBox = new CheckBox();
checkBox.Tag = item.Name;
checkBox.IsChecked = hashSet.Count == 0 || hashSet.Contains(item.Name);
checkBox.Margin = new Thickness(0.0, 0.0, 0.0, 4.0);
checkBox.Style = TryFindResource("ToggleSwitch") as Style;
CheckBox element = checkBox;
StackPanel stackPanel = new StackPanel
{
Orientation = Orientation.Horizontal,
Margin = new Thickness(0.0, 0.0, 0.0, 2.0)
};
stackPanel.Children.Add(element);
stackPanel.Children.Add(new TextBlock
{
Text = item.Name,
FontSize = 11.5,
FontFamily = new FontFamily("Consolas"),
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(6.0, 0.0, 0.0, 0.0)
});
if (!string.IsNullOrEmpty(item.Description))
{
stackPanel.ToolTip = item.Description;
}
ToolCheckListPanel.Children.Add(stackPanel);
}
}
private List<string> GetCheckedTools()
{
List<string> list = new List<string>();
foreach (object child in ToolCheckListPanel.Children)
{
if (child is StackPanel stackPanel && stackPanel.Children.Count > 0 && stackPanel.Children[0] is CheckBox { IsChecked: var isChecked } checkBox && isChecked == true && checkBox.Tag is string item)
{
list.Add(item);
}
}
return list;
}
private void BtnInsertTemplate_Click(object sender, MouseButtonEventArgs e)
{
if (sender is FrameworkElement { Tag: string tag })
{
if (1 == 0)
{
}
string text = tag switch
{
"tools" => BuildToolListTemplate(),
"format" => "## 출력 형식\n\n1. 결과를 사용자에게 한국어로 설명합니다.\n2. 코드 블록은 마크다운 형식으로 감쌉니다.\n3. 핵심 정보를 먼저 제시하고, 세부 사항은 뒤에 붙입니다.",
"steps" => "## 실행 단계\n\n1. **분석**: 사용자의 요청을 분석합니다.\n2. **계획**: 수행할 작업을 계획합니다.\n3. **실행**: 도구를 사용하여 작업을 수행합니다.\n4. **검증**: 결과를 검증합니다.\n5. **보고**: 결과를 사용자에게 보고합니다.",
_ => "",
};
if (1 == 0)
{
}
string text2 = text;
if (!string.IsNullOrEmpty(text2))
{
int caretIndex = TxtInstructions.CaretIndex;
string text3 = ((caretIndex > 0 && TxtInstructions.Text.Length > 0 && TxtInstructions.Text[caretIndex - 1] != '\n') ? "\n\n" : "");
TxtInstructions.Text = TxtInstructions.Text.Insert(caretIndex, text3 + text2.Trim());
TxtInstructions.CaretIndex = caretIndex + text3.Length + text2.Trim().Length;
TxtInstructions.Focus();
}
}
}
private string BuildToolListTemplate()
{
List<string> checkedTools = GetCheckedTools();
if (checkedTools.Count == 0)
{
return "## 사용 가능한 도구\n\n(도구가 선택되지 않았습니다. 우측 패널에서 도구를 선택하세요.)";
}
List<string> list = new List<string> { "## 사용 가능한 도구", "" };
foreach (string toolName in checkedTools)
{
IAgentTool agentTool = _toolRegistry.All.FirstOrDefault((IAgentTool t) => t.Name == toolName);
if (agentTool != null)
{
list.Add("- **" + agentTool.Name + "**: " + agentTool.Description);
}
}
return string.Join("\n", list);
}
private void TxtInstructions_TextChanged(object sender, TextChangedEventArgs e)
{
UpdatePreview();
}
private void UpdatePreview()
{
if (PreviewTokens != null && PreviewFileName != null)
{
string text = GenerateSkillContent();
int value = TokenEstimator.Estimate(text);
PreviewTokens.Text = $"예상 토큰: ~{value:N0}자 | {text.Length:N0}자";
string text2 = (string.IsNullOrWhiteSpace(TxtName?.Text) ? "new-skill" : TxtName.Text.Trim());
PreviewFileName.Text = text2 + ".skill.md";
}
}
private string GenerateSkillContent()
{
string text = TxtName?.Text?.Trim() ?? "new-skill";
string text2 = TxtLabel?.Text?.Trim() ?? "";
string text3 = TxtDescription?.Text?.Trim() ?? "";
string text4 = TxtInstructions?.Text ?? "";
string text5 = "";
if (CmbRequires?.SelectedItem is ComboBoxItem { Tag: string tag } && !string.IsNullOrEmpty(tag))
{
text5 = tag;
}
List<string> checkedTools = GetCheckedTools();
int count = _toolRegistry.All.Count;
string text6 = ((checkedTools.Count < count) ? string.Join(", ", checkedTools) : "");
List<string> list = new List<string> { "---" };
list.Add("name: " + text);
if (!string.IsNullOrEmpty(text2))
{
list.Add("label: " + text2);
}
if (!string.IsNullOrEmpty(text3))
{
list.Add("description: " + text3);
}
list.Add("icon: " + _selectedIcon);
if (!string.IsNullOrEmpty(text5))
{
list.Add("requires: " + text5);
}
if (!string.IsNullOrEmpty(text6))
{
list.Add("allowed-tools: " + text6);
}
list.Add("---");
list.Add("");
return string.Join("\n", list) + text4;
}
private void BtnPreview_Click(object sender, MouseButtonEventArgs e)
{
string text = GenerateSkillContent();
Window previewWin = new Window
{
Title = "스킬 파일 미리보기",
Width = 640.0,
Height = 520.0,
WindowStyle = WindowStyle.None,
AllowsTransparency = true,
Background = Brushes.Transparent,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = this
};
Brush background = (TryFindResource("LauncherBackground") as Brush) ?? Brushes.White;
Brush foreground = (TryFindResource("PrimaryText") as Brush) ?? Brushes.White;
Brush foreground2 = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray;
Brush itemBg = (TryFindResource("ItemBackground") as Brush) ?? Brushes.Transparent;
Brush borderBrush = (TryFindResource("BorderColor") as Brush) ?? Brushes.Gray;
Border border = new Border
{
Background = background,
CornerRadius = new CornerRadius(12.0),
BorderBrush = borderBrush,
BorderThickness = new Thickness(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
{
Height = new GridLength(1.0, GridUnitType.Star)
});
grid.RowDefinitions.Add(new RowDefinition
{
Height = GridLength.Auto
});
Border border2 = new Border
{
Background = itemBg,
CornerRadius = new CornerRadius(12.0, 12.0, 0.0, 0.0)
};
border2.MouseLeftButtonDown += delegate
{
previewWin.DragMove();
};
TextBlock child = new TextBlock
{
Text = "미리보기 — .skill.md",
FontSize = 14.0,
FontWeight = FontWeights.SemiBold,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(16.0, 0.0, 0.0, 0.0)
};
border2.Child = child;
Grid.SetRow(border2, 0);
TextBox element = new TextBox
{
Text = text,
FontFamily = new FontFamily("Consolas, Cascadia Code, Segoe UI"),
FontSize = 12.5,
IsReadOnly = true,
AcceptsReturn = true,
TextWrapping = TextWrapping.Wrap,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
Background = itemBg,
Foreground = foreground,
BorderThickness = new Thickness(0.0),
Padding = new Thickness(16.0, 12.0, 16.0, 12.0),
Margin = new Thickness(8.0, 8.0, 8.0, 0.0)
};
Grid.SetRow(element, 1);
Border border3 = new Border
{
Padding = new Thickness(16.0, 10.0, 16.0, 10.0),
CornerRadius = new CornerRadius(0.0, 0.0, 12.0, 12.0)
};
Border border4 = new Border
{
CornerRadius = new CornerRadius(8.0),
Padding = new Thickness(18.0, 8.0, 18.0, 8.0),
Background = itemBg,
Cursor = Cursors.Hand,
HorizontalAlignment = HorizontalAlignment.Right
};
border4.Child = new TextBlock
{
Text = "닫기",
FontSize = 12.5,
Foreground = foreground2
};
border4.MouseLeftButtonUp += delegate
{
previewWin.Close();
};
border4.MouseEnter += delegate(object s, MouseEventArgs _)
{
if (s is Border border5)
{
border5.Background = new SolidColorBrush(Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue));
}
};
border4.MouseLeave += delegate(object s, MouseEventArgs _)
{
if (s is Border border5)
{
border5.Background = itemBg;
}
};
border3.Child = border4;
Grid.SetRow(border3, 2);
grid.Children.Add(border2);
grid.Children.Add(element);
grid.Children.Add(border3);
border.Child = grid;
previewWin.Content = border;
previewWin.ShowDialog();
}
private void BtnSave_Click(object sender, MouseButtonEventArgs e)
{
string text = TxtName.Text.Trim();
if (string.IsNullOrWhiteSpace(text))
{
StatusText.Text = "⚠ 이름을 입력하세요.";
StatusText.Foreground = new SolidColorBrush(Color.FromRgb(248, 113, 113));
TxtName.Focus();
return;
}
if (!Regex.IsMatch(text, "^[a-zA-Z][a-zA-Z0-9\\-]*$"))
{
StatusText.Text = "⚠ 이름은 영문으로 시작하며 영문, 숫자, 하이픈만 사용 가능합니다.";
StatusText.Foreground = new SolidColorBrush(Color.FromRgb(248, 113, 113));
TxtName.Focus();
return;
}
if (string.IsNullOrWhiteSpace(TxtInstructions.Text))
{
StatusText.Text = "⚠ 지시사항을 입력하세요.";
StatusText.Foreground = new SolidColorBrush(Color.FromRgb(248, 113, 113));
TxtInstructions.Focus();
return;
}
string contents = GenerateSkillContent();
string path;
if (_editingSkill != null)
{
path = _editingSkill.FilePath;
}
else
{
string text2 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "skills");
if (!Directory.Exists(text2))
{
Directory.CreateDirectory(text2);
}
path = Path.Combine(text2, text + ".skill.md");
if (File.Exists(path))
{
int num = 2;
while (File.Exists(Path.Combine(text2, $"{text}_{num}.skill.md")))
{
num++;
}
path = Path.Combine(text2, $"{text}_{num}.skill.md");
}
}
try
{
File.WriteAllText(path, contents, Encoding.UTF8);
SkillService.LoadSkills();
StatusText.Text = "✓ 저장 완료: " + Path.GetFileName(path);
StatusText.Foreground = new SolidColorBrush(Color.FromRgb(52, 211, 153));
base.DialogResult = true;
}
catch (Exception ex)
{
CustomMessageBox.Show("저장 실패: " + ex.Message, "스킬 저장");
}
}
private void LoadSkill(SkillDefinition skill)
{
TitleText.Text = "스킬 편집";
TxtName.Text = skill.Name;
TxtLabel.Text = skill.Label;
TxtDescription.Text = skill.Description;
TxtInstructions.Text = skill.SystemPrompt;
_selectedIcon = (IconCandidates.Contains(skill.Icon) ? skill.Icon : IconCandidates[0]);
BuildIconSelector();
for (int i = 0; i < CmbRequires.Items.Count; i++)
{
if (CmbRequires.Items[i] is ComboBoxItem { Tag: string tag } && string.Equals(tag, skill.Requires, StringComparison.OrdinalIgnoreCase))
{
CmbRequires.SelectedIndex = i;
break;
}
}
UpdatePreview();
}
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "10.0.5.0")]
public void InitializeComponent()
{
if (!_contentLoaded)
{
_contentLoaded = true;
Uri resourceLocator = new Uri("/AxCopilot;component/views/skilleditorwindow.xaml", UriKind.Relative);
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:
TitleText = (TextBlock)target;
break;
case 3:
((Border)target).MouseLeftButtonUp += BtnClose_Click;
break;
case 4:
TxtName = (TextBox)target;
break;
case 5:
TxtLabel = (TextBox)target;
break;
case 6:
TxtDescription = (TextBox)target;
break;
case 7:
IconSelectorPanel = (StackPanel)target;
break;
case 8:
CmbRequires = (ComboBox)target;
break;
case 9:
BtnInsertToolList = (Border)target;
BtnInsertToolList.MouseLeftButtonUp += BtnInsertTemplate_Click;
break;
case 10:
BtnInsertFormat = (Border)target;
BtnInsertFormat.MouseLeftButtonUp += BtnInsertTemplate_Click;
break;
case 11:
((Border)target).MouseLeftButtonUp += BtnInsertTemplate_Click;
break;
case 12:
TxtInstructions = (TextBox)target;
TxtInstructions.TextChanged += TxtInstructions_TextChanged;
break;
case 13:
ToolCheckListPanel = (StackPanel)target;
break;
case 14:
PreviewTokens = (TextBlock)target;
break;
case 15:
PreviewFileName = (TextBlock)target;
break;
case 16:
StatusText = (TextBlock)target;
break;
case 17:
((Border)target).MouseLeftButtonUp += BtnCancel_Click;
break;
case 18:
((Border)target).MouseLeftButtonUp += BtnPreview_Click;
break;
case 19:
((Border)target).MouseLeftButtonUp += BtnSave_Click;
break;
default:
_contentLoaded = true;
break;
}
}
}