Files
AX-Copilot-Codex/.decompiled/AxCopilot.Views.ChatWindow.decompiled.cs

13445 lines
419 KiB
C#

#define DEBUG
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using AxCopilot.Models;
using AxCopilot.Services;
using AxCopilot.Services.Agent;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.Wpf;
using Microsoft.Win32;
namespace AxCopilot.Views;
public class ChatWindow : Window, IComponentConnector
{
private sealed class ConversationMeta
{
public string Id { get; init; } = "";
public string Title { get; init; } = "";
public string UpdatedAtText { get; init; } = "";
public bool Pinned { get; init; }
public string Category { get; init; } = "일반";
public string Symbol { get; init; } = "\ue8bd";
public string ColorHex { get; init; } = "#6B7280";
public string Tab { get; init; } = "Chat";
public DateTime UpdatedAt { get; init; }
public string Preview { get; init; } = "";
public string? ParentId { get; init; }
public int ExecutionEventCount { get; init; }
public int ErrorEventCount { get; init; }
}
private readonly SettingsService _settings;
private readonly ChatStorageService _storage;
private readonly LlmService _llm;
private readonly ToolRegistry _toolRegistry;
private readonly AgentLoopService _agentLoop;
private readonly ModelRouterService _router;
private readonly object _convLock = new object();
private ChatConversation? _currentConversation;
private CancellationTokenSource? _streamCts;
private bool _isStreaming;
private bool _sidebarVisible = true;
private bool _showExecutionHistory = true;
private readonly List<string> _draftQueue = new List<string>();
private bool _autoContinuingQueuedDraft;
private string _selectedCategory = "";
private bool _forceClose = false;
private readonly DispatcherTimer _cursorTimer;
private bool _cursorVisible = true;
private TextBlock? _activeStreamText;
private string _cachedStreamContent = "";
private TextBlock? _activeAiIcon;
private bool _aiIconPulseStopped;
private WorkflowAnalyzerWindow? _analyzerWindow;
private PlanViewerWindow? _planViewerWindow;
private bool _userScrolled;
private readonly DispatcherTimer _elapsedTimer;
private DateTime _streamStartTime;
private TextBlock? _elapsedLabel;
private readonly DispatcherTimer _typingTimer;
private int _displayedLength;
private string _activeTab = "Chat";
private readonly Dictionary<string, string?> _tabConversationId = new Dictionary<string, string>
{
["Chat"] = null,
["Cowork"] = null,
["Code"] = null
};
private readonly List<string> _attachedFiles = new List<string>();
private readonly List<ImageAttachment> _pendingImages = new List<ImageAttachment>();
private bool _autoWarningDismissed;
private static readonly HashSet<string> ImageExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp" };
private const int ConversationPageSize = 50;
private List<ConversationMeta>? _pendingConversations;
private bool _isEditing;
private List<(string Cmd, string Label, bool IsSkill)> _slashAllMatches = new List<(string, string, bool)>();
private int _slashPageOffset = 0;
private int _slashSelectedIndex = -1;
private string? _activeSlashCmd = null;
private static readonly Dictionary<string, (string Label, string SystemPrompt, string Tab)> SlashCommands = new Dictionary<string, (string, string, string)>(StringComparer.OrdinalIgnoreCase)
{
["/summary"] = ("Summary", "사용자가 제공한 내용을 핵심 포인트 위주로 간결하게 요약해 주세요. 불릿 포인트 형식을 사용하세요.", "all"),
["/translate"] = ("Translate", "사용자가 제공한 텍스트를 영어로 번역해 주세요. 원문의 톤과 뉘앙스를 유지하세요.", "all"),
["/explain"] = ("Explain", "사용자가 제공한 내용을 쉽고 자세하게 설명해 주세요. 필요하면 예시를 포함하세요.", "all"),
["/fix"] = ("Fix", "사용자가 제공한 텍스트의 맞춤법, 문법, 자연스러운 표현을 교정해 주세요. 수정 사항을 명확히 표시하세요.", "all"),
["/review"] = ("Code Review", "작업 폴더의 git diff를 분석하여 코드 리뷰를 수행해 주세요. code_review 도구를 사용하세요.", "dev"),
["/pr"] = ("PR Summary", "작업 폴더의 변경사항을 PR 설명 형식으로 요약해 주세요. code_review(action: pr_summary) 도구를 사용하세요.", "dev"),
["/test"] = ("Test", "작업 폴더의 코드에 대한 단위 테스트를 생성해 주세요. test_loop 도구를 사용하세요.", "dev"),
["/structure"] = ("Structure", "작업 폴더의 프로젝트 구조를 분석하고 설명해 주세요. folder_map 도구를 사용하세요.", "dev"),
["/build"] = ("Build", "작업 폴더의 프로젝트를 빌드해 주세요. build_run 도구를 사용하세요.", "dev"),
["/search"] = ("Search", "작업 폴더에서 관련 코드를 검색해 주세요. search_codebase 도구를 사용하세요.", "dev"),
["/help"] = ("Help", "__HELP__", "all")
};
private static readonly Dictionary<string, List<(string Label, string Icon, string Prompt)>> DropActions;
private static readonly HashSet<string> CodeExtensions;
private static readonly HashSet<string> DataExtensions;
private Popup? _dropActionPopup;
private int _agentCumulativeInputTokens;
private int _agentCumulativeOutputTokens;
private static readonly HashSet<string> WriteToolNames;
private Border? _planningCard;
private StackPanel? _planStepsPanel;
private System.Windows.Controls.ProgressBar? _planProgressBar;
private TextBlock? _planProgressText;
private List<int> _searchMatchIndices = new List<int>();
private int _searchCurrentIndex = -1;
private static readonly string[] Tips;
private int _tipIndex;
private DispatcherTimer? _tipDismissTimer;
private DispatcherTimer? _rainbowTimer;
private DateTime _rainbowStartTime;
private DispatcherTimer? _toastHideTimer;
private string _selectedMood = null;
private string _selectedLanguage = "auto";
private string _folderDataUsage = null;
private string? _promptCardPlaceholder;
private static readonly (string Id, string Label)[] GeminiModels;
private static readonly (string Id, string Label)[] ClaudeModels;
private static readonly HashSet<string> _previewableExtensions;
private readonly List<string> _previewTabs = new List<string>();
private string? _activePreviewTab;
private bool _webViewInitialized;
private static readonly string WebView2DataFolder;
private Popup? _previewTabPopup;
private DateTime _progressStartTime;
private DispatcherTimer? _progressElapsedTimer;
private static readonly HashSet<string> _ignoredDirs;
private DispatcherTimer? _fileBrowserRefreshTimer;
private Storyboard? _statusSpinStoryboard;
internal ColumnDefinition IconBarColumn;
internal ColumnDefinition SidebarColumn;
internal ColumnDefinition SplitterColumn;
internal ColumnDefinition PreviewColumn;
internal Border IconBarPanel;
internal System.Windows.Controls.Button BtnUserIconBar;
internal TextBlock UserInitialIconBar;
internal Border SidebarPanel;
internal System.Windows.Controls.Button BtnNewChat;
internal System.Windows.Controls.TextBox SearchBox;
internal System.Windows.Controls.Button BtnCategoryDrop;
internal TextBlock CategoryIcon;
internal TextBlock CategoryLabel;
internal StackPanel ConversationPanel;
internal System.Windows.Controls.Button BtnDeleteAll;
internal TextBlock UserInitialSidebar;
internal TextBlock UserNameText;
internal TextBlock UserPcText;
internal TextBlock ChatTitle;
internal System.Windows.Controls.TextBox ChatTitleEdit;
internal System.Windows.Controls.Button BtnPreviewToggle;
internal Ellipse PreviewDot;
internal Border AgentProgressBar;
internal TextBlock ProgressIcon;
internal TextBlock ProgressStepLabel;
internal Border ProgressFill;
internal TextBlock ProgressPercent;
internal TextBlock ProgressElapsed;
internal System.Windows.Controls.Button BtnToggleSidebar;
internal TextBlock ToggleSidebarIcon;
internal System.Windows.Controls.RadioButton TabChat;
internal System.Windows.Controls.RadioButton TabCowork;
internal System.Windows.Controls.RadioButton TabCode;
internal TextBlock MaximizeIcon;
internal Border MessageSearchBar;
internal System.Windows.Controls.TextBox SearchTextBox;
internal TextBlock SearchResultCount;
internal ScrollViewer MessageScroll;
internal StackPanel MessagePanel;
internal StackPanel EmptyState;
internal Border EmptyIcon;
internal TranslateTransform EmptyIconTranslate;
internal TextBlock EmptyStateTitle;
internal TextBlock EmptyStateDesc;
internal WrapPanel TopicButtonPanel;
internal Popup TemplatePopup;
internal ItemsControl TemplateItems;
internal TextBlock TemplateEmptyHint;
internal Popup PermissionPopup;
internal StackPanel PermissionItems;
internal Popup DataUsagePopup;
internal StackPanel DataUsageItems;
internal Popup SlashPopup;
internal TextBlock SlashPopupTitle;
internal TextBlock SlashPopupHint;
internal Border SlashNavUp;
internal TextBlock SlashNavUpText;
internal ItemsControl SlashItems;
internal Border SlashNavDown;
internal TextBlock SlashNavDownText;
internal Popup FolderMenuPopup;
internal StackPanel FolderMenuItems;
internal Border ToastBorder;
internal TextBlock ToastIcon;
internal TextBlock ToastText;
internal Border DraftPreviewCard;
internal TextBlock DraftPreviewText;
internal System.Windows.Controls.Button BtnDraftEnqueue;
internal System.Windows.Controls.Button BtnDraftEdit;
internal System.Windows.Controls.Button BtnDraftClear;
internal StackPanel DraftQueuePanel;
internal Border InputGlowBorder;
internal LinearGradientBrush RainbowBrush;
internal Border InputBorder;
internal System.Windows.Controls.Button BtnModelSelector;
internal TextBlock ModelLabel;
internal System.Windows.Controls.Button BtnTemplateSelector;
internal ItemsControl AttachedFilesPanel;
internal System.Windows.Controls.TextBox InputBox;
internal TextBlock InputWatermark;
internal Border SlashCommandChip;
internal TextBlock SlashChipText;
internal Border SlashChipClose;
internal System.Windows.Controls.Button BtnAttach;
internal Border BtnPause;
internal TextBlock PauseIcon;
internal System.Windows.Controls.Button BtnStop;
internal System.Windows.Controls.Button BtnSend;
internal Border FolderBar;
internal TextBlock FolderPathLabel;
internal StackPanel MoodIconPanel;
internal Border FormatMoodSeparator;
internal Border BtnDataUsage;
internal TextBlock DataUsageIcon;
internal TextBlock DataUsageLabel;
internal System.Windows.Controls.Button BtnPermission;
internal TextBlock PermissionIcon;
internal TextBlock PermissionLabel;
internal Popup FormatMenuPopup;
internal StackPanel FormatMenuItems;
internal Popup MoodMenuPopup;
internal StackPanel MoodMenuItems;
internal Border AutoPermissionWarning;
internal System.Windows.Controls.Button BtnAutoWarningClose;
internal Border FileBrowserPanel;
internal TextBlock FileBrowserTitle;
internal System.Windows.Controls.TreeView FileTreeView;
internal TextBlock StatusDiamond;
internal RotateTransform StatusDiamondRotate;
internal TextBlock StatusLabel;
internal System.Windows.Controls.Button BtnToggleExecutionLog;
internal TextBlock ExecutionLogIcon;
internal TextBlock ExecutionLogLabel;
internal Border SubAgentIndicator;
internal TextBlock SubAgentIndicatorLabel;
internal Border BtnShowAnalyzer;
internal TextBlock StatusElapsed;
internal TextBlock StatusTokens;
internal GridSplitter PreviewSplitter;
internal Border PreviewPanel;
internal StackPanel PreviewTabPanel;
internal System.Windows.Controls.Button BtnOpenExternal;
internal WebView2 PreviewWebView;
internal ScrollViewer PreviewTextScroll;
internal TextBlock PreviewTextBlock;
internal DataGrid PreviewDataGrid;
internal TextBlock PreviewEmpty;
internal Thumb ResizeGrip;
private bool _contentLoaded;
private int SlashPageSize => Math.Clamp(_settings.Settings.Llm.SlashPopupPageSize, 3, 20);
public ChatWindow(SettingsService settings)
{
//IL_0210: Unknown result type (might be due to invalid IL or missing references)
//IL_0215: Unknown result type (might be due to invalid IL or missing references)
//IL_022f: Expected O, but got Unknown
//IL_0248: Unknown result type (might be due to invalid IL or missing references)
//IL_024d: Unknown result type (might be due to invalid IL or missing references)
//IL_0267: Expected O, but got Unknown
//IL_0280: Unknown result type (might be due to invalid IL or missing references)
//IL_0285: Unknown result type (might be due to invalid IL or missing references)
//IL_029f: Expected O, but got Unknown
InitializeComponent();
_settings = settings;
_storage = new ChatStorageService();
_llm = new LlmService(settings);
_router = new ModelRouterService(settings);
_toolRegistry = ToolRegistry.CreateDefault();
_agentLoop = new AgentLoopService(_llm, _toolRegistry, settings)
{
Dispatcher = delegate(Action action)
{
((DispatcherObject)System.Windows.Application.Current).Dispatcher.Invoke(action);
},
AskPermissionCallback = async delegate(string toolName, string filePath)
{
MessageBoxResult result = MessageBoxResult.None;
await ((DispatcherObject)System.Windows.Application.Current).Dispatcher.InvokeAsync((Action)delegate
{
result = CustomMessageBox.Show($"도구 '{toolName}'이(가) 다음 파일에 접근하려 합니다:\n\n{filePath}\n\n허용하시겠습니까?", "AX Agent — 권한 확인", MessageBoxButton.YesNo, MessageBoxImage.Question);
});
return result == MessageBoxResult.Yes;
},
UserAskCallback = async delegate(string question, List<string> options, string defaultValue)
{
string response = null;
await ((DispatcherObject)System.Windows.Application.Current).Dispatcher.InvokeAsync((Action)delegate
{
response = UserAskDialog.Show(question, options, defaultValue);
});
return response;
}
};
SubAgentTool.StatusChanged += OnSubAgentStatusChanged;
_selectedMood = settings.Settings.Llm.DefaultMood ?? "modern";
_folderDataUsage = settings.Settings.Llm.FolderDataUsage ?? "active";
_cursorTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(530.0)
};
_cursorTimer.Tick += CursorTimer_Tick;
_elapsedTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1.0)
};
_elapsedTimer.Tick += ElapsedTimer_Tick;
_typingTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(12.0)
};
_typingTimer.Tick += TypingTimer_Tick;
base.KeyDown += ChatWindow_KeyDown;
base.Loaded += delegate
{
SetupUserInfo();
_selectedMood = _settings.Settings.Llm.DefaultMood ?? "modern";
_folderDataUsage = _settings.Settings.Llm.FolderDataUsage ?? "active";
UpdateAnalyzerButtonVisibility();
UpdateModelLabel();
InputBox.Focus();
UpdateDraftPreviewCard();
MessageScroll.ScrollChanged += MessageScroll_ScrollChanged;
((DispatcherObject)this).Dispatcher.BeginInvoke((Delegate)(Action)delegate
{
TemplateService.LoadCustomMoods(_settings.Settings.Llm.CustomMoods);
BuildTopicButtons();
RestoreLastConversations();
RefreshConversationList();
LoadMcpToolsAsync();
UpdateExecutionHistoryUi();
RefreshSubAgentIndicator();
Task.Run(delegate
{
int retentionDays = _settings.Settings.Llm.RetentionDays;
if (retentionDays > 0)
{
_storage.PurgeExpired(retentionDays);
}
_storage.PurgeForDiskSpace();
});
}, (DispatcherPriority)2, Array.Empty<object>());
System.Windows.Media.Brush accentBrush = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue;
System.Windows.Media.Brush borderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
InputBox.GotFocus += delegate
{
InputBorder.BorderBrush = accentBrush;
};
InputBox.LostFocus += delegate
{
InputBorder.BorderBrush = borderBrush;
};
InputBorder.AllowDrop = true;
InputBorder.DragOver += delegate(object obj, System.Windows.DragEventArgs de)
{
de.Effects = (de.Data.GetDataPresent(System.Windows.DataFormats.FileDrop) ? System.Windows.DragDropEffects.Copy : System.Windows.DragDropEffects.None);
de.Handled = true;
};
InputBorder.Drop += delegate(object obj, System.Windows.DragEventArgs de)
{
if (de.Data.GetData(System.Windows.DataFormats.FileDrop) is string[] array && array.Length != 0)
{
if (_settings.Settings.Llm.EnableDragDropAiActions && array.Length <= 5)
{
ShowDropActionMenu(array);
}
else
{
string[] array2 = array;
foreach (string filePath in array2)
{
AddAttachedFile(filePath);
}
}
}
};
if (_settings.Settings.Llm.EnableSkillSystem)
{
SkillService.EnsureSkillFolder();
SkillService.LoadSkills(_settings.Settings.Llm.SkillsFolderPath);
}
SlashNavUp.MouseLeftButtonUp += delegate
{
_slashPageOffset = Math.Max(0, _slashPageOffset - SlashPageSize);
RenderSlashPage();
};
SlashNavDown.MouseLeftButtonUp += delegate
{
_slashPageOffset = Math.Min(_slashAllMatches.Count - 1, _slashPageOffset + SlashPageSize);
RenderSlashPage();
};
SlashChipClose.MouseLeftButtonUp += delegate
{
HideSlashChip(restoreText: true);
InputBox.Focus();
};
InputBox.PreviewMouseWheel += delegate(object obj, MouseWheelEventArgs me)
{
if (SlashPopup.IsOpen)
{
me.Handled = true;
SlashPopup_ScrollByDelta(me.Delta);
}
};
UpdateFolderBar();
ApplyHoverBounceAnimation(BtnModelSelector);
ApplyHoverBounceAnimation(BtnTemplateSelector, -1.5);
ApplyHoverScaleAnimation(BtnSend, 1.12);
ApplyHoverScaleAnimation(BtnStop, 1.12);
};
base.Closed += delegate
{
_streamCts?.Cancel();
_cursorTimer.Stop();
_elapsedTimer.Stop();
_typingTimer.Stop();
SubAgentTool.StatusChanged -= OnSubAgentStatusChanged;
_llm.Dispose();
};
}
private async Task LoadMcpToolsAsync()
{
try
{
List<McpServerEntry> servers = _settings.Settings.Llm.McpServers;
if (servers != null && servers.Count != 0)
{
int count = await _toolRegistry.RegisterMcpToolsAsync(servers);
if (count > 0)
{
LogService.Info($"MCP tools registered for AX Agent: {count}");
}
}
}
catch (Exception ex)
{
Exception ex2 = ex;
LogService.Warn("Failed to register MCP tools: " + ex2.Message);
}
}
protected override void OnClosing(CancelEventArgs e)
{
if (!_forceClose)
{
e.Cancel = true;
Hide();
}
else
{
base.OnClosing(e);
}
}
public void ForceClose()
{
lock (_convLock)
{
if (_currentConversation != null && _currentConversation.Messages.Count > 0)
{
_tabConversationId[_activeTab] = _currentConversation.Id;
try
{
_storage.Save(_currentConversation);
}
catch
{
}
}
}
SaveLastConversations();
_forceClose = true;
Close();
}
private void SetupUserInfo()
{
string userName = Environment.UserName;
string text = userName;
char[] array = new char[3] { '\\', '/', ':' };
foreach (char value in array)
{
int num = text.LastIndexOf(value);
if (num >= 0)
{
string text2 = text;
int num2 = num + 1;
text = text2.Substring(num2, text2.Length - num2);
}
}
string text3 = ((text.Length > 0) ? text.Substring(0, 1).ToUpper() : "U");
string machineName = Environment.MachineName;
UserInitialSidebar.Text = text3;
UserInitialIconBar.Text = text3;
UserNameText.Text = text;
UserPcText.Text = machineName;
BtnUserIconBar.ToolTip = text + " (" + machineName + ")";
}
private void MessageScroll_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (MessageScroll.ScrollableHeight <= 1.0)
{
_userScrolled = false;
}
else if (!(Math.Abs(e.ExtentHeightChange) > 0.5))
{
bool flag = MessageScroll.VerticalOffset >= MessageScroll.ScrollableHeight - 40.0;
_userScrolled = !flag;
}
}
private void AutoScrollIfNeeded()
{
if (!_userScrolled)
{
SmoothScrollToEnd();
}
}
private void ForceScrollToEnd()
{
_userScrolled = false;
((DispatcherObject)this).Dispatcher.InvokeAsync((Action)delegate
{
SmoothScrollToEnd();
}, (DispatcherPriority)4);
}
private void SmoothScrollToEnd()
{
//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
//IL_0103: Expected O, but got Unknown
double targetOffset = MessageScroll.ScrollableHeight;
double currentOffset = MessageScroll.VerticalOffset;
double diff = targetOffset - currentOffset;
if (diff <= 60.0)
{
MessageScroll.ScrollToEnd();
return;
}
DoubleAnimation doubleAnimation = new DoubleAnimation
{
From = currentOffset,
To = targetOffset,
Duration = TimeSpan.FromMilliseconds(200.0),
EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
}
};
doubleAnimation.Completed += delegate
{
MessageScroll.ScrollToVerticalOffset(targetOffset);
};
DateTime startTime = DateTime.UtcNow;
DispatcherTimer timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(16.0)
};
EventHandler tickHandler = null;
tickHandler = delegate
{
double totalMilliseconds = (DateTime.UtcNow - startTime).TotalMilliseconds;
double num = Math.Min(totalMilliseconds / 200.0, 1.0);
double num2 = 1.0 - Math.Pow(1.0 - num, 3.0);
double offset = currentOffset + diff * num2;
MessageScroll.ScrollToVerticalOffset(offset);
if (num >= 1.0)
{
timer.Stop();
timer.Tick -= tickHandler;
}
};
timer.Tick += tickHandler;
timer.Start();
}
private void ChatTitle_MouseDown(object sender, MouseButtonEventArgs e)
{
lock (_convLock)
{
if (_currentConversation == null)
{
return;
}
}
ChatTitle.Visibility = Visibility.Collapsed;
ChatTitleEdit.Text = ChatTitle.Text;
ChatTitleEdit.Visibility = Visibility.Visible;
ChatTitleEdit.Focus();
ChatTitleEdit.SelectAll();
}
private void ChatTitleEdit_LostFocus(object sender, RoutedEventArgs e)
{
CommitTitleEdit();
}
private void ChatTitleEdit_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Invalid comparison between Unknown and I4
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Invalid comparison between Unknown and I4
if ((int)e.Key == 6)
{
CommitTitleEdit();
e.Handled = true;
}
if ((int)e.Key == 13)
{
CancelTitleEdit();
e.Handled = true;
}
}
private void CommitTitleEdit()
{
string text = ChatTitleEdit.Text.Trim();
ChatTitleEdit.Visibility = Visibility.Collapsed;
ChatTitle.Visibility = Visibility.Visible;
if (string.IsNullOrEmpty(text))
{
return;
}
lock (_convLock)
{
if (_currentConversation == null)
{
return;
}
_currentConversation.Title = text;
}
ChatTitle.Text = text;
try
{
ChatConversation currentConversation;
lock (_convLock)
{
currentConversation = _currentConversation;
}
_storage.Save(currentConversation);
}
catch
{
}
RefreshConversationList();
}
private void CancelTitleEdit()
{
ChatTitleEdit.Visibility = Visibility.Collapsed;
ChatTitle.Visibility = Visibility.Visible;
}
private void BtnCategoryDrop_Click(object sender, RoutedEventArgs e)
{
System.Windows.Media.Brush background = (TryFindResource("LauncherBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromRgb(26, 27, 46));
System.Windows.Media.Brush borderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush primaryText = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush iconColor = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush hoverBg = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromArgb(30, byte.MaxValue, byte.MaxValue, byte.MaxValue));
System.Windows.Media.Brush accentBrush = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
Popup popup = new Popup
{
StaysOpen = false,
AllowsTransparency = true,
PopupAnimation = PopupAnimation.Fade,
PlacementTarget = BtnCategoryDrop,
Placement = PlacementMode.Bottom,
HorizontalOffset = 0.0,
VerticalOffset = 4.0
};
Border border = new Border
{
Background = background,
BorderBrush = borderBrush,
BorderThickness = new Thickness(1.0),
CornerRadius = new CornerRadius(12.0),
Padding = new Thickness(6.0),
MinWidth = 180.0,
Effect = new DropShadowEffect
{
BlurRadius = 16.0,
ShadowDepth = 4.0,
Opacity = 0.3,
Color = Colors.Black
}
};
StackPanel stackPanel = new StackPanel();
string activeTab = _activeTab;
if (1 == 0)
{
}
string text = ((activeTab == "Cowork") ? "모든 작업" : ((!(activeTab == "Code")) ? "모든 주제" : "모든 작업"));
if (1 == 0)
{
}
string text2 = text;
stackPanel.Children.Add(CreateCatItem("\ue8bd", text2, iconColor, string.IsNullOrEmpty(_selectedCategory), delegate
{
_selectedCategory = "";
UpdateCategoryLabel();
RefreshConversationList();
}));
stackPanel.Children.Add(CreateSep());
if (_activeTab == "Cowork" || _activeTab == "Code")
{
IReadOnlyList<TopicPreset> byTabWithCustom = PresetService.GetByTabWithCustom(_activeTab, _settings.Settings.Llm.CustomPresets);
HashSet<string> hashSet = new HashSet<string>();
foreach (TopicPreset item5 in byTabWithCustom)
{
if (!item5.IsCustom && hashSet.Add(item5.Category))
{
string capturedCat = item5.Category;
stackPanel.Children.Add(CreateCatItem(item5.Symbol, item5.Label, BrushFromHex(item5.Color), _selectedCategory == capturedCat, delegate
{
_selectedCategory = capturedCat;
UpdateCategoryLabel();
RefreshConversationList();
}));
}
}
if (byTabWithCustom.Any((TopicPreset p) => p.IsCustom))
{
stackPanel.Children.Add(CreateSep());
stackPanel.Children.Add(CreateCatItem("\ue710", "커스텀 프리셋", iconColor, _selectedCategory == "__custom__", delegate
{
_selectedCategory = "__custom__";
UpdateCategoryLabel();
RefreshConversationList();
}));
}
}
else
{
(string, string, string, string)[] all = ChatCategory.All;
for (int num = 0; num < all.Length; num++)
{
(string, string, string, string) tuple = all[num];
string item = tuple.Item1;
string item2 = tuple.Item2;
string item3 = tuple.Item3;
string item4 = tuple.Item4;
string capturedKey = item;
stackPanel.Children.Add(CreateCatItem(item3, item2, BrushFromHex(item4), _selectedCategory == capturedKey, delegate
{
_selectedCategory = capturedKey;
UpdateCategoryLabel();
RefreshConversationList();
}));
}
List<CustomPresetEntry> list = _settings.Settings.Llm.CustomPresets.Where((CustomPresetEntry c) => c.Tab == "Chat").ToList();
if (list.Count > 0)
{
stackPanel.Children.Add(CreateSep());
stackPanel.Children.Add(CreateCatItem("\ue710", "커스텀 프리셋", iconColor, _selectedCategory == "__custom__", delegate
{
_selectedCategory = "__custom__";
UpdateCategoryLabel();
RefreshConversationList();
}));
}
}
border.Child = stackPanel;
popup.Child = border;
popup.IsOpen = true;
Border CreateCatItem(string icon, string text3, System.Windows.Media.Brush foreground, bool isSelected, Action onClick)
{
Border border2 = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(8.0),
Padding = new Thickness(10.0, 7.0, 10.0, 7.0),
Margin = new Thickness(0.0, 1.0, 0.0, 1.0),
Cursor = System.Windows.Input.Cursors.Hand
};
Grid grid = new Grid
{
ColumnDefinitions =
{
new ColumnDefinition
{
Width = new GridLength(24.0)
},
new ColumnDefinition
{
Width = new GridLength(1.0, GridUnitType.Star)
},
new ColumnDefinition
{
Width = new GridLength(20.0)
}
}
};
TextBlock element = new TextBlock
{
Text = icon,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 12.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(element, 0);
grid.Children.Add(element);
TextBlock element2 = new TextBlock
{
Text = text3,
FontSize = 12.5,
Foreground = primaryText,
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(element2, 1);
grid.Children.Add(element2);
if (isSelected)
{
FrameworkElement element3 = CreateSimpleCheck(accentBrush);
Grid.SetColumn(element3, 2);
grid.Children.Add(element3);
}
border2.Child = grid;
border2.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border3)
{
border3.Background = hoverBg;
}
};
border2.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border3)
{
border3.Background = System.Windows.Media.Brushes.Transparent;
}
};
border2.MouseLeftButtonUp += delegate
{
popup.IsOpen = false;
onClick();
};
return border2;
}
Border CreateSep()
{
return new Border
{
Height = 1.0,
Background = borderBrush,
Opacity = 0.3,
Margin = new Thickness(8.0, 4.0, 8.0, 4.0)
};
}
}
private void UpdateCategoryLabel()
{
if (string.IsNullOrEmpty(_selectedCategory))
{
TextBlock categoryLabel = CategoryLabel;
string activeTab = _activeTab;
if (1 == 0)
{
}
string text = ((!(activeTab == "Cowork") && !(activeTab == "Code")) ? "모든 주제" : "모든 작업");
if (1 == 0)
{
}
categoryLabel.Text = text;
CategoryIcon.Text = "\ue8bd";
return;
}
if (_selectedCategory == "__custom__")
{
CategoryLabel.Text = "커스텀 프리셋";
CategoryIcon.Text = "\ue710";
return;
}
(string, string, string, string)[] all = ChatCategory.All;
for (int i = 0; i < all.Length; i++)
{
var (text2, text3, text4, _) = all[i];
if (text2 == _selectedCategory)
{
CategoryLabel.Text = text3;
CategoryIcon.Text = text4;
return;
}
}
IReadOnlyList<TopicPreset> byTabWithCustom = PresetService.GetByTabWithCustom(_activeTab, _settings.Settings.Llm.CustomPresets);
TopicPreset topicPreset = byTabWithCustom.FirstOrDefault((TopicPreset p) => p.Category == _selectedCategory);
if (topicPreset != null)
{
CategoryLabel.Text = topicPreset.Label;
CategoryIcon.Text = topicPreset.Symbol;
}
else
{
CategoryLabel.Text = _selectedCategory;
CategoryIcon.Text = "\ue8bd";
}
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource.FromHwnd(new WindowInteropHelper(this).Handle)?.AddHook(WndProc);
}
private nint WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled)
{
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
if (msg == 36)
{
Screen screen = Screen.FromHandle(hwnd);
System.Drawing.Rectangle workingArea = screen.WorkingArea;
System.Drawing.Rectangle bounds = screen.Bounds;
HwndSource hwndSource = HwndSource.FromHwnd(hwnd);
double? obj;
if (hwndSource == null)
{
obj = null;
}
else
{
HwndTarget compositionTarget = hwndSource.CompositionTarget;
if (compositionTarget == null)
{
obj = null;
}
else
{
Matrix transformToDevice = compositionTarget.TransformToDevice;
obj = ((Matrix)(ref transformToDevice)).M11;
}
}
double num = obj ?? 1.0;
Marshal.WriteInt32(lParam, 8, workingArea.Width);
Marshal.WriteInt32(lParam, 12, workingArea.Height);
Marshal.WriteInt32(lParam, 16, workingArea.Left - bounds.Left);
Marshal.WriteInt32(lParam, 20, workingArea.Top - bounds.Top);
handled = true;
}
return IntPtr.Zero;
}
private void BtnMinimize_Click(object sender, RoutedEventArgs e)
{
base.WindowState = WindowState.Minimized;
}
private void BtnMaximize_Click(object sender, RoutedEventArgs e)
{
base.WindowState = ((base.WindowState != WindowState.Maximized) ? WindowState.Maximized : WindowState.Normal);
MaximizeIcon.Text = ((base.WindowState == WindowState.Maximized) ? "\ue923" : "\ue739");
}
private void BtnClose_Click(object sender, RoutedEventArgs e)
{
Close();
}
private void SaveCurrentTabConversationId()
{
lock (_convLock)
{
if (_currentConversation != null && _currentConversation.Messages.Count > 0)
{
_tabConversationId[_activeTab] = _currentConversation.Id;
try
{
_storage.Save(_currentConversation);
}
catch
{
}
}
}
SaveLastConversations();
}
private void StopStreamingIfActive()
{
if (_isStreaming)
{
_streamCts?.Cancel();
_cursorTimer.Stop();
_elapsedTimer.Stop();
_typingTimer.Stop();
StopRainbowGlow();
HideStickyProgress();
_activeStreamText = null;
_elapsedLabel = null;
_cachedStreamContent = "";
_isStreaming = false;
BtnSend.IsEnabled = true;
BtnStop.Visibility = Visibility.Collapsed;
BtnPause.Visibility = Visibility.Collapsed;
PauseIcon.Text = "\ue769";
BtnSend.Visibility = Visibility.Visible;
_streamCts?.Dispose();
_streamCts = null;
SetStatusIdle();
}
}
private void TabChat_Checked(object sender, RoutedEventArgs e)
{
if (!(_activeTab == "Chat"))
{
StopStreamingIfActive();
SaveCurrentTabConversationId();
_activeTab = "Chat";
_selectedCategory = "";
UpdateCategoryLabel();
UpdateTabUI();
}
}
private void TabCowork_Checked(object sender, RoutedEventArgs e)
{
if (!(_activeTab == "Cowork"))
{
StopStreamingIfActive();
SaveCurrentTabConversationId();
_activeTab = "Cowork";
_selectedCategory = "";
UpdateCategoryLabel();
UpdateTabUI();
}
}
private void TabCode_Checked(object sender, RoutedEventArgs e)
{
if (!(_activeTab == "Code"))
{
StopStreamingIfActive();
SaveCurrentTabConversationId();
_activeTab = "Code";
_selectedCategory = "";
UpdateCategoryLabel();
UpdateTabUI();
}
}
private void UpdateTabUI()
{
if (FolderBar != null)
{
FolderBar.Visibility = ((!(_activeTab != "Chat")) ? Visibility.Collapsed : Visibility.Visible);
}
if (InputWatermark != null)
{
TextBlock inputWatermark = InputWatermark;
string activeTab = _activeTab;
if (1 == 0)
{
}
string text = ((activeTab == "Cowork") ? "에이전트에게 작업을 요청하세요 (파일 읽기/쓰기, 문서 생성...)" : ((!(activeTab == "Code")) ? _promptCardPlaceholder : "코드 관련 작업을 요청하세요..."));
if (1 == 0)
{
}
inputWatermark.Text = text;
}
ApplyTabDefaultPermission();
if (_activeTab == "Cowork")
{
BuildBottomBar();
if (_settings.Settings.Llm.ShowFileBrowser && FileBrowserPanel != null)
{
FileBrowserPanel.Visibility = Visibility.Visible;
BuildFileTree();
}
}
else if (_activeTab == "Code")
{
BuildCodeBottomBar();
if (_settings.Settings.Llm.ShowFileBrowser && FileBrowserPanel != null)
{
FileBrowserPanel.Visibility = Visibility.Visible;
BuildFileTree();
}
}
else
{
MoodIconPanel.Children.Clear();
if (FormatMoodSeparator != null)
{
FormatMoodSeparator.Visibility = Visibility.Collapsed;
}
if (FileBrowserPanel != null)
{
FileBrowserPanel.Visibility = Visibility.Collapsed;
}
}
BuildTopicButtons();
SwitchToTabConversation();
ShowRandomTip();
}
private void SwitchToTabConversation()
{
lock (_convLock)
{
if (_currentConversation != null && _currentConversation.Messages.Count > 0)
{
try
{
_storage.Save(_currentConversation);
}
catch
{
}
}
}
string valueOrDefault = _tabConversationId.GetValueOrDefault(_activeTab);
if (!string.IsNullOrEmpty(valueOrDefault))
{
ChatConversation chatConversation = _storage.Load(valueOrDefault);
if (chatConversation != null)
{
if (string.IsNullOrEmpty(chatConversation.Tab))
{
chatConversation.Tab = _activeTab;
}
lock (_convLock)
{
_currentConversation = chatConversation;
}
MessagePanel.Children.Clear();
foreach (ChatMessage message in chatConversation.Messages)
{
AddMessageBubble(message.Role, message.Content, animate: false, message);
}
EmptyState.Visibility = ((chatConversation.Messages.Count > 0) ? Visibility.Collapsed : Visibility.Visible);
UpdateChatTitle();
RefreshConversationList();
UpdateFolderBar();
return;
}
}
lock (_convLock)
{
_currentConversation = new ChatConversation
{
Tab = _activeTab
};
string workFolder = _settings.Settings.Llm.WorkFolder;
if (!string.IsNullOrEmpty(workFolder) && _activeTab != "Chat")
{
_currentConversation.WorkFolder = workFolder;
}
}
MessagePanel.Children.Clear();
EmptyState.Visibility = Visibility.Visible;
_attachedFiles.Clear();
RefreshAttachedFilesUI();
UpdateChatTitle();
RefreshConversationList();
UpdateFolderBar();
}
private void FolderPathLabel_Click(object sender, MouseButtonEventArgs e)
{
ShowFolderMenu();
}
private void ShowFolderMenu()
{
FolderMenuItems.Children.Clear();
System.Windows.Media.Brush brush = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
System.Windows.Media.Brush foreground = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush foreground2 = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
int count = Math.Clamp(_settings.Settings.Llm.MaxRecentFolders, 3, 30);
List<string> list = _settings.Settings.Llm.RecentWorkFolders.Where((string p) => IsPathAllowed(p) && Directory.Exists(p)).Take(count).ToList();
if (list.Count > 0)
{
FolderMenuItems.Children.Add(new TextBlock
{
Text = "최근 폴더",
FontSize = 12.5,
FontWeight = FontWeights.SemiBold,
Foreground = foreground2,
Margin = new Thickness(10.0, 6.0, 10.0, 4.0)
});
string currentWorkFolder = GetCurrentWorkFolder();
foreach (string item in list)
{
bool flag = item.Equals(currentWorkFolder, StringComparison.OrdinalIgnoreCase);
string text = System.IO.Path.GetFileName(item);
if (string.IsNullOrEmpty(text))
{
text = item;
}
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
if (flag)
{
FrameworkElement frameworkElement = CreateSimpleCheck(brush);
frameworkElement.Margin = new Thickness(0.0, 0.0, 8.0, 0.0);
stackPanel.Children.Add(frameworkElement);
}
TextBlock element = new TextBlock
{
Text = text,
FontSize = 14.0,
FontWeight = (flag ? FontWeights.SemiBold : FontWeights.Normal),
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center,
MaxWidth = 340.0,
TextTrimming = TextTrimming.CharacterEllipsis
};
stackPanel.Children.Add(element);
Border border = new Border
{
Child = stackPanel,
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(8.0),
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(10.0, 7.0, 10.0, 7.0),
ToolTip = item
};
border.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border3)
{
border3.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue));
}
};
border.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border3)
{
border3.Background = System.Windows.Media.Brushes.Transparent;
}
};
string capturedPath = item;
border.MouseLeftButtonUp += delegate
{
FolderMenuPopup.IsOpen = false;
SetWorkFolder(capturedPath);
};
border.MouseRightButtonUp += delegate(object _, MouseButtonEventArgs re)
{
re.Handled = true;
ShowRecentFolderContextMenu(capturedPath);
};
FolderMenuItems.Children.Add(border);
}
FolderMenuItems.Children.Add(new Border
{
Height = 1.0,
Background = ((TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
Margin = new Thickness(8.0, 4.0, 8.0, 4.0),
Opacity = 0.5
});
}
StackPanel stackPanel2 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel2.Children.Add(new TextBlock
{
Text = "\ued25",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 14.0,
Foreground = brush,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 8.0, 0.0)
});
stackPanel2.Children.Add(new TextBlock
{
Text = "폴더 찾아보기...",
FontSize = 14.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center
});
Border border2 = new Border
{
Child = stackPanel2,
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(8.0),
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(10.0, 7.0, 10.0, 7.0)
};
border2.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border3)
{
border3.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue));
}
};
border2.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border3)
{
border3.Background = System.Windows.Media.Brushes.Transparent;
}
};
border2.MouseLeftButtonUp += delegate
{
FolderMenuPopup.IsOpen = false;
BrowseWorkFolder();
};
FolderMenuItems.Children.Add(border2);
FolderMenuPopup.IsOpen = true;
}
private void BrowseWorkFolder()
{
FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog
{
Description = "작업 폴더를 선택하세요",
ShowNewFolderButton = false,
UseDescriptionForTitle = true
};
string currentWorkFolder = GetCurrentWorkFolder();
if (!string.IsNullOrEmpty(currentWorkFolder) && Directory.Exists(currentWorkFolder))
{
folderBrowserDialog.SelectedPath = currentWorkFolder;
}
if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
if (!IsPathAllowed(folderBrowserDialog.SelectedPath))
{
CustomMessageBox.Show("이 경로는 작업 폴더로 선택할 수 없습니다.", "경로 제한", MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
else
{
SetWorkFolder(folderBrowserDialog.SelectedPath);
}
}
}
private static bool IsPathAllowed(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return false;
}
string text = path.TrimEnd('\\', '/');
if (text.Equals("C:", StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (path.IndexOf("Document", StringComparison.OrdinalIgnoreCase) >= 0)
{
return false;
}
return true;
}
private void SetWorkFolder(string path)
{
string fullPath = System.IO.Path.GetFullPath(path);
string pathRoot = System.IO.Path.GetPathRoot(fullPath);
if (!string.IsNullOrEmpty(pathRoot) && fullPath.TrimEnd('\\', '/').Equals(pathRoot.TrimEnd('\\', '/'), StringComparison.OrdinalIgnoreCase))
{
ShowToast("드라이브 루트(" + pathRoot + ")는 작업공간으로 설정할 수 없습니다. 하위 폴더를 선택하세요.", "\ue783", 3000);
return;
}
FolderPathLabel.Text = path;
FolderPathLabel.ToolTip = path;
lock (_convLock)
{
if (_currentConversation != null)
{
_currentConversation.WorkFolder = path;
}
}
List<string> recentWorkFolders = _settings.Settings.Llm.RecentWorkFolders;
recentWorkFolders.RemoveAll((string p) => !IsPathAllowed(p));
recentWorkFolders.Remove(path);
recentWorkFolders.Insert(0, path);
int num = Math.Clamp(_settings.Settings.Llm.MaxRecentFolders, 3, 30);
if (recentWorkFolders.Count > num)
{
recentWorkFolders.RemoveRange(num, recentWorkFolders.Count - num);
}
_settings.Settings.Llm.WorkFolder = path;
_settings.Save();
}
private string GetCurrentWorkFolder()
{
lock (_convLock)
{
if (_currentConversation != null && !string.IsNullOrEmpty(_currentConversation.WorkFolder))
{
return _currentConversation.WorkFolder;
}
}
return _settings.Settings.Llm.WorkFolder;
}
private ContextMenu CreateThemedContextMenu()
{
System.Windows.Media.Brush background = (TryFindResource("LauncherBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromRgb(30, 30, 46));
System.Windows.Media.Brush borderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
return new ContextMenu
{
Background = background,
BorderBrush = borderBrush,
BorderThickness = new Thickness(1.0),
Padding = new Thickness(4.0)
};
}
private void ShowRecentFolderContextMenu(string folderPath)
{
ContextMenu menu = CreateThemedContextMenu();
System.Windows.Media.Brush primaryText = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush secondaryText = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
AddItem("\ued25", "폴더 열기", delegate
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = folderPath,
UseShellExecute = true
});
}
catch
{
}
});
AddItem("\ue8c8", "경로 복사", delegate
{
try
{
System.Windows.Clipboard.SetText(folderPath);
}
catch
{
}
});
menu.Items.Add(new Separator());
AddItem("\ue74d", "목록에서 삭제", delegate
{
_settings.Settings.Llm.RecentWorkFolders.RemoveAll((string p) => p.Equals(folderPath, StringComparison.OrdinalIgnoreCase));
_settings.Save();
if (FolderMenuPopup.IsOpen)
{
ShowFolderMenu();
}
});
menu.IsOpen = true;
void AddItem(string icon, string label, Action action)
{
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(new TextBlock
{
Text = icon,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 12.0,
Foreground = secondaryText,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 8.0, 0.0)
});
stackPanel.Children.Add(new TextBlock
{
Text = label,
FontSize = 12.0,
Foreground = primaryText,
VerticalAlignment = VerticalAlignment.Center
});
MenuItem menuItem = new MenuItem
{
Header = stackPanel,
Padding = new Thickness(8.0, 6.0, 16.0, 6.0)
};
menuItem.Click += delegate
{
action();
};
menu.Items.Add(menuItem);
}
}
private void BtnFolderClear_Click(object sender, RoutedEventArgs e)
{
FolderPathLabel.Text = "폴더를 선택하세요";
FolderPathLabel.ToolTip = null;
lock (_convLock)
{
if (_currentConversation != null)
{
_currentConversation.WorkFolder = "";
}
}
}
private void UpdateFolderBar()
{
if (FolderBar == null)
{
return;
}
if (_activeTab == "Chat")
{
FolderBar.Visibility = Visibility.Collapsed;
return;
}
FolderBar.Visibility = Visibility.Visible;
string currentWorkFolder = GetCurrentWorkFolder();
if (!string.IsNullOrEmpty(currentWorkFolder))
{
FolderPathLabel.Text = currentWorkFolder;
FolderPathLabel.ToolTip = currentWorkFolder;
}
else
{
FolderPathLabel.Text = "폴더를 선택하세요";
FolderPathLabel.ToolTip = null;
}
LoadConversationSettings();
UpdatePermissionUI();
UpdateDataUsageUI();
}
private void LoadConversationSettings()
{
ChatConversation currentConversation;
lock (_convLock)
{
currentConversation = _currentConversation;
}
LlmSettings llm = _settings.Settings.Llm;
if (currentConversation != null && currentConversation.Permission != null)
{
_settings.Settings.Llm.FilePermission = currentConversation.Permission;
}
_folderDataUsage = currentConversation?.DataUsage ?? llm.FolderDataUsage ?? "active";
_selectedMood = currentConversation?.Mood ?? llm.DefaultMood ?? "modern";
}
private void SaveConversationSettings()
{
ChatConversation currentConversation;
lock (_convLock)
{
currentConversation = _currentConversation;
}
if (currentConversation == null)
{
return;
}
currentConversation.Permission = _settings.Settings.Llm.FilePermission;
currentConversation.DataUsage = _folderDataUsage;
currentConversation.Mood = _selectedMood;
try
{
_storage.Save(currentConversation);
}
catch (Exception ex)
{
LogService.Debug("대화 저장 실패: " + ex.Message);
}
}
private void BtnPermission_Click(object sender, RoutedEventArgs e)
{
if (PermissionPopup == null)
{
return;
}
PermissionItems.Children.Clear();
(string, string, string, string)[] array = new(string, string, string, string)[3]
{
("Ask", "\ue8d7", "매번 확인 — 파일 접근 시 사용자에게 묻습니다", "#4B5EFC"),
("Auto", "\ue73e", "자동 허용 — 파일을 자동으로 읽고 씁니다", "#DD6B20"),
("Deny", "\ue711", "접근 차단 — 파일 접근을 허용하지 않습니다", "#C50F1F")
};
string filePermission = _settings.Settings.Llm.FilePermission;
(string, string, string, string)[] array2 = array;
for (int i = 0; i < array2.Length; i++)
{
(string, string, string, string) tuple = array2[i];
string item = tuple.Item1;
string item2 = tuple.Item2;
string item3 = tuple.Item3;
string item4 = tuple.Item4;
bool isChecked = item.Equals(filePermission, StringComparison.OrdinalIgnoreCase);
ControlTemplate controlTemplate = new ControlTemplate(typeof(System.Windows.Controls.Button));
FrameworkElementFactory frameworkElementFactory = new FrameworkElementFactory(typeof(Border));
frameworkElementFactory.SetValue(Border.BackgroundProperty, System.Windows.Media.Brushes.Transparent);
frameworkElementFactory.SetValue(Border.CornerRadiusProperty, new CornerRadius(8.0));
frameworkElementFactory.SetValue(Border.PaddingProperty, new Thickness(12.0, 8.0, 12.0, 8.0));
frameworkElementFactory.Name = "Bd";
FrameworkElementFactory frameworkElementFactory2 = new FrameworkElementFactory(typeof(ContentPresenter));
frameworkElementFactory2.SetValue(FrameworkElement.HorizontalAlignmentProperty, System.Windows.HorizontalAlignment.Left);
frameworkElementFactory2.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
frameworkElementFactory.AppendChild(frameworkElementFactory2);
controlTemplate.VisualTree = frameworkElementFactory;
Trigger trigger = new Trigger
{
Property = UIElement.IsMouseOverProperty,
Value = true
};
trigger.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue)), "Bd"));
controlTemplate.Triggers.Add(trigger);
System.Windows.Controls.Button button = new System.Windows.Controls.Button
{
Template = controlTemplate,
BorderThickness = new Thickness(0.0),
Cursor = System.Windows.Input.Cursors.Hand,
HorizontalContentAlignment = System.Windows.HorizontalAlignment.Left,
Margin = new Thickness(0.0, 1.0, 0.0, 1.0)
};
ApplyHoverScaleAnimation(button, 1.02);
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(CreateCheckIcon(isChecked));
stackPanel.Children.Add(new TextBlock
{
Text = item2,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 14.0,
Foreground = BrushFromHex(item4),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 10.0, 0.0)
});
StackPanel stackPanel2 = new StackPanel();
stackPanel2.Children.Add(new TextBlock
{
Text = item,
FontSize = 13.0,
FontWeight = FontWeights.Bold,
Foreground = BrushFromHex(item4)
});
stackPanel2.Children.Add(new TextBlock
{
Text = item3,
FontSize = 11.0,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
TextWrapping = TextWrapping.Wrap,
MaxWidth = 220.0
});
stackPanel.Children.Add(stackPanel2);
button.Content = stackPanel;
string capturedLevel = item;
button.Click += delegate
{
_settings.Settings.Llm.FilePermission = capturedLevel;
UpdatePermissionUI();
SaveConversationSettings();
PermissionPopup.IsOpen = false;
};
PermissionItems.Children.Add(button);
}
PermissionPopup.IsOpen = true;
}
private void BtnAutoWarningClose_Click(object sender, RoutedEventArgs e)
{
_autoWarningDismissed = true;
if (AutoPermissionWarning != null)
{
AutoPermissionWarning.Visibility = Visibility.Collapsed;
}
}
private void UpdatePermissionUI()
{
if (PermissionLabel == null || PermissionIcon == null)
{
return;
}
string filePermission = _settings.Settings.Llm.FilePermission;
PermissionLabel.Text = filePermission;
TextBlock permissionIcon = PermissionIcon;
if (1 == 0)
{
}
string text = ((filePermission == "Auto") ? "\ue73e" : ((!(filePermission == "Deny")) ? "\ue8d7" : "\ue711"));
if (1 == 0)
{
}
permissionIcon.Text = text;
if (filePermission == "Auto")
{
SolidColorBrush foreground = new SolidColorBrush(System.Windows.Media.Color.FromRgb(221, 107, 32));
PermissionLabel.Foreground = foreground;
PermissionIcon.Foreground = foreground;
ChatConversation currentConversation;
lock (_convLock)
{
currentConversation = _currentConversation;
}
bool flag = currentConversation != null && currentConversation.Messages.Count > 0 && currentConversation.Permission == "Auto";
if (AutoPermissionWarning != null && !_autoWarningDismissed && !flag)
{
AutoPermissionWarning.Visibility = Visibility.Visible;
}
}
else
{
_autoWarningDismissed = false;
System.Windows.Media.Brush foreground2 = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
SolidColorBrush foreground3 = ((filePermission == "Deny") ? new SolidColorBrush(System.Windows.Media.Color.FromRgb(197, 15, 31)) : new SolidColorBrush(System.Windows.Media.Color.FromRgb(75, 94, 252)));
PermissionLabel.Foreground = foreground2;
PermissionIcon.Foreground = foreground3;
if (AutoPermissionWarning != null)
{
AutoPermissionWarning.Visibility = Visibility.Collapsed;
}
}
}
private void BtnDataUsage_Click(object sender, MouseButtonEventArgs e)
{
if (DataUsagePopup == null)
{
return;
}
DataUsageItems.Children.Clear();
(string, string, string, string, string)[] array = new(string, string, string, string, string)[3]
{
("active", "\ue9f5", "적극 활용", "폴더 내 문서를 자동 탐색하여 보고서 작성에 적극 활용합니다", "#107C10"),
("passive", "\ue8fd", "소극 활용", "사용자가 요청할 때만 폴더 데이터를 참조합니다", "#D97706"),
("none", "\ue8d8", "활용하지 않음", "폴더 내 문서를 읽거나 참조하지 않습니다", "#9CA3AF")
};
(string, string, string, string, string)[] array2 = array;
for (int i = 0; i < array2.Length; i++)
{
(string, string, string, string, string) tuple = array2[i];
string item = tuple.Item1;
string item2 = tuple.Item2;
string item3 = tuple.Item3;
string item4 = tuple.Item4;
string item5 = tuple.Item5;
bool isChecked = item.Equals(_folderDataUsage, StringComparison.OrdinalIgnoreCase);
ControlTemplate controlTemplate = new ControlTemplate(typeof(System.Windows.Controls.Button));
FrameworkElementFactory frameworkElementFactory = new FrameworkElementFactory(typeof(Border));
frameworkElementFactory.SetValue(Border.BackgroundProperty, System.Windows.Media.Brushes.Transparent);
frameworkElementFactory.SetValue(Border.CornerRadiusProperty, new CornerRadius(8.0));
frameworkElementFactory.SetValue(Border.PaddingProperty, new Thickness(12.0, 8.0, 12.0, 8.0));
frameworkElementFactory.Name = "Bd";
FrameworkElementFactory frameworkElementFactory2 = new FrameworkElementFactory(typeof(ContentPresenter));
frameworkElementFactory2.SetValue(FrameworkElement.HorizontalAlignmentProperty, System.Windows.HorizontalAlignment.Left);
frameworkElementFactory2.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
frameworkElementFactory.AppendChild(frameworkElementFactory2);
controlTemplate.VisualTree = frameworkElementFactory;
Trigger trigger = new Trigger
{
Property = UIElement.IsMouseOverProperty,
Value = true
};
trigger.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue)), "Bd"));
controlTemplate.Triggers.Add(trigger);
System.Windows.Controls.Button button = new System.Windows.Controls.Button
{
Template = controlTemplate,
BorderThickness = new Thickness(0.0),
Cursor = System.Windows.Input.Cursors.Hand,
HorizontalContentAlignment = System.Windows.HorizontalAlignment.Left,
Margin = new Thickness(0.0, 1.0, 0.0, 1.0)
};
ApplyHoverScaleAnimation(button, 1.02);
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(CreateCheckIcon(isChecked));
stackPanel.Children.Add(new TextBlock
{
Text = item2,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 14.0,
Foreground = BrushFromHex(item5),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 10.0, 0.0)
});
StackPanel stackPanel2 = new StackPanel();
stackPanel2.Children.Add(new TextBlock
{
Text = item3,
FontSize = 13.0,
FontWeight = FontWeights.Bold,
Foreground = BrushFromHex(item5)
});
stackPanel2.Children.Add(new TextBlock
{
Text = item4,
FontSize = 11.0,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
TextWrapping = TextWrapping.Wrap,
MaxWidth = 240.0
});
stackPanel.Children.Add(stackPanel2);
button.Content = stackPanel;
string capturedKey = item;
button.Click += delegate
{
_folderDataUsage = capturedKey;
UpdateDataUsageUI();
SaveConversationSettings();
DataUsagePopup.IsOpen = false;
};
DataUsageItems.Children.Add(button);
}
DataUsagePopup.IsOpen = true;
}
private void UpdateDataUsageUI()
{
if (DataUsageLabel != null && DataUsageIcon != null)
{
string folderDataUsage = _folderDataUsage;
if (1 == 0)
{
}
(string, string, string) tuple = ((folderDataUsage == "passive") ? ("소극", "\ue8fd", "#D97706") : ((!(folderDataUsage == "none")) ? ("적극", "\ue9f5", "#107C10") : ("미사용", "\ue8d8", "#9CA3AF")));
if (1 == 0)
{
}
var (text, text2, hex) = tuple;
DataUsageLabel.Text = text;
DataUsageIcon.Text = text2;
DataUsageIcon.Foreground = BrushFromHex(hex);
}
}
private void ApplyTabDefaultPermission()
{
if (_activeTab == "Chat")
{
_settings.Settings.Llm.FilePermission = "Ask";
UpdatePermissionUI();
return;
}
string defaultAgentPermission = _settings.Settings.Llm.DefaultAgentPermission;
if (!string.IsNullOrEmpty(defaultAgentPermission))
{
_settings.Settings.Llm.FilePermission = defaultAgentPermission;
UpdatePermissionUI();
}
}
private void BtnAttach_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog
{
Multiselect = true,
Title = "첨부할 파일을 선택하세요",
Filter = "모든 파일 (*.*)|*.*|텍스트 (*.txt;*.md;*.csv)|*.txt;*.md;*.csv|코드 (*.cs;*.py;*.js;*.ts)|*.cs;*.py;*.js;*.ts"
};
string currentWorkFolder = GetCurrentWorkFolder();
if (!string.IsNullOrEmpty(currentWorkFolder) && Directory.Exists(currentWorkFolder))
{
openFileDialog.InitialDirectory = currentWorkFolder;
}
if (openFileDialog.ShowDialog() == true)
{
string[] fileNames = openFileDialog.FileNames;
foreach (string filePath in fileNames)
{
AddAttachedFile(filePath);
}
}
}
private void AddAttachedFile(string filePath)
{
if (_attachedFiles.Contains(filePath))
{
return;
}
try
{
FileInfo fileInfo = new FileInfo(filePath);
if (fileInfo.Length > 10485760)
{
CustomMessageBox.Show("파일이 너무 큽니다 (10MB 초과):\n" + fileInfo.Name, "첨부 제한", MessageBoxButton.OK, MessageBoxImage.Exclamation);
return;
}
string text = fileInfo.Extension.ToLowerInvariant();
if (ImageExtensions.Contains(text) && _settings.Settings.Llm.EnableImageInput)
{
int num = _settings.Settings.Llm.MaxImageSizeKb;
if (num <= 0)
{
num = 5120;
}
if (fileInfo.Length > num * 1024)
{
CustomMessageBox.Show($"이미지가 너무 큽니다 ({fileInfo.Length / 1024}KB, 최대 {num}KB).", "이미지 크기 초과", MessageBoxButton.OK, MessageBoxImage.Exclamation);
return;
}
byte[] inArray = File.ReadAllBytes(filePath);
if (1 == 0)
{
}
string text2;
switch (text)
{
case ".jpg":
case ".jpeg":
text2 = "image/jpeg";
break;
case ".gif":
text2 = "image/gif";
break;
case ".bmp":
text2 = "image/bmp";
break;
case ".webp":
text2 = "image/webp";
break;
default:
text2 = "image/png";
break;
}
if (1 == 0)
{
}
string mimeType = text2;
ImageAttachment attachment = new ImageAttachment
{
Base64 = Convert.ToBase64String(inArray),
MimeType = mimeType,
FileName = fileInfo.Name
};
if (!_pendingImages.Any((ImageAttachment i) => i.FileName == attachment.FileName))
{
_pendingImages.Add(attachment);
AddImagePreview(attachment);
}
return;
}
}
catch
{
return;
}
_attachedFiles.Add(filePath);
RefreshAttachedFilesUI();
}
private void RemoveAttachedFile(string filePath)
{
_attachedFiles.Remove(filePath);
RefreshAttachedFilesUI();
}
private void RefreshAttachedFilesUI()
{
AttachedFilesPanel.Items.Clear();
if (_attachedFiles.Count == 0)
{
AttachedFilesPanel.Visibility = Visibility.Collapsed;
return;
}
AttachedFilesPanel.Visibility = Visibility.Visible;
System.Windows.Media.Brush foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush background = (TryFindResource("HintBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.LightGray;
foreach (string item in _attachedFiles.ToList())
{
string fileName = System.IO.Path.GetFileName(item);
string capturedFile = item;
Border border = new Border
{
Background = background,
CornerRadius = new CornerRadius(6.0),
Padding = new Thickness(8.0, 4.0, 4.0, 4.0),
Margin = new Thickness(0.0, 0.0, 4.0, 4.0),
Cursor = System.Windows.Input.Cursors.Hand
};
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(new TextBlock
{
Text = "\ue8a5",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 10.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 4.0, 0.0)
});
stackPanel.Children.Add(new TextBlock
{
Text = fileName,
FontSize = 11.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center,
MaxWidth = 150.0,
TextTrimming = TextTrimming.CharacterEllipsis,
ToolTip = item
});
System.Windows.Controls.Button button = new System.Windows.Controls.Button
{
Content = new TextBlock
{
Text = "\ue711",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 8.0,
Foreground = foreground
},
Background = System.Windows.Media.Brushes.Transparent,
BorderThickness = new Thickness(0.0),
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(4.0, 2.0, 4.0, 2.0),
Margin = new Thickness(2.0, 0.0, 0.0, 0.0)
};
button.Click += delegate
{
RemoveAttachedFile(capturedFile);
};
stackPanel.Children.Add(button);
border.Child = stackPanel;
AttachedFilesPanel.Items.Add(border);
}
}
private string BuildFileContextPrompt()
{
if (_attachedFiles.Count == 0)
{
return "";
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("\n[첨부 파일 컨텍스트]");
foreach (string attachedFile in _attachedFiles)
{
try
{
bool flag;
switch (System.IO.Path.GetExtension(attachedFile).ToLowerInvariant())
{
case ".exe":
case ".dll":
case ".zip":
case ".7z":
case ".rar":
case ".tar":
case ".gz":
case ".png":
case ".jpg":
case ".jpeg":
case ".gif":
case ".bmp":
case ".ico":
case ".webp":
case ".svg":
case ".pdf":
case ".docx":
case ".xlsx":
case ".pptx":
case ".doc":
case ".xls":
case ".ppt":
case ".mp3":
case ".mp4":
case ".avi":
case ".mov":
case ".mkv":
case ".wav":
case ".flac":
case ".psd":
case ".ai":
case ".sketch":
case ".fig":
case ".msi":
case ".iso":
case ".img":
case ".bin":
case ".dat":
case ".db":
case ".sqlite":
flag = true;
break;
default:
flag = false;
break;
}
StringBuilder stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler;
if (flag)
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(26, 1, stringBuilder2);
handler.AppendLiteral("\n--- ");
handler.AppendFormatted(System.IO.Path.GetFileName(attachedFile));
handler.AppendLiteral(" (바이너리 파일, 내용 생략) ---");
stringBuilder3.AppendLine(ref handler);
continue;
}
string text = File.ReadAllText(attachedFile);
if (text.Length > 8000)
{
text = text.Substring(0, 8000) + "\n... (이하 생략)";
}
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
handler.AppendLiteral("\n--- ");
handler.AppendFormatted(System.IO.Path.GetFileName(attachedFile));
handler.AppendLiteral(" ---");
stringBuilder4.AppendLine(ref handler);
stringBuilder.AppendLine(text);
}
catch (Exception ex)
{
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(19, 2, stringBuilder2);
handler.AppendLiteral("\n--- ");
handler.AppendFormatted(System.IO.Path.GetFileName(attachedFile));
handler.AppendLiteral(" (읽기 실패: ");
handler.AppendFormatted(ex.Message);
handler.AppendLiteral(") ---");
stringBuilder5.AppendLine(ref handler);
}
}
return stringBuilder.ToString();
}
private void ResizeGrip_DragDelta(object sender, DragDeltaEventArgs e)
{
double num = base.Width + e.HorizontalChange;
double num2 = base.Height + e.VerticalChange;
if (num >= base.MinWidth)
{
base.Width = num;
}
if (num2 >= base.MinHeight)
{
base.Height = num2;
}
}
private void BtnToggleSidebar_Click(object sender, RoutedEventArgs e)
{
_sidebarVisible = !_sidebarVisible;
if (_sidebarVisible)
{
IconBarColumn.Width = new GridLength(0.0);
IconBarPanel.Visibility = Visibility.Collapsed;
SidebarPanel.Visibility = Visibility.Visible;
ToggleSidebarIcon.Text = "\ue76b";
AnimateSidebar(0.0, 270.0, delegate
{
SidebarColumn.MinWidth = 200.0;
});
}
else
{
SidebarColumn.MinWidth = 0.0;
ToggleSidebarIcon.Text = "\ue76c";
AnimateSidebar(270.0, 0.0, delegate
{
SidebarPanel.Visibility = Visibility.Collapsed;
IconBarColumn.Width = new GridLength(52.0);
IconBarPanel.Visibility = Visibility.Visible;
});
}
}
private void AnimateSidebar(double from, double to, Action? onComplete = null)
{
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Expected O, but got Unknown
double duration = 200.0;
DateTime start = DateTime.UtcNow;
DispatcherTimer timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(10.0)
};
EventHandler tickHandler = null;
tickHandler = delegate
{
double totalMilliseconds = (DateTime.UtcNow - start).TotalMilliseconds;
double num = Math.Min(totalMilliseconds / duration, 1.0);
num = 1.0 - (1.0 - num) * (1.0 - num);
SidebarColumn.Width = new GridLength(from + (to - from) * num);
if (totalMilliseconds >= duration)
{
timer.Stop();
timer.Tick -= tickHandler;
SidebarColumn.Width = new GridLength(to);
onComplete?.Invoke();
}
};
timer.Tick += tickHandler;
timer.Start();
}
public void RefreshConversationList()
{
List<ChatConversation> source = _storage.LoadAllMeta();
IEnumerable<TopicPreset> enumerable = PresetService.GetByTabWithCustom("Cowork", _settings.Settings.Llm.CustomPresets).Concat(PresetService.GetByTabWithCustom("Code", _settings.Settings.Llm.CustomPresets)).Concat(PresetService.GetByTabWithCustom("Chat", _settings.Settings.Llm.CustomPresets));
Dictionary<string, (string Symbol, string Color)> presetMap = new Dictionary<string, (string, string)>(StringComparer.OrdinalIgnoreCase);
foreach (TopicPreset item in enumerable)
{
presetMap.TryAdd(item.Category, (item.Symbol, item.Color));
}
List<ConversationMeta> source2 = source.Select(delegate(ChatConversation c)
{
string text = ChatCategory.GetSymbol(c.Category);
string text2 = ChatCategory.GetColor(c.Category);
if (text == "\ue8bd" && text2 == "#6B7280" && c.Category != "일반" && presetMap.TryGetValue(c.Category, out (string, string) value))
{
(text, text2) = value;
}
return new ConversationMeta
{
Id = c.Id,
Title = c.Title,
Pinned = c.Pinned,
Category = c.Category,
Symbol = text,
ColorHex = text2,
Tab = (string.IsNullOrEmpty(c.Tab) ? "Chat" : c.Tab),
UpdatedAtText = FormatDate(c.UpdatedAt),
UpdatedAt = c.UpdatedAt,
Preview = (c.Preview ?? ""),
ParentId = c.ParentId,
ExecutionEventCount = (c.ExecutionEvents?.Count ?? 0),
ErrorEventCount = (c.ExecutionEvents?.Count((ChatExecutionEvent x) => string.Equals(x.Type, "Error", StringComparison.OrdinalIgnoreCase)) ?? 0)
};
}).ToList();
source2 = source2.Where((ConversationMeta i) => i.Tab == _activeTab).ToList();
if (_selectedCategory == "__custom__")
{
HashSet<string> customCats = _settings.Settings.Llm.CustomPresets.Select((CustomPresetEntry c) => "custom_" + c.Id).ToHashSet();
source2 = source2.Where((ConversationMeta i) => customCats.Contains(i.Category)).ToList();
}
else if (!string.IsNullOrEmpty(_selectedCategory))
{
source2 = source2.Where((ConversationMeta i) => i.Category == _selectedCategory).ToList();
}
string search = SearchBox?.Text?.Trim() ?? "";
if (!string.IsNullOrEmpty(search))
{
source2 = source2.Where((ConversationMeta i) => i.Title.Contains(search, StringComparison.OrdinalIgnoreCase) || i.Preview.Contains(search, StringComparison.OrdinalIgnoreCase)).ToList();
}
RenderConversationList(source2);
}
private void RenderConversationList(List<ConversationMeta> items)
{
ConversationPanel.Children.Clear();
_pendingConversations = null;
if (items.Count == 0)
{
TextBlock textBlock = new TextBlock();
textBlock.Text = "대화가 없습니다";
textBlock.FontSize = 12.0;
textBlock.Foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
textBlock.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
textBlock.Margin = new Thickness(0.0, 20.0, 0.0, 0.0);
TextBlock element = textBlock;
ConversationPanel.Children.Add(element);
return;
}
DateTime today = DateTime.Today;
List<ConversationMeta> list = items.Where((ConversationMeta i) => i.UpdatedAt.Date == today).ToList();
List<ConversationMeta> list2 = items.Where((ConversationMeta i) => i.UpdatedAt.Date < today).ToList();
List<(string, ConversationMeta)> list3 = new List<(string, ConversationMeta)>();
foreach (ConversationMeta item2 in list)
{
list3.Add(("오늘", item2));
}
foreach (ConversationMeta item3 in list2)
{
list3.Add(("이전", item3));
}
List<(string, ConversationMeta)> list4 = list3.Take(50).ToList();
string text = null;
foreach (var (text2, item) in list4)
{
if (text2 != text)
{
AddGroupHeader(text2);
text = text2;
}
AddConversationItem(item);
}
if (list3.Count > 50)
{
_pendingConversations = items;
AddLoadMoreButton(list3.Count - 50);
}
}
private void AddLoadMoreButton(int remaining)
{
System.Windows.Media.Brush brush = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush foreground = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
Border border = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(8.0),
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(8.0, 10.0, 8.0, 10.0),
Margin = new Thickness(6.0, 4.0, 6.0, 4.0),
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch
};
StackPanel stackPanel = new StackPanel
{
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
};
stackPanel.Children.Add(new TextBlock
{
Text = $"더 보기 ({remaining}개 남음)",
FontSize = 12.0,
Foreground = foreground,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
});
border.Child = stackPanel;
border.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(18, byte.MaxValue, byte.MaxValue, byte.MaxValue));
}
};
border.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = System.Windows.Media.Brushes.Transparent;
}
};
border.MouseLeftButtonUp += delegate
{
if (_pendingConversations != null)
{
List<ConversationMeta> pendingConversations = _pendingConversations;
_pendingConversations = null;
ConversationPanel.Children.Clear();
DateTime today = DateTime.Today;
List<ConversationMeta> list = pendingConversations.Where((ConversationMeta i) => i.UpdatedAt.Date == today).ToList();
List<ConversationMeta> list2 = pendingConversations.Where((ConversationMeta i) => i.UpdatedAt.Date < today).ToList();
if (list.Count > 0)
{
AddGroupHeader("오늘");
foreach (ConversationMeta item in list)
{
AddConversationItem(item);
}
}
if (list2.Count > 0)
{
AddGroupHeader("이전");
foreach (ConversationMeta item2 in list2)
{
AddConversationItem(item2);
}
}
}
};
ConversationPanel.Children.Add(border);
}
private void AddGroupHeader(string text)
{
TextBlock textBlock = new TextBlock();
textBlock.Text = text;
textBlock.FontSize = 11.0;
textBlock.FontWeight = FontWeights.SemiBold;
textBlock.Foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
textBlock.Margin = new Thickness(8.0, 12.0, 0.0, 4.0);
TextBlock element = textBlock;
ConversationPanel.Children.Add(element);
}
private void AddConversationItem(ConversationMeta item)
{
//IL_0846: Unknown result type (might be due to invalid IL or missing references)
bool isSelected = false;
lock (_convLock)
{
isSelected = _currentConversation?.Id == item.Id;
}
bool flag = !string.IsNullOrEmpty(item.ParentId);
Border border = new Border
{
Background = (isSelected ? new SolidColorBrush(System.Windows.Media.Color.FromArgb(48, 75, 94, 252)) : System.Windows.Media.Brushes.Transparent),
CornerRadius = new CornerRadius(8.0),
Padding = new Thickness(10.0, 8.0, 10.0, 8.0),
Margin = (flag ? new Thickness(16.0, 1.0, 0.0, 1.0) : new Thickness(0.0, 1.0, 0.0, 1.0)),
Cursor = System.Windows.Input.Cursors.Hand
};
Grid grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(28.0)
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(1.0, GridUnitType.Star)
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = GridLength.Auto
});
System.Windows.Media.Brush foreground;
if (item.Pinned)
{
foreground = System.Windows.Media.Brushes.Orange;
}
else
{
try
{
foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(item.ColorHex));
}
catch
{
foreground = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue;
}
}
string text = (item.Pinned ? "\ue718" : ((!string.IsNullOrEmpty(item.ParentId)) ? "\ue8a5" : item.Symbol));
if (!string.IsNullOrEmpty(item.ParentId))
{
foreground = new SolidColorBrush(System.Windows.Media.Color.FromRgb(139, 92, 246));
}
TextBlock element = new TextBlock
{
Text = text,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 13.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(element, 0);
grid.Children.Add(element);
System.Windows.Media.Brush titleColor = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush foreground2 = (TryFindResource("HintText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.DarkGray;
StackPanel stackPanel = new StackPanel
{
VerticalAlignment = VerticalAlignment.Center
};
TextBlock title = new TextBlock
{
Text = item.Title,
FontSize = 12.5,
Foreground = titleColor,
TextTrimming = TextTrimming.CharacterEllipsis
};
TextBlock element2 = new TextBlock
{
Text = item.UpdatedAtText,
FontSize = 10.0,
Foreground = foreground2,
Margin = new Thickness(0.0, 2.0, 0.0, 0.0)
};
StackPanel stackPanel2 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
Margin = new Thickness(0.0, 4.0, 0.0, 0.0),
Visibility = ((item.ExecutionEventCount <= 0) ? Visibility.Collapsed : Visibility.Visible)
};
stackPanel2.Children.Add(new Border
{
Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, 37, 99, 235)),
CornerRadius = new CornerRadius(4.0),
Padding = new Thickness(5.0, 1.0, 5.0, 1.0),
Margin = new Thickness(0.0, 0.0, 6.0, 0.0),
Child = new TextBlock
{
Text = $"실행 {item.ExecutionEventCount}",
FontSize = 9.5,
Foreground = new SolidColorBrush(System.Windows.Media.Color.FromRgb(37, 99, 235))
}
});
if (item.ErrorEventCount > 0)
{
stackPanel2.Children.Add(new Border
{
Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, 220, 38, 38)),
CornerRadius = new CornerRadius(4.0),
Padding = new Thickness(5.0, 1.0, 5.0, 1.0),
Child = new TextBlock
{
Text = $"오류 {item.ErrorEventCount}",
FontSize = 9.5,
Foreground = new SolidColorBrush(System.Windows.Media.Color.FromRgb(220, 38, 38))
}
});
}
stackPanel.Children.Add(title);
stackPanel.Children.Add(element2);
stackPanel.Children.Add(stackPanel2);
Grid.SetColumn(stackPanel, 1);
grid.Children.Add(stackPanel);
System.Windows.Controls.Button catBtn = new System.Windows.Controls.Button
{
Content = new TextBlock
{
Text = "\ue70f",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 10.0,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray)
},
Background = System.Windows.Media.Brushes.Transparent,
BorderThickness = new Thickness(0.0),
Cursor = System.Windows.Input.Cursors.Hand,
VerticalAlignment = VerticalAlignment.Center,
Visibility = Visibility.Collapsed,
Padding = new Thickness(4.0),
ToolTip = ((_activeTab == "Cowork") ? "작업 유형" : "대화 주제 변경")
};
string capturedId = item.Id;
catBtn.Click += delegate
{
ShowConversationMenu(capturedId);
};
Grid.SetColumn(catBtn, 2);
grid.Children.Add(catBtn);
if (isSelected)
{
border.BorderBrush = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
border.BorderThickness = new Thickness(2.0, 0.0, 0.0, 0.0);
}
border.Child = grid;
border.RenderTransformOrigin = new Point(0.5, 0.5);
border.RenderTransform = new ScaleTransform(1.0, 1.0);
SolidColorBrush solidColorBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(48, 75, 94, 252));
border.MouseEnter += delegate
{
if (!isSelected)
{
border.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(21, byte.MaxValue, byte.MaxValue, byte.MaxValue));
}
catBtn.Visibility = Visibility.Visible;
ScaleTransform scaleTransform = border.RenderTransform as ScaleTransform;
scaleTransform?.BeginAnimation(ScaleTransform.ScaleXProperty, new DoubleAnimation(1.02, TimeSpan.FromMilliseconds(120.0)));
scaleTransform?.BeginAnimation(ScaleTransform.ScaleYProperty, new DoubleAnimation(1.02, TimeSpan.FromMilliseconds(120.0)));
};
border.MouseLeave += delegate
{
if (!isSelected)
{
border.Background = System.Windows.Media.Brushes.Transparent;
}
catBtn.Visibility = Visibility.Collapsed;
ScaleTransform scaleTransform = border.RenderTransform as ScaleTransform;
scaleTransform?.BeginAnimation(ScaleTransform.ScaleXProperty, new DoubleAnimation(1.0, TimeSpan.FromMilliseconds(150.0)));
scaleTransform?.BeginAnimation(ScaleTransform.ScaleYProperty, new DoubleAnimation(1.0, TimeSpan.FromMilliseconds(150.0)));
};
border.MouseLeftButtonDown += delegate
{
try
{
if (isSelected)
{
EnterTitleEditMode(title, item.Id, titleColor);
}
else
{
if (_isStreaming)
{
_streamCts?.Cancel();
_cursorTimer.Stop();
_typingTimer.Stop();
_elapsedTimer.Stop();
_activeStreamText = null;
_elapsedLabel = null;
_isStreaming = false;
}
ChatConversation chatConversation = _storage.Load(item.Id);
if (chatConversation != null)
{
if (string.IsNullOrEmpty(chatConversation.Tab))
{
chatConversation.Tab = _activeTab;
}
lock (_convLock)
{
_currentConversation = chatConversation;
}
_tabConversationId[_activeTab] = chatConversation.Id;
UpdateChatTitle();
RenderMessages();
RefreshConversationList();
}
}
}
catch (Exception ex)
{
LogService.Error("대화 전환 오류: " + ex.Message);
}
};
border.MouseRightButtonUp += delegate(object _, MouseButtonEventArgs me)
{
me.Handled = true;
if (!isSelected)
{
ChatConversation chatConversation = _storage.Load(item.Id);
if (chatConversation != null)
{
if (string.IsNullOrEmpty(chatConversation.Tab))
{
chatConversation.Tab = _activeTab;
}
lock (_convLock)
{
_currentConversation = chatConversation;
}
_tabConversationId[_activeTab] = chatConversation.Id;
UpdateChatTitle();
RenderMessages();
}
}
((DispatcherObject)this).Dispatcher.BeginInvoke((Delegate)(Action)delegate
{
ShowConversationMenu(item.Id);
}, (DispatcherPriority)5, Array.Empty<object>());
};
ConversationPanel.Children.Add(border);
}
private void EnterTitleEditMode(TextBlock titleTb, string conversationId, System.Windows.Media.Brush titleColor)
{
try
{
StackPanel parent = titleTb.Parent as StackPanel;
if (parent == null)
{
return;
}
int num = parent.Children.IndexOf(titleTb);
if (num < 0)
{
return;
}
System.Windows.Controls.TextBox editBox = new System.Windows.Controls.TextBox
{
Text = titleTb.Text,
FontSize = 12.5,
Foreground = titleColor,
Background = System.Windows.Media.Brushes.Transparent,
BorderBrush = ((TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue),
BorderThickness = new Thickness(0.0, 0.0, 0.0, 1.0),
CaretBrush = titleColor,
Padding = new Thickness(0.0),
Margin = new Thickness(0.0)
};
parent.Children.RemoveAt(num);
parent.Children.Insert(num, editBox);
bool committed = false;
editBox.KeyDown += delegate(object _, System.Windows.Input.KeyEventArgs ke)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Invalid comparison between Unknown and I4
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Invalid comparison between Unknown and I4
if ((int)ke.Key == 6)
{
ke.Handled = true;
CommitEdit();
}
if ((int)ke.Key == 13)
{
ke.Handled = true;
CancelEdit();
}
};
editBox.LostFocus += delegate
{
CommitEdit();
};
editBox.Focus();
editBox.SelectAll();
void CancelEdit()
{
if (committed)
{
return;
}
committed = true;
try
{
int num2 = parent.Children.IndexOf(editBox);
if (num2 >= 0)
{
parent.Children.RemoveAt(num2);
parent.Children.Insert(num2, titleTb);
}
}
catch
{
}
}
void CommitEdit()
{
if (!committed)
{
committed = true;
string text = editBox.Text.Trim();
if (string.IsNullOrEmpty(text))
{
text = titleTb.Text;
}
titleTb.Text = text;
try
{
int num2 = parent.Children.IndexOf(editBox);
if (num2 >= 0)
{
parent.Children.RemoveAt(num2);
parent.Children.Insert(num2, titleTb);
}
}
catch
{
}
ChatConversation chatConversation = _storage.Load(conversationId);
if (chatConversation != null)
{
chatConversation.Title = text;
_storage.Save(chatConversation);
lock (_convLock)
{
if (_currentConversation?.Id == conversationId)
{
_currentConversation.Title = text;
}
}
UpdateChatTitle();
}
}
}
}
catch (Exception ex)
{
LogService.Error("제목 편집 오류: " + ex.Message);
}
}
private void ShowConversationMenu(string conversationId)
{
ChatConversation conv = _storage.Load(conversationId);
bool flag = conv?.Pinned ?? false;
System.Windows.Media.Brush background = (TryFindResource("LauncherBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromRgb(26, 27, 46));
System.Windows.Media.Brush borderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush primaryText = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush brush = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush hoverBg = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromArgb(30, byte.MaxValue, byte.MaxValue, byte.MaxValue));
Popup popup = new Popup
{
StaysOpen = false,
AllowsTransparency = true,
PopupAnimation = PopupAnimation.Fade,
Placement = PlacementMode.MousePoint
};
Border border = new Border
{
Background = background,
BorderBrush = borderBrush,
BorderThickness = new Thickness(1.0),
CornerRadius = new CornerRadius(12.0),
Padding = new Thickness(6.0),
MinWidth = 200.0,
Effect = new DropShadowEffect
{
BlurRadius = 16.0,
ShadowDepth = 4.0,
Opacity = 0.3,
Color = Colors.Black
}
};
StackPanel stackPanel = new StackPanel();
stackPanel.Children.Add(CreateMenuItem(flag ? "\ue77a" : "\ue718", flag ? "고정 해제" : "상단 고정", (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue, delegate
{
ChatConversation chatConversation = _storage.Load(conversationId);
if (chatConversation != null)
{
chatConversation.Pinned = !chatConversation.Pinned;
_storage.Save(chatConversation);
lock (_convLock)
{
if (_currentConversation?.Id == conversationId)
{
_currentConversation.Pinned = chatConversation.Pinned;
}
}
RefreshConversationList();
}
}));
stackPanel.Children.Add(CreateMenuItem("\ue8ac", "이름 변경", brush, delegate
{
foreach (UIElement child2 in ConversationPanel.Children)
{
if (child2 is Border { Child: Grid child })
{
foreach (UIElement child3 in child.Children)
{
if (child3 is StackPanel stackPanel3 && stackPanel3.Children.Count > 0 && stackPanel3.Children[0] is TextBlock textBlock && conv != null && textBlock.Text == conv.Title)
{
System.Windows.Media.Brush titleColor = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
EnterTitleEditMode(textBlock, conversationId, titleColor);
return;
}
}
}
}
}));
if ((_activeTab == "Cowork" || _activeTab == "Code") && conv != null)
{
string catKey = conv.Category ?? "일반";
string text = "\ue8bd";
string text2 = catKey;
string value = "#6B7280";
(string, string, string, string) tuple = ChatCategory.All.FirstOrDefault(((string Key, string Label, string Symbol, string Color) c) => c.Key == catKey);
(string, string, string, string) tuple2 = tuple;
if ((tuple2.Item1 != null || tuple2.Item2 != null || tuple2.Item3 != null || tuple2.Item4 != null) && tuple.Item1 != "일반")
{
text = tuple.Item3;
text2 = tuple.Item2;
value = tuple.Item4;
}
else
{
TopicPreset topicPreset = PresetService.GetByTabWithCustom(_activeTab, _settings.Settings.Llm.CustomPresets).FirstOrDefault((TopicPreset p) => p.Category == catKey);
if (topicPreset != null)
{
text = topicPreset.Symbol;
text2 = topicPreset.Label;
value = topicPreset.Color;
}
}
stackPanel.Children.Add(CreateSeparator());
StackPanel stackPanel2 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
Margin = new Thickness(10.0, 4.0, 10.0, 4.0)
};
try
{
SolidColorBrush foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(value));
stackPanel2.Children.Add(new TextBlock
{
Text = text,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 12.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 6.0, 0.0)
});
stackPanel2.Children.Add(new TextBlock
{
Text = text2,
FontSize = 12.0,
Foreground = primaryText,
VerticalAlignment = VerticalAlignment.Center
});
}
catch
{
stackPanel2.Children.Add(new TextBlock
{
Text = text2,
FontSize = 12.0,
Foreground = primaryText
});
}
stackPanel.Children.Add(stackPanel2);
}
if (_activeTab == "Chat")
{
stackPanel.Children.Add(CreateSeparator());
stackPanel.Children.Add(new TextBlock
{
Text = "분류 변경",
FontSize = 10.5,
Foreground = brush,
Margin = new Thickness(10.0, 4.0, 0.0, 4.0),
FontWeight = FontWeights.SemiBold
});
string text3 = conv?.Category ?? "일반";
System.Windows.Media.Brush color = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
(string, string, string, string)[] all = ChatCategory.All;
for (int num = 0; num < all.Length; num++)
{
(string, string, string, string) tuple3 = all[num];
string item = tuple3.Item1;
string item2 = tuple3.Item2;
string item3 = tuple3.Item3;
string item4 = tuple3.Item4;
string capturedKey = item;
bool flag2 = capturedKey == text3;
Border border2 = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(8.0),
Padding = new Thickness(10.0, 7.0, 10.0, 7.0),
Margin = new Thickness(0.0, 1.0, 0.0, 1.0),
Cursor = System.Windows.Input.Cursors.Hand
};
Grid grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(24.0)
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(1.0, GridUnitType.Star)
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(20.0)
});
TextBlock element = new TextBlock
{
Text = item3,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 12.0,
Foreground = BrushFromHex(item4),
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(element, 0);
grid.Children.Add(element);
TextBlock element2 = new TextBlock
{
Text = item2,
FontSize = 12.5,
Foreground = primaryText,
VerticalAlignment = VerticalAlignment.Center,
FontWeight = (flag2 ? FontWeights.Bold : FontWeights.Normal)
};
Grid.SetColumn(element2, 1);
grid.Children.Add(element2);
if (flag2)
{
FrameworkElement element3 = CreateSimpleCheck(color);
Grid.SetColumn(element3, 2);
grid.Children.Add(element3);
}
border2.Child = grid;
border2.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border3)
{
border3.Background = hoverBg;
}
};
border2.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border3)
{
border3.Background = System.Windows.Media.Brushes.Transparent;
}
};
border2.MouseLeftButtonUp += delegate
{
popup.IsOpen = false;
ChatConversation chatConversation = _storage.Load(conversationId);
if (chatConversation != null)
{
chatConversation.Category = capturedKey;
TopicPreset byCategory = PresetService.GetByCategory(capturedKey);
if (byCategory != null)
{
chatConversation.SystemCommand = byCategory.SystemPrompt;
}
_storage.Save(chatConversation);
lock (_convLock)
{
if (_currentConversation?.Id == conversationId)
{
_currentConversation.Category = capturedKey;
if (byCategory != null)
{
_currentConversation.SystemCommand = byCategory.SystemPrompt;
}
}
}
bool flag3;
lock (_convLock)
{
flag3 = _currentConversation?.Id == conversationId;
}
if (flag3 && byCategory != null && !string.IsNullOrEmpty(byCategory.Placeholder))
{
_promptCardPlaceholder = byCategory.Placeholder;
UpdateWatermarkVisibility();
if (string.IsNullOrEmpty(InputBox.Text))
{
InputWatermark.Text = byCategory.Placeholder;
InputWatermark.Visibility = Visibility.Visible;
}
}
else if (flag3)
{
ClearPromptCardPlaceholder();
}
RefreshConversationList();
}
};
stackPanel.Children.Add(border2);
}
}
stackPanel.Children.Add(CreateSeparator());
stackPanel.Children.Add(CreateMenuItem("\ue74d", "이 대화 삭제", System.Windows.Media.Brushes.IndianRed, delegate
{
MessageBoxResult messageBoxResult = CustomMessageBox.Show("이 대화를 삭제하시겠습니까?", "대화 삭제", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (messageBoxResult == MessageBoxResult.Yes)
{
_storage.Delete(conversationId);
lock (_convLock)
{
if (_currentConversation?.Id == conversationId)
{
_currentConversation = null;
MessagePanel.Children.Clear();
EmptyState.Visibility = Visibility.Visible;
UpdateChatTitle();
}
}
RefreshConversationList();
}
}));
border.Child = stackPanel;
popup.Child = border;
popup.IsOpen = true;
Border CreateMenuItem(string icon, string text4, System.Windows.Media.Brush iconColor, Action onClick)
{
Border border3 = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(8.0),
Padding = new Thickness(10.0, 7.0, 10.0, 7.0),
Margin = new Thickness(0.0, 1.0, 0.0, 1.0),
Cursor = System.Windows.Input.Cursors.Hand
};
Grid grid2 = new Grid
{
ColumnDefinitions =
{
new ColumnDefinition
{
Width = new GridLength(24.0)
},
new ColumnDefinition
{
Width = new GridLength(1.0, GridUnitType.Star)
}
}
};
TextBlock element4 = new TextBlock
{
Text = icon,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 12.0,
Foreground = iconColor,
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(element4, 0);
grid2.Children.Add(element4);
TextBlock element5 = new TextBlock
{
Text = text4,
FontSize = 12.5,
Foreground = primaryText,
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(element5, 1);
grid2.Children.Add(element5);
border3.Child = grid2;
border3.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border4)
{
border4.Background = hoverBg;
}
};
border3.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border4)
{
border4.Background = System.Windows.Media.Brushes.Transparent;
}
};
border3.MouseLeftButtonUp += delegate
{
popup.IsOpen = false;
onClick();
};
return border3;
}
Border CreateSeparator()
{
return new Border
{
Height = 1.0,
Background = borderBrush,
Opacity = 0.3,
Margin = new Thickness(8.0, 4.0, 8.0, 4.0)
};
}
}
private void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
{
RefreshConversationList();
}
private static string FormatDate(DateTime dt)
{
TimeSpan timeSpan = DateTime.Now - dt;
if (timeSpan.TotalMinutes < 1.0)
{
return "방금 전";
}
if (timeSpan.TotalHours < 1.0)
{
return $"{(int)timeSpan.TotalMinutes}분 전";
}
if (timeSpan.TotalDays < 1.0)
{
return $"{(int)timeSpan.TotalHours}시간 전";
}
if (timeSpan.TotalDays < 7.0)
{
return $"{(int)timeSpan.TotalDays}일 전";
}
return dt.ToString("MM/dd");
}
private void UpdateChatTitle()
{
lock (_convLock)
{
ChatTitle.Text = _currentConversation?.Title ?? "";
}
}
private void RenderMessages()
{
MessagePanel.Children.Clear();
UpdateExecutionHistoryUi();
ChatConversation currentConversation;
lock (_convLock)
{
currentConversation = _currentConversation;
}
if (currentConversation == null || (currentConversation.Messages.Count == 0 && currentConversation.ExecutionEvents.Count == 0))
{
EmptyState.Visibility = Visibility.Visible;
return;
}
EmptyState.Visibility = Visibility.Collapsed;
List<(DateTime, int, ChatMessage, ChatExecutionEvent)> list = new List<(DateTime, int, ChatMessage, ChatExecutionEvent)>();
int num = 0;
foreach (ChatMessage message in currentConversation.Messages)
{
if (!(message.Role == "system"))
{
list.Add((message.Timestamp, num++, message, null));
}
}
if (_showExecutionHistory)
{
foreach (ChatExecutionEvent executionEvent in currentConversation.ExecutionEvents)
{
list.Add((executionEvent.Timestamp, num++, null, executionEvent));
}
}
foreach (var item in from x in list
orderby x.Timestamp, x.Sequence
select x)
{
if (item.Item3 != null)
{
AddMessageBubble(item.Item3.Role, item.Item3.Content, animate: false, item.Item3);
}
else if (item.Item4 != null)
{
AddAgentEventBanner(ToAgentEvent(item.Item4), animate: false);
}
}
((DispatcherObject)this).Dispatcher.InvokeAsync((Action)delegate
{
MessageScroll.ScrollToEnd();
}, (DispatcherPriority)4);
}
private static AgentEvent ToAgentEvent(ChatExecutionEvent stored)
{
Enum.TryParse<AgentEventType>(stored.Type, ignoreCase: true, out var result);
return new AgentEvent
{
Timestamp = stored.Timestamp,
Type = result,
ToolName = stored.ToolName,
Summary = stored.Summary,
FilePath = stored.FilePath,
Success = stored.Success,
StepCurrent = stored.StepCurrent,
StepTotal = stored.StepTotal,
Steps = stored.Steps,
ElapsedMs = stored.ElapsedMs,
InputTokens = stored.InputTokens,
OutputTokens = stored.OutputTokens,
ToolInput = stored.ToolInput,
Iteration = stored.Iteration
};
}
private static ChatExecutionEvent ToStoredAgentEvent(AgentEvent evt)
{
return new ChatExecutionEvent
{
Timestamp = evt.Timestamp,
Type = evt.Type.ToString(),
ToolName = evt.ToolName,
Summary = evt.Summary,
FilePath = evt.FilePath,
Success = evt.Success,
StepCurrent = evt.StepCurrent,
StepTotal = evt.StepTotal,
Steps = evt.Steps?.ToList(),
ElapsedMs = evt.ElapsedMs,
InputTokens = evt.InputTokens,
OutputTokens = evt.OutputTokens,
ToolInput = evt.ToolInput,
Iteration = evt.Iteration
};
}
private bool ShouldPersistAgentEvent(AgentEvent evt)
{
string agentLogLevel = _settings.Settings.Llm.AgentLogLevel;
if (agentLogLevel == "simple" && evt.Type == AgentEventType.ToolCall)
{
return false;
}
AgentEventType type = evt.Type;
if (1 == 0)
{
}
bool result;
switch (type)
{
case AgentEventType.Planning:
{
List<string> steps = evt.Steps;
result = steps != null && steps.Count > 0;
break;
}
case AgentEventType.StepStart:
result = evt.StepTotal > 0;
break;
default:
result = true;
break;
}
if (1 == 0)
{
}
return result;
}
private void PersistAgentEvent(AgentEvent evt)
{
if (!ShouldPersistAgentEvent(evt))
{
return;
}
ChatConversation currentConversation;
lock (_convLock)
{
currentConversation = _currentConversation;
currentConversation?.ExecutionEvents.Add(ToStoredAgentEvent(evt));
}
if (currentConversation != null)
{
try
{
_storage.Save(currentConversation);
}
catch (Exception ex)
{
LogService.Debug("실행 이력 저장 실패: " + ex.Message);
}
UpdateExecutionHistoryUi();
}
}
private int GetExecutionEventCount()
{
lock (_convLock)
{
return _currentConversation?.ExecutionEvents.Count ?? 0;
}
}
private void UpdateExecutionHistoryUi()
{
if (ExecutionLogLabel != null && ExecutionLogIcon != null && BtnToggleExecutionLog != null)
{
int executionEventCount = GetExecutionEventCount();
ExecutionLogLabel.Text = (_showExecutionHistory ? $"실행 로그 {executionEventCount}" : $"실행 로그 숨김 ({executionEventCount})");
ExecutionLogIcon.Text = (_showExecutionHistory ? "\ue7c1" : "\ue8f8");
BtnToggleExecutionLog.ToolTip = (_showExecutionHistory ? "실행 로그를 숨깁니다" : "실행 로그를 다시 표시합니다");
}
}
private void RefreshSubAgentIndicator()
{
if (SubAgentIndicator == null || SubAgentIndicatorLabel == null)
{
return;
}
List<SubAgentTask> list = (from x in SubAgentTool.ActiveTasks.Values
where !x.CompletedAt.HasValue
orderby x.StartedAt
select x).ToList();
if (list.Count == 0)
{
SubAgentIndicator.Visibility = Visibility.Collapsed;
SubAgentIndicator.ToolTip = "실행 중인 서브에이전트가 없습니다";
return;
}
SubAgentIndicator.Visibility = Visibility.Visible;
SubAgentIndicatorLabel.Text = $"서브에이전트 {list.Count}";
SubAgentIndicator.ToolTip = string.Join(Environment.NewLine, list.Select((SubAgentTask x) => x.Id + ": " + TruncateForStatus(x.Task, 48)));
}
private void AddMessageBubble(string role, string content, bool animate = true, ChatMessage? message = null)
{
//IL_05d5: Unknown result type (might be due to invalid IL or missing references)
Border border;
if (role == "user")
{
StackPanel wrapper = new StackPanel
{
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
MaxWidth = 620.0,
Margin = new Thickness(160.0, 10.0, 48.0, 10.0)
};
border = new Border();
border.Background = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue;
border.CornerRadius = new CornerRadius(16.0, 4.0, 16.0, 16.0);
border.Padding = new Thickness(16.0, 10.0, 16.0, 10.0);
border.Child = new TextBlock
{
Text = content,
FontSize = 13.5,
Foreground = System.Windows.Media.Brushes.White,
TextWrapping = TextWrapping.Wrap,
LineHeight = 21.0
};
Border element = border;
wrapper.Children.Add(element);
StackPanel userActionBar = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
Opacity = 0.0,
Margin = new Thickness(0.0, 2.0, 0.0, 0.0)
};
string capturedUserContent = content;
System.Windows.Media.Brush foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
userActionBar.Children.Add(CreateActionButton("\ue8c8", "복사", foreground, delegate
{
try
{
System.Windows.Clipboard.SetText(capturedUserContent);
}
catch
{
}
}));
userActionBar.Children.Add(CreateActionButton("\ue70f", "편집", foreground, delegate
{
EnterEditMode(wrapper, capturedUserContent);
}));
Grid grid = new Grid
{
Margin = new Thickness(0.0, 2.0, 0.0, 0.0)
};
DateTime dateTime = message?.Timestamp ?? DateTime.Now;
grid.Children.Add(new TextBlock
{
Text = dateTime.ToString("HH:mm"),
FontSize = 10.0,
Opacity = 0.5,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 4.0, 0.0)
});
grid.Children.Add(userActionBar);
wrapper.Children.Add(grid);
wrapper.MouseEnter += delegate
{
userActionBar.Opacity = 1.0;
};
wrapper.MouseLeave += delegate
{
userActionBar.Opacity = 0.0;
};
string userContent = content;
wrapper.MouseRightButtonUp += delegate(object _, MouseButtonEventArgs re)
{
re.Handled = true;
ShowMessageContextMenu(userContent, "user");
};
if (animate)
{
ApplyMessageEntryAnimation(wrapper);
}
MessagePanel.Children.Add(wrapper);
return;
}
StackPanel stackPanel = new StackPanel
{
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
MaxWidth = GetMessageMaxWidth(),
Margin = new Thickness(48.0, 10.0, 120.0, 10.0)
};
if (animate)
{
ApplyMessageEntryAnimation(stackPanel);
}
border = new Border();
border.Background = (TryFindResource("ItemBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
border.BorderBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, 17, 24, 39));
border.BorderThickness = new Thickness(1.0);
border.CornerRadius = new CornerRadius(18.0);
border.Padding = new Thickness(16.0, 14.0, 16.0, 12.0);
border.Effect = new DropShadowEffect
{
BlurRadius = 16.0,
ShadowDepth = 0.0,
Opacity = 0.06
};
Border border2 = border;
StackPanel stackPanel2 = new StackPanel();
(string Name, string Symbol, string Color) agentIdentity = GetAgentIdentity();
string item = agentIdentity.Name;
string item2 = agentIdentity.Symbol;
string item3 = agentIdentity.Color;
SolidColorBrush foreground2 = BrushFromHex(item3);
StackPanel stackPanel3 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
Margin = new Thickness(0.0, 0.0, 0.0, 8.0)
};
TextBlock textBlock = new TextBlock
{
Text = "◆",
FontSize = 13.0,
Foreground = foreground2,
VerticalAlignment = VerticalAlignment.Center,
RenderTransformOrigin = new Point(0.5, 0.5),
RenderTransform = new RotateTransform(0.0)
};
if (animate)
{
DoubleAnimation animation = new DoubleAnimation
{
From = 0.0,
To = 360.0,
Duration = TimeSpan.FromSeconds(1.2),
EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
}
};
((RotateTransform)textBlock.RenderTransform).BeginAnimation(RotateTransform.AngleProperty, animation);
}
stackPanel3.Children.Add(textBlock);
stackPanel3.Children.Add(new TextBlock
{
Text = item,
FontSize = 11.0,
FontWeight = FontWeights.SemiBold,
Foreground = foreground2,
Margin = new Thickness(6.0, 0.0, 0.0, 0.0),
VerticalAlignment = VerticalAlignment.Center
});
stackPanel2.Children.Add(stackPanel3);
MarkdownRenderer.EnableFilePathHighlight = ((!(System.Windows.Application.Current is App app)) ? ((bool?)null) : app.SettingsService?.Settings.Llm.EnableFilePathHighlight) ?? true;
System.Windows.Media.Brush textColor = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush secondaryColor = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush accentColor = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue;
System.Windows.Media.Brush codeBg = (TryFindResource("HintBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.DarkGray;
StackPanel stackPanel4 = MarkdownRenderer.Render(content, textColor, secondaryColor, accentColor, codeBg);
stackPanel4.Margin = new Thickness(0.0, 0.0, 0.0, 6.0);
stackPanel2.Children.Add(stackPanel4);
StackPanel stackPanel5 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
Margin = new Thickness(0.0, 6.0, 0.0, 0.0)
};
System.Windows.Media.Brush brush = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
SolidColorBrush solidColorBrush = new SolidColorBrush(System.Windows.Media.Color.FromRgb(139, 144, 176));
string capturedContent = content;
stackPanel5.Children.Add(CreateActionButton("\ue8c8", "복사", brush, delegate
{
try
{
System.Windows.Clipboard.SetText(capturedContent);
}
catch
{
}
}));
stackPanel5.Children.Add(CreateActionButton("\ue72c", "다시 생성", brush, delegate
{
RegenerateLastAsync();
}));
stackPanel5.Children.Add(CreateActionButton("\ue70f", "수정 후 재시도", brush, delegate
{
ShowRetryWithFeedbackInput();
}));
AddLinkedFeedbackButtons(stackPanel5, brush, message);
DateTime dateTime2 = message?.Timestamp ?? DateTime.Now;
stackPanel5.Children.Add(new TextBlock
{
Text = dateTime2.ToString("HH:mm"),
FontSize = 10.0,
Opacity = 0.5,
Foreground = brush,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(8.0, 0.0, 0.0, 0.0)
});
stackPanel2.Children.Add(stackPanel5);
border2.Child = stackPanel2;
stackPanel.Children.Add(border2);
string aiContent = content;
border2.MouseRightButtonUp += delegate(object _, MouseButtonEventArgs re)
{
re.Handled = true;
ShowMessageContextMenu(aiContent, "assistant");
};
MessagePanel.Children.Add(stackPanel);
}
private string GetCheckStyle()
{
string text = (_settings.Settings.Launcher.Theme ?? "system").ToLowerInvariant();
if (1 == 0)
{
}
string result;
switch (text)
{
case "dark":
case "system":
result = "circle";
break;
case "oled":
result = "glow";
break;
case "light":
result = "roundrect";
break;
case "nord":
result = "diamond";
break;
case "catppuccin":
result = "pill";
break;
case "monokai":
result = "square";
break;
case "sepia":
result = "stamp";
break;
case "alfred":
result = "minimal";
break;
case "alfredlight":
result = "minimal";
break;
default:
result = "circle";
break;
}
if (1 == 0)
{
}
return result;
}
private FrameworkElement CreateCheckIcon(bool isChecked, System.Windows.Media.Brush? accentBrush = null)
{
System.Windows.Media.Brush color = accentBrush ?? (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
if (isChecked)
{
return CreateSimpleCheck(color);
}
return new System.Windows.Shapes.Rectangle
{
Width = 14.0,
Height = 14.0,
Fill = System.Windows.Media.Brushes.Transparent,
Margin = new Thickness(0.0, 0.0, 10.0, 0.0)
};
}
private static void AnimateScale(FrameworkElement el, double from, double to, int ms, IEasingFunction ease)
{
if (el.RenderTransform is TransformGroup transformGroup)
{
ScaleTransform scaleTransform = transformGroup.Children.OfType<ScaleTransform>().FirstOrDefault();
if (scaleTransform != null)
{
DoubleAnimation animation = new DoubleAnimation(from, to, TimeSpan.FromMilliseconds(ms))
{
EasingFunction = ease
};
scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, animation);
scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, animation);
return;
}
}
if (el.RenderTransform is ScaleTransform scaleTransform2)
{
DoubleAnimation animation2 = new DoubleAnimation(from, to, TimeSpan.FromMilliseconds(ms))
{
EasingFunction = ease
};
scaleTransform2.BeginAnimation(ScaleTransform.ScaleXProperty, animation2);
scaleTransform2.BeginAnimation(ScaleTransform.ScaleYProperty, animation2);
}
}
private static void ApplyHoverScaleAnimation(FrameworkElement element, double hoverScale = 1.08)
{
element.Loaded += delegate
{
EnsureTransform();
};
element.MouseEnter += delegate
{
EnsureTransform();
ScaleTransform scaleTransform = (ScaleTransform)element.RenderTransform;
DoubleAnimation animation = new DoubleAnimation(hoverScale, TimeSpan.FromMilliseconds(150.0))
{
EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
}
};
scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, animation);
scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, animation);
};
element.MouseLeave += delegate
{
EnsureTransform();
ScaleTransform scaleTransform = (ScaleTransform)element.RenderTransform;
DoubleAnimation animation = new DoubleAnimation(1.0, TimeSpan.FromMilliseconds(200.0))
{
EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
}
};
scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, animation);
scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, animation);
};
void EnsureTransform()
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
element.RenderTransformOrigin = new Point(0.5, 0.5);
if (!(element.RenderTransform is ScaleTransform) || ((Freezable)element.RenderTransform).IsFrozen)
{
element.RenderTransform = new ScaleTransform(1.0, 1.0);
}
}
}
private static void ApplyHoverBounceAnimation(FrameworkElement element, double bounceY = -2.5)
{
element.Loaded += delegate
{
EnsureTransform();
};
element.MouseEnter += delegate
{
EnsureTransform();
TranslateTransform translateTransform = (TranslateTransform)element.RenderTransform;
translateTransform.BeginAnimation(TranslateTransform.YProperty, new DoubleAnimation(bounceY, TimeSpan.FromMilliseconds(200.0))
{
EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
}
});
};
element.MouseLeave += delegate
{
EnsureTransform();
TranslateTransform translateTransform = (TranslateTransform)element.RenderTransform;
translateTransform.BeginAnimation(TranslateTransform.YProperty, new DoubleAnimation(0.0, TimeSpan.FromMilliseconds(250.0))
{
EasingFunction = new ElasticEase
{
EasingMode = EasingMode.EaseOut,
Oscillations = 1,
Springiness = 10.0
}
});
};
void EnsureTransform()
{
if (!(element.RenderTransform is TranslateTransform) || ((Freezable)element.RenderTransform).IsFrozen)
{
element.RenderTransform = new TranslateTransform(0.0, 0.0);
}
}
}
private static FrameworkElement CreateSimpleCheck(System.Windows.Media.Brush color, double size = 14.0)
{
return new System.Windows.Shapes.Path
{
Data = Geometry.Parse($"M {size * 0.15} {size * 0.5} L {size * 0.4} {size * 0.75} L {size * 0.85} {size * 0.28}"),
Stroke = color,
StrokeThickness = 2.0,
StrokeStartLineCap = PenLineCap.Round,
StrokeEndLineCap = PenLineCap.Round,
StrokeLineJoin = PenLineJoin.Round,
Width = size,
Height = size,
Margin = new Thickness(0.0, 0.0, 10.0, 0.0),
VerticalAlignment = VerticalAlignment.Center
};
}
private static void ApplyMenuItemHover(Border item)
{
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
System.Windows.Media.Brush originalBg = item.Background?.Clone() ?? System.Windows.Media.Brushes.Transparent;
if (((Freezable)originalBg).CanFreeze)
{
((Freezable)originalBg).Freeze();
}
item.RenderTransformOrigin = new Point(0.5, 0.5);
item.RenderTransform = new ScaleTransform(1.0, 1.0);
item.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border)
{
if (originalBg is SolidColorBrush { Color: { A: >32 } })
{
border.Opacity = 0.85;
}
else
{
border.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue));
}
}
ScaleTransform scaleTransform = item.RenderTransform as ScaleTransform;
scaleTransform?.BeginAnimation(ScaleTransform.ScaleXProperty, new DoubleAnimation(1.02, TimeSpan.FromMilliseconds(120.0)));
scaleTransform?.BeginAnimation(ScaleTransform.ScaleYProperty, new DoubleAnimation(1.02, TimeSpan.FromMilliseconds(120.0)));
};
item.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border)
{
border.Opacity = 1.0;
border.Background = originalBg;
}
ScaleTransform scaleTransform = item.RenderTransform as ScaleTransform;
scaleTransform?.BeginAnimation(ScaleTransform.ScaleXProperty, new DoubleAnimation(1.0, TimeSpan.FromMilliseconds(150.0)));
scaleTransform?.BeginAnimation(ScaleTransform.ScaleYProperty, new DoubleAnimation(1.0, TimeSpan.FromMilliseconds(150.0)));
};
}
private System.Windows.Controls.Button CreateActionButton(string symbol, string tooltip, System.Windows.Media.Brush foreground, Action onClick)
{
System.Windows.Media.Brush hoverBrush = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
TextBlock icon = new TextBlock
{
Text = symbol,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 12.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center
};
System.Windows.Controls.Button button = new System.Windows.Controls.Button
{
Content = icon,
Background = System.Windows.Media.Brushes.Transparent,
BorderThickness = new Thickness(0.0),
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(6.0, 4.0, 6.0, 4.0),
Margin = new Thickness(0.0, 0.0, 4.0, 0.0),
ToolTip = tooltip
};
button.MouseEnter += delegate
{
icon.Foreground = hoverBrush;
};
button.MouseLeave += delegate
{
icon.Foreground = foreground;
};
button.Click += delegate
{
onClick();
};
ApplyHoverScaleAnimation(button, 1.15);
return button;
}
private System.Windows.Controls.Button CreateFeedbackButton(string outline, string filled, string tooltip, System.Windows.Media.Brush normalColor, System.Windows.Media.Brush activeColor, ChatMessage? message = null, string feedbackType = "", Action? resetSibling = null, Action<Action>? registerReset = null)
{
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
System.Windows.Media.Brush hoverBrush = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
bool isActive = message?.Feedback == feedbackType;
TextBlock icon = new TextBlock
{
Text = (isActive ? filled : outline),
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 12.0,
Foreground = (isActive ? activeColor : normalColor),
VerticalAlignment = VerticalAlignment.Center,
RenderTransformOrigin = new Point(0.5, 0.5),
RenderTransform = new ScaleTransform(1.0, 1.0)
};
System.Windows.Controls.Button button = new System.Windows.Controls.Button
{
Content = icon,
Background = System.Windows.Media.Brushes.Transparent,
BorderThickness = new Thickness(0.0),
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(6.0, 4.0, 6.0, 4.0),
Margin = new Thickness(0.0, 0.0, 4.0, 0.0),
ToolTip = tooltip
};
registerReset?.Invoke(delegate
{
isActive = false;
icon.Text = outline;
icon.Foreground = normalColor;
});
button.MouseEnter += delegate
{
if (!isActive)
{
icon.Foreground = hoverBrush;
}
};
button.MouseLeave += delegate
{
if (!isActive)
{
icon.Foreground = normalColor;
}
};
button.Click += delegate
{
isActive = !isActive;
icon.Text = (isActive ? filled : outline);
icon.Foreground = (isActive ? activeColor : normalColor);
if (isActive)
{
resetSibling?.Invoke();
}
if (message != null)
{
message.Feedback = (isActive ? feedbackType : null);
try
{
ChatConversation currentConversation;
lock (_convLock)
{
currentConversation = _currentConversation;
}
if (currentConversation != null)
{
_storage.Save(currentConversation);
}
}
catch
{
}
}
ScaleTransform scaleTransform = (ScaleTransform)icon.RenderTransform;
DoubleAnimation animation = new DoubleAnimation(1.3, 1.0, TimeSpan.FromMilliseconds(250.0))
{
EasingFunction = new ElasticEase
{
EasingMode = EasingMode.EaseOut,
Oscillations = 1,
Springiness = 5.0
}
};
scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, animation);
scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, animation);
};
return button;
}
private void AddLinkedFeedbackButtons(StackPanel actionBar, System.Windows.Media.Brush btnColor, ChatMessage? message)
{
Action resetLikeAction = null;
Action resetDislikeAction = null;
System.Windows.Controls.Button element = CreateFeedbackButton("\ue8e1", "\ueb51", "좋아요", btnColor, new SolidColorBrush(System.Windows.Media.Color.FromRgb(56, 161, 105)), message, "like", delegate
{
resetDislikeAction?.Invoke();
}, delegate(Action reset)
{
resetLikeAction = reset;
});
System.Windows.Controls.Button element2 = CreateFeedbackButton("\ue8e0", "\ueb50", "싫어요", btnColor, new SolidColorBrush(System.Windows.Media.Color.FromRgb(229, 62, 62)), message, "dislike", delegate
{
resetLikeAction?.Invoke();
}, delegate(Action reset)
{
resetDislikeAction = reset;
});
actionBar.Children.Add(element);
actionBar.Children.Add(element2);
}
private static void ApplyMessageEntryAnimation(FrameworkElement element)
{
element.Opacity = 0.0;
element.RenderTransform = new TranslateTransform(0.0, 16.0);
element.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0.0, 1.0, TimeSpan.FromMilliseconds(350.0))
{
EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
}
});
((TranslateTransform)element.RenderTransform).BeginAnimation(TranslateTransform.YProperty, new DoubleAnimation(16.0, 0.0, TimeSpan.FromMilliseconds(400.0))
{
EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
}
});
}
private void EnterEditMode(StackPanel wrapper, string originalText)
{
if (_isStreaming || _isEditing)
{
return;
}
_isEditing = true;
int idx = MessagePanel.Children.IndexOf(wrapper);
if (idx < 0)
{
_isEditing = false;
return;
}
StackPanel stackPanel = new StackPanel
{
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
MaxWidth = 540.0,
Margin = wrapper.Margin
};
System.Windows.Controls.TextBox editBox = new System.Windows.Controls.TextBox
{
Text = originalText,
FontSize = 13.5,
Foreground = ((TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White),
Background = ((TryFindResource("ItemBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.DarkGray),
CaretBrush = ((TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue),
BorderBrush = ((TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue),
BorderThickness = new Thickness(1.5),
Padding = new Thickness(14.0, 10.0, 14.0, 10.0),
TextWrapping = TextWrapping.Wrap,
AcceptsReturn = false,
MaxHeight = 200.0,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto
};
Border element = new Border
{
CornerRadius = new CornerRadius(14.0),
Child = editBox,
ClipToBounds = true
};
stackPanel.Children.Add(element);
StackPanel stackPanel2 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
Margin = new Thickness(0.0, 6.0, 0.0, 0.0)
};
System.Windows.Media.Brush brush = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue;
System.Windows.Media.Brush foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Controls.Button button = new System.Windows.Controls.Button
{
Content = new TextBlock
{
Text = "취소",
FontSize = 12.0,
Foreground = foreground
},
Background = System.Windows.Media.Brushes.Transparent,
BorderThickness = new Thickness(0.0),
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(12.0, 5.0, 12.0, 5.0),
Margin = new Thickness(0.0, 0.0, 6.0, 0.0)
};
button.Click += delegate
{
_isEditing = false;
if (idx >= 0 && idx < MessagePanel.Children.Count)
{
MessagePanel.Children[idx] = wrapper;
}
};
stackPanel2.Children.Add(button);
System.Windows.Controls.Button button2 = new System.Windows.Controls.Button
{
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(0.0)
};
button2.Template = (ControlTemplate)XamlReader.Parse("<ControlTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' TargetType='Button'><Border Background='" + ((brush is SolidColorBrush { Color: var color }) ? color.ToString() : "#4B5EFC") + "' CornerRadius='8' Padding='12,5,12,5'><TextBlock Text='전송' FontSize='12' Foreground='White' HorizontalAlignment='Center'/></Border></ControlTemplate>");
button2.Click += delegate
{
string text = editBox.Text.Trim();
if (!string.IsNullOrEmpty(text))
{
SubmitEditAsync(idx, text);
}
};
stackPanel2.Children.Add(button2);
stackPanel.Children.Add(stackPanel2);
MessagePanel.Children[idx] = stackPanel;
editBox.KeyDown += delegate(object _, System.Windows.Input.KeyEventArgs ke)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Invalid comparison between Unknown and I4
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Invalid comparison between Unknown and I4
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Invalid comparison between Unknown and I4
if ((int)ke.Key == 6 && (int)Keyboard.Modifiers == 0)
{
ke.Handled = true;
string text = editBox.Text.Trim();
if (!string.IsNullOrEmpty(text))
{
_ = SubmitEditAsync(idx, text);
}
}
if ((int)ke.Key == 13)
{
ke.Handled = true;
_isEditing = false;
if (idx >= 0 && idx < MessagePanel.Children.Count)
{
MessagePanel.Children[idx] = wrapper;
}
}
};
editBox.Focus();
editBox.SelectAll();
}
private async Task SubmitEditAsync(int bubbleIndex, string newText)
{
_isEditing = false;
if (_isStreaming)
{
return;
}
ChatConversation conv;
lock (_convLock)
{
if (_currentConversation == null)
{
return;
}
conv = _currentConversation;
}
int userMsgIdx = -1;
int uiIdx = 0;
lock (_convLock)
{
for (int i = 0; i < conv.Messages.Count; i++)
{
if (!(conv.Messages[i].Role == "system"))
{
if (uiIdx == bubbleIndex)
{
userMsgIdx = i;
break;
}
uiIdx++;
}
}
}
if (userMsgIdx < 0)
{
return;
}
lock (_convLock)
{
conv.Messages[userMsgIdx].Content = newText;
while (conv.Messages.Count > userMsgIdx + 1)
{
conv.Messages.RemoveAt(conv.Messages.Count - 1);
}
}
while (MessagePanel.Children.Count > bubbleIndex + 1)
{
MessagePanel.Children.RemoveAt(MessagePanel.Children.Count - 1);
}
MessagePanel.Children.RemoveAt(bubbleIndex);
AddMessageBubble("user", newText, animate: false);
await SendRegenerateAsync(conv);
try
{
_storage.Save(conv);
}
catch (Exception ex)
{
LogService.Debug("대화 저장 실패: " + ex.Message);
}
RefreshConversationList();
}
private void StopAiIconPulse()
{
if (!_aiIconPulseStopped && _activeAiIcon != null)
{
_activeAiIcon.BeginAnimation(UIElement.OpacityProperty, null);
_activeAiIcon.Opacity = 1.0;
_activeAiIcon = null;
_aiIconPulseStopped = true;
}
}
private void CursorTimer_Tick(object? sender, EventArgs e)
{
_cursorVisible = !_cursorVisible;
if (_activeStreamText != null && _displayedLength > 0)
{
string text = ((_cachedStreamContent.Length > 0) ? _cachedStreamContent.Substring(0, Math.Min(_displayedLength, _cachedStreamContent.Length)) : "");
_activeStreamText.Text = text + (_cursorVisible ? "▌" : " ");
}
}
private void ElapsedTimer_Tick(object? sender, EventArgs e)
{
int value = (int)(DateTime.UtcNow - _streamStartTime).TotalSeconds;
if (_elapsedLabel != null)
{
_elapsedLabel.Text = $"{value}s";
}
if (StatusElapsed != null)
{
StatusElapsed.Text = $"{value}초";
}
}
private void TypingTimer_Tick(object? sender, EventArgs e)
{
if (_activeStreamText == null || string.IsNullOrEmpty(_cachedStreamContent))
{
return;
}
int length = _cachedStreamContent.Length;
if (_displayedLength < length)
{
int num = length - _displayedLength;
int num2 = ((num > 200) ? Math.Min(num / 5, 40) : ((num <= 50) ? Math.Min(3, num) : Math.Min(num / 4, 15)));
_displayedLength += num2;
string text = _cachedStreamContent.Substring(0, _displayedLength);
_activeStreamText.Text = text + (_cursorVisible ? "▌" : " ");
if (!_userScrolled)
{
MessageScroll.ScrollToVerticalOffset(MessageScroll.ScrollableHeight);
}
}
}
public void SendInitialMessage(string message)
{
StartNewConversation();
InputBox.Text = message;
SendMessageAsync();
}
private void StartNewConversation()
{
lock (_convLock)
{
if (_currentConversation != null && _currentConversation.Messages.Count > 0)
{
try
{
_storage.Save(_currentConversation);
}
catch
{
}
}
_currentConversation = new ChatConversation
{
Tab = _activeTab
};
string workFolder = _settings.Settings.Llm.WorkFolder;
if (!string.IsNullOrEmpty(workFolder) && _activeTab != "Chat")
{
_currentConversation.WorkFolder = workFolder;
}
}
_tabConversationId[_activeTab] = null;
MessagePanel.Children.Clear();
EmptyState.Visibility = Visibility.Visible;
_attachedFiles.Clear();
RefreshAttachedFilesUI();
UpdateChatTitle();
RefreshConversationList();
UpdateFolderBar();
if (_activeTab == "Cowork")
{
BuildBottomBar();
}
}
private void RestoreLastConversations()
{
Dictionary<string, string> lastConversationIds = _settings.Settings.Llm.LastConversationIds;
if (lastConversationIds == null || lastConversationIds.Count == 0)
{
return;
}
foreach (KeyValuePair<string, string> item in lastConversationIds)
{
if (_tabConversationId.ContainsKey(item.Key) && !string.IsNullOrEmpty(item.Value))
{
_tabConversationId[item.Key] = item.Value;
}
}
string valueOrDefault = _tabConversationId.GetValueOrDefault(_activeTab);
if (string.IsNullOrEmpty(valueOrDefault))
{
return;
}
ChatConversation chatConversation = _storage.Load(valueOrDefault);
if (chatConversation != null)
{
if (string.IsNullOrEmpty(chatConversation.Tab))
{
chatConversation.Tab = _activeTab;
}
lock (_convLock)
{
_currentConversation = chatConversation;
}
RenderMessages();
UpdateChatTitle();
UpdateFolderBar();
LoadConversationSettings();
}
}
private void SaveLastConversations()
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> item in _tabConversationId)
{
if (!string.IsNullOrEmpty(item.Value))
{
dictionary[item.Key] = item.Value;
}
}
_settings.Settings.Llm.LastConversationIds = dictionary;
try
{
_settings.Save();
}
catch
{
}
}
private async void BtnSend_Click(object sender, RoutedEventArgs e)
{
await SendMessageAsync();
}
private async void InputBox_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if ((int)e.Key == 65 && ((Enum)Keyboard.Modifiers).HasFlag((Enum)(object)(ModifierKeys)2) && TryPasteClipboardImage())
{
e.Handled = true;
}
else if ((int)e.Key == 6 && !((Enum)Keyboard.Modifiers).HasFlag((Enum)(object)(ModifierKeys)4))
{
if (SlashPopup.IsOpen && _slashSelectedIndex >= 0)
{
e.Handled = true;
ExecuteSlashSelectedItem();
}
else if (InputBox.Text.Trim().Equals("/help", StringComparison.OrdinalIgnoreCase))
{
e.Handled = true;
InputBox.Text = "";
SlashPopup.IsOpen = false;
ShowSlashHelpWindow();
}
else
{
e.Handled = true;
await SendMessageAsync();
}
}
}
private bool TryPasteClipboardImage()
{
if (!_settings.Settings.Llm.EnableImageInput)
{
return false;
}
if (!System.Windows.Clipboard.ContainsImage())
{
return false;
}
try
{
BitmapSource image = System.Windows.Clipboard.GetImage();
if (image == null)
{
return false;
}
PngBitmapEncoder pngBitmapEncoder = new PngBitmapEncoder();
pngBitmapEncoder.Frames.Add(BitmapFrame.Create(image));
using MemoryStream memoryStream = new MemoryStream();
pngBitmapEncoder.Save(memoryStream);
byte[] array = memoryStream.ToArray();
int num = _settings.Settings.Llm.MaxImageSizeKb;
if (num <= 0)
{
num = 5120;
}
if (array.Length > num * 1024)
{
CustomMessageBox.Show($"이미지가 너무 큽니다 ({array.Length / 1024}KB, 최대 {num}KB).", "이미지 크기 초과", MessageBoxButton.OK, MessageBoxImage.Exclamation);
return true;
}
string @base = Convert.ToBase64String(array);
ImageAttachment imageAttachment = new ImageAttachment
{
Base64 = @base,
MimeType = "image/png",
FileName = $"clipboard_{DateTime.Now:HHmmss}.png"
};
_pendingImages.Add(imageAttachment);
AddImagePreview(imageAttachment, image);
return true;
}
catch (Exception ex)
{
LogService.Debug("클립보드 이미지 붙여넣기 실패: " + ex.Message);
return false;
}
}
private void AddImagePreview(ImageAttachment attachment, BitmapSource? thumbnail = null)
{
System.Windows.Media.Brush foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush background = (TryFindResource("HintBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.LightGray;
Border border = new Border
{
Background = background,
CornerRadius = new CornerRadius(6.0),
Padding = new Thickness(4.0),
Margin = new Thickness(0.0, 0.0, 4.0, 4.0)
};
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
if (thumbnail != null)
{
stackPanel.Children.Add(new System.Windows.Controls.Image
{
Source = thumbnail,
MaxHeight = 48.0,
MaxWidth = 64.0,
Stretch = Stretch.Uniform,
Margin = new Thickness(0.0, 0.0, 4.0, 0.0)
});
}
else
{
stackPanel.Children.Add(new TextBlock
{
Text = "\ue8b9",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 16.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(2.0, 0.0, 4.0, 0.0)
});
}
stackPanel.Children.Add(new TextBlock
{
Text = attachment.FileName,
FontSize = 10.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center,
MaxWidth = 100.0,
TextTrimming = TextTrimming.CharacterEllipsis
});
ImageAttachment capturedAttachment = attachment;
Border capturedChip = border;
System.Windows.Controls.Button button = new System.Windows.Controls.Button
{
Content = new TextBlock
{
Text = "\ue711",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 8.0,
Foreground = foreground
},
Background = System.Windows.Media.Brushes.Transparent,
BorderThickness = new Thickness(0.0),
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(4.0, 2.0, 4.0, 2.0),
Margin = new Thickness(2.0, 0.0, 0.0, 0.0)
};
button.Click += delegate
{
_pendingImages.Remove(capturedAttachment);
AttachedFilesPanel.Items.Remove(capturedChip);
if (_pendingImages.Count == 0 && _attachedFiles.Count == 0)
{
AttachedFilesPanel.Visibility = Visibility.Collapsed;
}
};
stackPanel.Children.Add(button);
border.Child = stackPanel;
AttachedFilesPanel.Items.Add(border);
AttachedFilesPanel.Visibility = Visibility.Visible;
}
private void InputBox_TextChanged(object sender, TextChangedEventArgs e)
{
UpdateWatermarkVisibility();
UpdateDraftPreviewCard();
string text = InputBox.Text;
if (_activeSlashCmd != null && text.StartsWith("/"))
{
HideSlashChip();
}
if (text.StartsWith("/") && !text.Contains(' '))
{
string activeTab = _activeTab;
bool flag = ((activeTab == "Cowork" || activeTab == "Code") ? true : false);
bool isDev = flag;
List<(string, string, bool)> list = (from kv in SlashCommands
where kv.Key.StartsWith(text, StringComparison.OrdinalIgnoreCase)
where kv.Value.Tab == "all" || (isDev && kv.Value.Tab == "dev")
select (Cmd: kv.Key, Label: kv.Value.Label, IsSkill: false)).ToList();
if (_settings.Settings.Llm.EnableSkillSystem)
{
IEnumerable<(string, string, bool, bool)> enumerable = from s in SkillService.MatchSlashCommand(text)
where s.IsVisibleInTab(_activeTab)
select (Cmd: "/" + s.Name, Label: s.IsAvailable ? s.Label : (s.Label + " " + s.UnavailableHint), IsSkill: true, Available: s.IsAvailable);
foreach (var item in enumerable)
{
list.Add((item.Item1, item.Item2, item.Item3));
}
}
if (list.Count > 0)
{
List<string> favorites = _settings.Settings.Llm.FavoriteSlashCommands;
if (favorites.Count > 0)
{
list = list.OrderByDescending<(string, string, bool), bool>(((string Cmd, string Label, bool IsSkill) m) => favorites.Contains<string>(m.Cmd, StringComparer.OrdinalIgnoreCase)).ToList();
}
_slashAllMatches = list;
_slashPageOffset = 0;
_slashSelectedIndex = -1;
RenderSlashPage();
SlashPopup.IsOpen = true;
return;
}
}
SlashPopup.IsOpen = false;
}
private void UpdateDraftPreviewCard()
{
if (DraftPreviewCard != null && DraftPreviewText != null && InputBox != null)
{
string text = InputBox.Text?.Trim() ?? "";
string activeSlashCmd = _activeSlashCmd;
string text2 = ((activeSlashCmd != null && activeSlashCmd.Length > 0) ? (_activeSlashCmd + " ") : "");
string text3 = (text2 + text).Trim();
if (string.IsNullOrWhiteSpace(text3))
{
DraftPreviewCard.Visibility = Visibility.Collapsed;
DraftPreviewText.Text = "";
RebuildDraftQueuePanel();
}
else
{
DraftPreviewCard.Visibility = Visibility.Visible;
DraftPreviewText.Text = ((text3.Length > 140) ? (text3.Substring(0, 140) + "...") : text3);
RebuildDraftQueuePanel();
}
}
}
private string GetPreparedDraftText()
{
string text = InputBox?.Text?.Trim() ?? "";
return (_activeSlashCmd != null) ? (_activeSlashCmd + " " + text).Trim() : text;
}
private void RebuildDraftQueuePanel()
{
if (DraftQueuePanel == null)
{
return;
}
DraftQueuePanel.Children.Clear();
DraftQueuePanel.Visibility = ((_draftQueue.Count <= 0) ? Visibility.Collapsed : Visibility.Visible);
if (_draftQueue.Count == 0)
{
return;
}
DraftQueuePanel.Children.Add(new TextBlock
{
Text = $"다음 작업 대기열 {_draftQueue.Count}",
FontSize = 10.5,
FontWeight = FontWeights.SemiBold,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
Margin = new Thickness(4.0, 0.0, 0.0, 6.0)
});
for (int i = 0; i < _draftQueue.Count; i++)
{
int index = i;
string text = _draftQueue[i];
Border border = new Border();
border.Background = (TryFindResource("ItemBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
border.BorderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.LightGray;
border.BorderThickness = new Thickness(1.0);
border.CornerRadius = new CornerRadius(16.0);
border.Padding = new Thickness(12.0, 9.0, 12.0, 9.0);
border.Margin = new Thickness(0.0, 0.0, 0.0, 6.0);
Border border2 = border;
Grid grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(1.0, GridUnitType.Star)
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = GridLength.Auto
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = GridLength.Auto
});
StackPanel stackPanel = new StackPanel();
stackPanel.Children.Add(new TextBlock
{
Text = ((index == 0) ? "다음 순서" : $"대기 {index + 1}"),
FontSize = 10.0,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray)
});
stackPanel.Children.Add(new TextBlock
{
Text = ((text.Length > 160) ? (text.Substring(0, 160) + "...") : text),
FontSize = 12.5,
Foreground = ((TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Black),
Margin = new Thickness(0.0, 4.0, 0.0, 0.0),
TextTrimming = TextTrimming.CharacterEllipsis
});
Grid.SetColumn(stackPanel, 0);
grid.Children.Add(stackPanel);
System.Windows.Controls.Button button = new System.Windows.Controls.Button();
button.Style = TryFindResource("GhostBtn") as Style;
button.Padding = new Thickness(8.0, 4.0, 8.0, 4.0);
button.Margin = new Thickness(8.0, 0.0, 0.0, 0.0);
button.ToolTip = "지금 바로 실행";
button.Content = new TextBlock
{
Text = "\ue768",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 10.0,
Foreground = ((TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue)
};
System.Windows.Controls.Button button2 = button;
button2.Click += async delegate
{
if (index >= 0 && index < _draftQueue.Count && !_isStreaming)
{
string next = _draftQueue[index];
_draftQueue.RemoveAt(index);
RebuildDraftQueuePanel();
InputBox.Text = next;
InputBox.CaretIndex = InputBox.Text.Length;
UpdateDraftPreviewCard();
await SendMessageAsync();
}
};
Grid.SetColumn(button2, 1);
grid.Children.Add(button2);
button = new System.Windows.Controls.Button();
button.Style = TryFindResource("GhostBtn") as Style;
button.Padding = new Thickness(6.0, 4.0, 6.0, 4.0);
button.Margin = new Thickness(8.0, 0.0, 0.0, 0.0);
button.ToolTip = "대기열에서 제거";
button.Content = new TextBlock
{
Text = "\ue711",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 10.0,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray)
};
System.Windows.Controls.Button button3 = button;
button3.Click += delegate
{
if (index >= 0 && index < _draftQueue.Count)
{
_draftQueue.RemoveAt(index);
RebuildDraftQueuePanel();
}
};
Grid.SetColumn(button3, 2);
grid.Children.Add(button3);
border2.Child = grid;
DraftQueuePanel.Children.Add(border2);
}
}
private void BtnDraftEnqueue_Click(object sender, RoutedEventArgs e)
{
string preparedDraftText = GetPreparedDraftText();
if (!string.IsNullOrWhiteSpace(preparedDraftText))
{
_draftQueue.Add(preparedDraftText);
if (_activeSlashCmd != null)
{
HideSlashChip();
}
InputBox.Clear();
RebuildDraftQueuePanel();
UpdateDraftPreviewCard();
InputBox.Focus();
}
}
private void StartNextQueuedDraftIfAny()
{
if (_autoContinuingQueuedDraft || _isStreaming || _draftQueue.Count == 0)
{
return;
}
string next = _draftQueue[0];
_draftQueue.RemoveAt(0);
_autoContinuingQueuedDraft = true;
RebuildDraftQueuePanel();
((DispatcherObject)this).Dispatcher.BeginInvoke((Delegate)(Func<Task>)async delegate
{
try
{
InputBox.Text = next;
InputBox.CaretIndex = InputBox.Text.Length;
UpdateDraftPreviewCard();
await SendMessageAsync();
}
finally
{
_autoContinuingQueuedDraft = false;
}
}, (DispatcherPriority)4, Array.Empty<object>());
}
private void BtnDraftEdit_Click(object sender, RoutedEventArgs e)
{
InputBox.Focus();
InputBox.CaretIndex = InputBox.Text?.Length ?? 0;
}
private void BtnDraftClear_Click(object sender, RoutedEventArgs e)
{
if (_activeSlashCmd != null)
{
HideSlashChip();
}
InputBox.Clear();
UpdateDraftPreviewCard();
InputBox.Focus();
}
private void RenderSlashPage()
{
SlashItems.Items.Clear();
int count = _slashAllMatches.Count;
int slashPageOffset = _slashPageOffset;
int num = Math.Min(slashPageOffset + SlashPageSize, count);
int value = _slashAllMatches.Count<(string, string, bool)>(((string Cmd, string Label, bool IsSkill) x) => !x.IsSkill);
int num2 = _slashAllMatches.Count<(string, string, bool)>(((string Cmd, string Label, bool IsSkill) x) => x.IsSkill);
if (SlashPopupTitle != null)
{
SlashPopupTitle.Text = ((num2 > 0) ? "명령어와 스킬" : "빠른 명령어");
}
if (SlashPopupHint != null)
{
SlashPopupHint.Text = ((num2 > 0) ? $"명령 {value}개 · 스킬 {num2}개" : $"명령 {value}개");
}
if (slashPageOffset > 0)
{
SlashNavUp.Visibility = Visibility.Visible;
SlashNavUpText.Text = $"▲ 위로 {slashPageOffset}개";
}
else
{
SlashNavUp.Visibility = Visibility.Collapsed;
}
bool? flag = null;
for (int num3 = slashPageOffset; num3 < num; num3++)
{
(string Cmd, string Label, bool IsSkill) tuple = _slashAllMatches[num3];
string item = tuple.Cmd;
string item2 = tuple.Label;
bool item3 = tuple.IsSkill;
string capturedCmd = item;
SkillDefinition skillDefinition = (item3 ? SkillService.Find(item.TrimStart('/')) : null);
bool flag2 = skillDefinition?.IsAvailable ?? true;
bool flag3 = _settings.Settings.Llm.FavoriteSlashCommands.Contains<string>(item, StringComparer.OrdinalIgnoreCase);
int num4 = num3 - slashPageOffset;
bool flag4 = num4 == _slashSelectedIndex;
System.Windows.Media.Brush brush = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.LightGray;
string toolTip = ((item3 && skillDefinition != null && !string.IsNullOrWhiteSpace(skillDefinition.Description)) ? skillDefinition.Description : item2);
if (flag != item3)
{
flag = item3;
SlashItems.Items.Add(new TextBlock
{
Text = (item3 ? "스킬" : "명령어"),
FontSize = 10.5,
FontWeight = FontWeights.SemiBold,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
Margin = new Thickness(8.0, (num3 == slashPageOffset) ? 2 : 10, 8.0, 6.0)
});
}
Border item4 = new Border
{
Background = (flag4 ? brush : System.Windows.Media.Brushes.Transparent),
BorderBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(18, 17, 24, 39)),
BorderThickness = new Thickness(1.0),
CornerRadius = new CornerRadius(12.0),
Padding = new Thickness(12.0, 10.0, 12.0, 10.0),
Margin = new Thickness(4.0, 2.0, 4.0, 2.0),
Cursor = (flag2 ? System.Windows.Input.Cursors.Hand : System.Windows.Input.Cursors.Arrow),
Opacity = (flag2 ? 1.0 : 0.5),
ToolTip = toolTip
};
Grid grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = GridLength.Auto
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(1.0, GridUnitType.Star)
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = GridLength.Auto
});
Border element = new Border
{
Width = 28.0,
Height = 28.0,
CornerRadius = new CornerRadius(8.0),
Background = (item3 ? new SolidColorBrush(System.Windows.Media.Color.FromArgb(22, 37, 99, 235)) : new SolidColorBrush(System.Windows.Media.Color.FromArgb(18, 107, 114, 128))),
Margin = new Thickness(0.0, 0.0, 10.0, 0.0),
Child = new TextBlock
{
Text = ((!item3) ? "\ue8a7" : (skillDefinition?.Icon ?? "\ue768")),
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 11.0,
Foreground = (item3 ? new SolidColorBrush(System.Windows.Media.Color.FromRgb(37, 99, 235)) : new SolidColorBrush(System.Windows.Media.Color.FromRgb(107, 114, 128))),
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
}
};
Grid.SetColumn(element, 0);
grid.Children.Add(element);
StackPanel stackPanel = new StackPanel();
if (flag3)
{
stackPanel.Children.Add(new TextBlock
{
Text = "\ue735 ",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 10.0,
Foreground = new SolidColorBrush(System.Windows.Media.Color.FromRgb(245, 158, 11)),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 2.0, 0.0)
});
}
stackPanel.Children.Add(new TextBlock
{
Text = item,
FontSize = 13.0,
FontWeight = FontWeights.SemiBold,
Foreground = (flag2 ? ((TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue) : ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray)),
VerticalAlignment = VerticalAlignment.Center
});
stackPanel.Children.Add(new TextBlock
{
Text = " — " + item2,
FontSize = 11.5,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 4.0, 0.0, 0.0),
TextWrapping = TextWrapping.Wrap
});
Grid.SetColumn(stackPanel, 1);
grid.Children.Add(stackPanel);
string favCapturedCmd = item;
Border border = new Border();
border.Width = 24.0;
border.Height = 24.0;
border.CornerRadius = new CornerRadius(4.0);
border.Background = System.Windows.Media.Brushes.Transparent;
border.Cursor = System.Windows.Input.Cursors.Hand;
border.VerticalAlignment = VerticalAlignment.Center;
border.Child = new TextBlock
{
Text = (flag3 ? "\ue735" : "\ue734"),
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 11.0,
Foreground = (flag3 ? new SolidColorBrush(System.Windows.Media.Color.FromRgb(245, 158, 11)) : ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray)),
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Opacity = (flag3 ? 1.0 : 0.4)
};
Border border2 = border;
border2.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
((Border)s).Opacity = 0.8;
};
border2.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
((Border)s).Opacity = 1.0;
};
border2.MouseLeftButtonDown += delegate(object _, MouseButtonEventArgs me)
{
me.Handled = true;
ToggleSlashFavorite(favCapturedCmd);
};
Grid.SetColumn(border2, 2);
grid.Children.Add(border2);
item4.Child = grid;
if (flag2)
{
System.Windows.Media.Brush hoverBrush = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.LightGray;
item4.MouseEnter += delegate
{
item4.Background = hoverBrush;
};
item4.MouseLeave += delegate
{
item4.Background = System.Windows.Media.Brushes.Transparent;
};
item4.MouseLeftButtonDown += delegate
{
SlashPopup.IsOpen = false;
if (capturedCmd.Equals("/help", StringComparison.OrdinalIgnoreCase))
{
InputBox.Text = "";
ShowSlashHelpWindow();
}
else
{
ShowSlashChip(capturedCmd);
InputBox.Focus();
}
};
}
SlashItems.Items.Add(item4);
}
if (num < count)
{
SlashNavDown.Visibility = Visibility.Visible;
SlashNavDownText.Text = $"▼ 아래로 {count - num}개";
}
else
{
SlashNavDown.Visibility = Visibility.Collapsed;
}
}
private void SlashPopup_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
e.Handled = true;
SlashPopup_ScrollByDelta(e.Delta);
}
private void SlashPopup_ScrollByDelta(int delta)
{
if (_slashAllMatches.Count == 0)
{
return;
}
int num = Math.Min(SlashPageSize, _slashAllMatches.Count - _slashPageOffset);
if (delta > 0)
{
if (_slashSelectedIndex > 0)
{
_slashSelectedIndex--;
}
else if (_slashSelectedIndex == 0 && _slashPageOffset > 0)
{
_slashPageOffset = Math.Max(0, _slashPageOffset - 1);
_slashSelectedIndex = 0;
}
}
else if (_slashSelectedIndex < 0)
{
_slashSelectedIndex = 0;
}
else if (_slashSelectedIndex < num - 1)
{
_slashSelectedIndex++;
}
else if (_slashPageOffset + SlashPageSize < _slashAllMatches.Count)
{
_slashPageOffset++;
_slashSelectedIndex = Math.Min(SlashPageSize - 1, _slashAllMatches.Count - _slashPageOffset - 1);
}
RenderSlashPage();
}
private void ExecuteSlashSelectedItem()
{
int num = _slashPageOffset + _slashSelectedIndex;
if (num < 0 || num >= _slashAllMatches.Count)
{
return;
}
(string Cmd, string Label, bool IsSkill) tuple = _slashAllMatches[num];
string item = tuple.Cmd;
SkillDefinition skillDefinition = (tuple.IsSkill ? SkillService.Find(item.TrimStart('/')) : null);
if (skillDefinition == null || skillDefinition.IsAvailable)
{
SlashPopup.IsOpen = false;
_slashSelectedIndex = -1;
if (item.Equals("/help", StringComparison.OrdinalIgnoreCase))
{
InputBox.Text = "";
ShowSlashHelpWindow();
}
else
{
ShowSlashChip(item);
InputBox.Focus();
}
}
}
private void ToggleSlashFavorite(string cmd)
{
List<string> favoriteSlashCommands = _settings.Settings.Llm.FavoriteSlashCommands;
string text = favoriteSlashCommands.FirstOrDefault((string f) => f.Equals(cmd, StringComparison.OrdinalIgnoreCase));
if (text != null)
{
favoriteSlashCommands.Remove(text);
}
else
{
favoriteSlashCommands.Add(cmd);
}
_settings.Save();
string text2 = InputBox.Text;
InputBox.TextChanged -= InputBox_TextChanged;
InputBox.Text = "";
InputBox.TextChanged += InputBox_TextChanged;
InputBox.Text = text2;
}
private void ShowSlashChip(string cmd)
{
_activeSlashCmd = cmd;
SlashChipText.Text = cmd;
SlashCommandChip.Visibility = Visibility.Visible;
SlashCommandChip.UpdateLayout();
double left = SlashCommandChip.Margin.Left + SlashCommandChip.ActualWidth + 6.0;
InputBox.Padding = new Thickness(left, 10.0, 14.0, 10.0);
InputBox.Text = "";
UpdateDraftPreviewCard();
}
private void HideSlashChip(bool restoreText = false)
{
if (_activeSlashCmd != null)
{
string activeSlashCmd = _activeSlashCmd;
_activeSlashCmd = null;
SlashCommandChip.Visibility = Visibility.Collapsed;
InputBox.Padding = new Thickness(14.0, 10.0, 14.0, 10.0);
if (restoreText)
{
InputBox.Text = activeSlashCmd + " ";
InputBox.CaretIndex = InputBox.Text.Length;
}
UpdateDraftPreviewCard();
}
}
private static (string? slashSystem, string userText) ParseSlashCommand(string input)
{
foreach (KeyValuePair<string, (string, string, string)> slashCommand in SlashCommands)
{
slashCommand.Deconstruct(out var key, out var value);
(string, string, string) tuple = value;
string text = key;
string item = tuple.Item2;
if (input.StartsWith(text, StringComparison.OrdinalIgnoreCase))
{
if (item == "__HELP__")
{
return (slashSystem: null, userText: input);
}
key = input;
int length = text.Length;
string text2 = key.Substring(length, key.Length - length).Trim();
return (slashSystem: item, userText: string.IsNullOrEmpty(text2) ? text : text2);
}
}
foreach (SkillDefinition skill in SkillService.Skills)
{
string text3 = "/" + skill.Name;
if (input.StartsWith(text3, StringComparison.OrdinalIgnoreCase))
{
string key = input;
int length = text3.Length;
string text4 = key.Substring(length, key.Length - length).Trim();
return (slashSystem: skill.SystemPrompt, userText: string.IsNullOrEmpty(text4) ? skill.Label : text4);
}
}
return (slashSystem: null, userText: input);
}
private void ShowDropActionMenu(string[] files)
{
string item = System.IO.Path.GetExtension(files[0]).ToLowerInvariant();
List<(string, string, string)> list = CollectionExtensions.GetValueOrDefault(key: CodeExtensions.Contains(item) ? "code" : (DataExtensions.Contains(item) ? "data" : ((!ImageExtensions.Contains(item)) ? "document" : "image")), dictionary: DropActions) ?? DropActions["document"];
Popup? dropActionPopup = _dropActionPopup;
if (dropActionPopup != null)
{
((DependencyObject)dropActionPopup).SetValue(Popup.IsOpenProperty, (object)false);
}
StackPanel stackPanel = new StackPanel();
TextBlock textBlock = new TextBlock();
textBlock.Text = "\ud83d\udcce " + System.IO.Path.GetFileName(files[0]) + ((files.Length > 1) ? $" 외 {files.Length - 1}개" : "");
textBlock.FontSize = 11.0;
textBlock.Foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
textBlock.Margin = new Thickness(12.0, 8.0, 12.0, 6.0);
TextBlock element = textBlock;
stackPanel.Children.Add(element);
System.Windows.Media.Brush hoverBrush = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue));
System.Windows.Media.Brush foreground = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue;
System.Windows.Media.Brush foreground2 = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
foreach (var item5 in list)
{
string item2 = item5.Item1;
string item3 = item5.Item2;
string item4 = item5.Item3;
string capturedPrompt = item4;
Border row = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(6.0),
Padding = new Thickness(12.0, 7.0, 12.0, 7.0),
Margin = new Thickness(4.0, 1.0, 4.0, 1.0),
Cursor = System.Windows.Input.Cursors.Hand
};
StackPanel stackPanel2 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel2.Children.Add(new TextBlock
{
Text = item3,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 13.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 8.0, 0.0)
});
stackPanel2.Children.Add(new TextBlock
{
Text = item2,
FontSize = 13.0,
FontWeight = FontWeights.SemiBold,
Foreground = foreground2,
VerticalAlignment = VerticalAlignment.Center
});
row.Child = stackPanel2;
row.MouseEnter += delegate
{
row.Background = hoverBrush;
};
row.MouseLeave += delegate
{
row.Background = System.Windows.Media.Brushes.Transparent;
};
row.MouseLeftButtonUp += delegate
{
if (_dropActionPopup != null)
{
_dropActionPopup.IsOpen = false;
}
string[] array = files;
foreach (string filePath in array)
{
AddAttachedFile(filePath);
}
InputBox.Text = capturedPrompt;
InputBox.CaretIndex = InputBox.Text.Length;
InputBox.Focus();
if (_settings.Settings.Llm.DragDropAutoSend)
{
SendMessageAsync();
}
};
stackPanel.Children.Add(row);
}
Border attachOnly = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(6.0),
Padding = new Thickness(12.0, 7.0, 12.0, 7.0),
Margin = new Thickness(4.0, 1.0, 4.0, 1.0),
Cursor = System.Windows.Input.Cursors.Hand
};
StackPanel stackPanel3 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel3.Children.Add(new TextBlock
{
Text = "\ue723",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 13.0,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 8.0, 0.0)
});
stackPanel3.Children.Add(new TextBlock
{
Text = "첨부만",
FontSize = 13.0,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
VerticalAlignment = VerticalAlignment.Center
});
attachOnly.Child = stackPanel3;
attachOnly.MouseEnter += delegate
{
attachOnly.Background = hoverBrush;
};
attachOnly.MouseLeave += delegate
{
attachOnly.Background = System.Windows.Media.Brushes.Transparent;
};
attachOnly.MouseLeftButtonUp += delegate
{
if (_dropActionPopup != null)
{
_dropActionPopup.IsOpen = false;
}
string[] array = files;
foreach (string filePath in array)
{
AddAttachedFile(filePath);
}
InputBox.Focus();
};
stackPanel.Children.Add(attachOnly);
Border border = new Border();
border.Background = (TryFindResource("LauncherBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromRgb(26, 27, 46));
border.CornerRadius = new CornerRadius(12.0);
border.BorderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
border.BorderThickness = new Thickness(1.0);
border.Padding = new Thickness(4.0, 4.0, 4.0, 6.0);
border.Child = stackPanel;
border.MinWidth = 200.0;
border.Effect = new DropShadowEffect
{
Color = Colors.Black,
BlurRadius = 16.0,
ShadowDepth = 4.0,
Opacity = 0.3
};
Border child = border;
_dropActionPopup = new Popup
{
PlacementTarget = InputBorder,
Placement = PlacementMode.Top,
StaysOpen = false,
AllowsTransparency = true,
PopupAnimation = PopupAnimation.Fade,
Child = child
};
_dropActionPopup.IsOpen = true;
}
private void ShowSlashHelpWindow()
{
//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
//IL_0303: Unknown result type (might be due to invalid IL or missing references)
System.Windows.Media.Brush background = (TryFindResource("LauncherBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromRgb(26, 27, 46));
System.Windows.Media.Brush brush = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush brush2 = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush accent = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue;
System.Windows.Media.Brush itemBg = (TryFindResource("ItemBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromArgb(30, byte.MaxValue, byte.MaxValue, byte.MaxValue));
System.Windows.Media.Brush hoverBg = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromArgb(40, byte.MaxValue, byte.MaxValue, byte.MaxValue));
Window win = new Window
{
Title = "AX Agent — 슬래시 명령어 도움말",
Width = 560.0,
Height = 640.0,
MinWidth = 440.0,
MinHeight = 500.0,
WindowStyle = WindowStyle.None,
AllowsTransparency = true,
Background = System.Windows.Media.Brushes.Transparent,
ResizeMode = ResizeMode.CanResize,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = this,
Icon = base.Icon
};
Border border = new Border();
border.Background = background;
border.CornerRadius = new CornerRadius(16.0);
border.BorderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
border.BorderThickness = new Thickness(1.0);
border.Margin = new Thickness(10.0);
border.Effect = new DropShadowEffect
{
Color = Colors.Black,
BlurRadius = 20.0,
ShadowDepth = 4.0,
Opacity = 0.3
};
Border border2 = border;
Grid grid = new Grid();
grid.RowDefinitions.Add(new RowDefinition
{
Height = new GridLength(52.0)
});
grid.RowDefinitions.Add(new RowDefinition
{
Height = new GridLength(1.0, GridUnitType.Star)
});
Border border3 = new Border
{
CornerRadius = new CornerRadius(16.0, 16.0, 0.0, 0.0),
Background = new LinearGradientBrush(System.Windows.Media.Color.FromRgb(26, 27, 46), System.Windows.Media.Color.FromRgb(59, 78, 204), new Point(0.0, 0.0), new Point(1.0, 1.0)),
Padding = new Thickness(20.0, 0.0, 20.0, 0.0)
};
Grid grid2 = new Grid();
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
VerticalAlignment = VerticalAlignment.Center
};
stackPanel.Children.Add(new TextBlock
{
Text = "\ue946",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 16.0,
Foreground = System.Windows.Media.Brushes.LightCyan,
Margin = new Thickness(0.0, 0.0, 10.0, 0.0),
VerticalAlignment = VerticalAlignment.Center
});
stackPanel.Children.Add(new TextBlock
{
Text = "슬래시 명령어 (/ Commands)",
FontSize = 15.0,
FontWeight = FontWeights.SemiBold,
Foreground = System.Windows.Media.Brushes.White,
VerticalAlignment = VerticalAlignment.Center
});
grid2.Children.Add(stackPanel);
Border closeBtn = new Border
{
Width = 30.0,
Height = 30.0,
CornerRadius = new CornerRadius(8.0),
Background = System.Windows.Media.Brushes.Transparent,
Cursor = System.Windows.Input.Cursors.Hand,
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Center
};
closeBtn.Child = new TextBlock
{
Text = "\ue711",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 11.0,
Foreground = new SolidColorBrush(System.Windows.Media.Color.FromArgb(136, 170, byte.MaxValue, 204)),
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
closeBtn.MouseEnter += delegate
{
closeBtn.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(34, byte.MaxValue, byte.MaxValue, byte.MaxValue));
};
closeBtn.MouseLeave += delegate
{
closeBtn.Background = System.Windows.Media.Brushes.Transparent;
};
closeBtn.MouseLeftButtonDown += delegate(object _, MouseButtonEventArgs me)
{
me.Handled = true;
win.Close();
};
grid2.Children.Add(closeBtn);
border3.Child = grid2;
Grid.SetRow(border3, 0);
grid.Children.Add(border3);
ScrollViewer scrollViewer = new ScrollViewer
{
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
Padding = new Thickness(20.0, 14.0, 20.0, 20.0)
};
StackPanel stackPanel2 = new StackPanel();
stackPanel2.Children.Add(new TextBlock
{
Text = "입력창에 /를 입력하면 사용할 수 있는 명령어가 표시됩니다.\n명령어를 선택한 후 내용을 입력하면 해당 기능이 적용됩니다.",
FontSize = 12.0,
Foreground = brush2,
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(0.0, 0.0, 0.0, 16.0),
LineHeight = 20.0
});
AddHelpSection(stackPanel2, "\ud83d\udccc 공통 명령어", "모든 탭(Chat, Cowork, Code)에서 사용 가능", brush, brush2, accent, itemBg, hoverBg, ("/summary", "텍스트/문서를 핵심 포인트 중심으로 요약합니다."), ("/translate", "텍스트를 영어로 번역합니다. 원문의 톤을 유지합니다."), ("/explain", "내용을 쉽고 자세하게 설명합니다. 예시를 포함합니다."), ("/fix", "맞춤법, 문법, 자연스러운 표현을 교정합니다."));
AddHelpSection(stackPanel2, "\ud83d\udee0\ufe0f 개발 명령어", "Cowork, Code 탭에서만 사용 가능", brush, brush2, accent, itemBg, hoverBg, ("/review", "Git diff를 분석하여 버그, 성능, 보안 이슈를 찾습니다."), ("/pr", "변경사항을 PR 설명 형식(Summary, Changes, Test Plan)으로 요약합니다."), ("/test", "코드에 대한 단위 테스트를 자동 생성합니다."), ("/structure", "프로젝트의 폴더/파일 구조를 분석하고 설명합니다."), ("/build", "프로젝트를 빌드합니다. 오류 발생 시 분석합니다."), ("/search", "자연어로 코드베이스를 시맨틱 검색합니다."));
IReadOnlyList<SkillDefinition> skills = SkillService.Skills;
if (skills.Count > 0)
{
(string, string)[] items = skills.Select((SkillDefinition s) => ("/" + s.Name, Description: s.Description)).ToArray();
AddHelpSection(stackPanel2, "⚡ 스킬 명령어", $"{skills.Count}개 로드됨 — %APPDATA%\\AxCopilot\\skills\\에서 추가 가능", brush, brush2, accent, itemBg, hoverBg, items);
}
stackPanel2.Children.Add(new Border
{
Height = 1.0,
Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(30, byte.MaxValue, byte.MaxValue, byte.MaxValue)),
Margin = new Thickness(0.0, 12.0, 0.0, 12.0)
});
StackPanel stackPanel3 = new StackPanel();
stackPanel3.Children.Add(new TextBlock
{
Text = "\ud83d\udca1 사용 팁",
FontSize = 13.0,
FontWeight = FontWeights.SemiBold,
Foreground = brush,
Margin = new Thickness(0.0, 0.0, 0.0, 8.0)
});
string[] array = new string[4] { "/ 입력 시 현재 탭에 맞는 명령어만 자동완성됩니다.", "파일을 드래그하면 유형별 AI 액션 팝업이 나타납니다.", "스킬 파일(*.skill.md)을 추가하면 나만의 워크플로우를 만들 수 있습니다.", "Cowork/Code 탭에서 에이전트가 도구를 활용하여 더 강력한 작업을 수행합니다." };
string[] array2 = array;
foreach (string text in array2)
{
stackPanel3.Children.Add(new TextBlock
{
Text = "• " + text,
FontSize = 12.0,
Foreground = brush2,
Margin = new Thickness(8.0, 2.0, 0.0, 2.0),
TextWrapping = TextWrapping.Wrap,
LineHeight = 18.0
});
}
stackPanel2.Children.Add(stackPanel3);
scrollViewer.Content = stackPanel2;
Grid.SetRow(scrollViewer, 1);
grid.Children.Add(scrollViewer);
border2.Child = grid;
win.Content = border2;
border3.MouseLeftButtonDown += delegate
{
try
{
win.DragMove();
}
catch
{
}
};
win.ShowDialog();
}
private static void AddHelpSection(StackPanel parent, string title, string subtitle, System.Windows.Media.Brush fg, System.Windows.Media.Brush fg2, System.Windows.Media.Brush accent, System.Windows.Media.Brush itemBg, System.Windows.Media.Brush hoverBg, params (string Cmd, string Desc)[] items)
{
parent.Children.Add(new TextBlock
{
Text = title,
FontSize = 14.0,
FontWeight = FontWeights.SemiBold,
Foreground = fg,
Margin = new Thickness(0.0, 8.0, 0.0, 2.0)
});
parent.Children.Add(new TextBlock
{
Text = subtitle,
FontSize = 11.0,
Foreground = fg2,
Margin = new Thickness(0.0, 0.0, 0.0, 8.0)
});
for (int i = 0; i < items.Length; i++)
{
(string Cmd, string Desc) tuple = items[i];
string item = tuple.Cmd;
string item2 = tuple.Desc;
Border row = new Border
{
Background = itemBg,
CornerRadius = new CornerRadius(8.0),
Padding = new Thickness(12.0, 8.0, 12.0, 8.0),
Margin = new Thickness(0.0, 3.0, 0.0, 3.0)
};
Grid grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(120.0)
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(1.0, GridUnitType.Star)
});
TextBlock element = new TextBlock
{
Text = item,
FontSize = 13.0,
FontWeight = FontWeights.SemiBold,
Foreground = accent,
VerticalAlignment = VerticalAlignment.Center,
FontFamily = new System.Windows.Media.FontFamily("Consolas")
};
Grid.SetColumn(element, 0);
grid.Children.Add(element);
TextBlock element2 = new TextBlock
{
Text = item2,
FontSize = 12.0,
Foreground = fg2,
VerticalAlignment = VerticalAlignment.Center,
TextWrapping = TextWrapping.Wrap
};
Grid.SetColumn(element2, 1);
grid.Children.Add(element2);
row.Child = grid;
row.MouseEnter += delegate
{
row.Background = hoverBg;
};
row.MouseLeave += delegate
{
row.Background = itemBg;
};
parent.Children.Add(row);
}
}
private async Task SendMessageAsync()
{
string rawText = InputBox.Text.Trim();
string text = ((_activeSlashCmd != null) ? (_activeSlashCmd + " " + rawText).Trim() : rawText);
HideSlashChip();
if (string.IsNullOrEmpty(text) || _isStreaming)
{
return;
}
ClearPromptCardPlaceholder();
(string, string) tuple = ParseSlashCommand(text);
var (slashSystem, _) = tuple;
_ = tuple.Item2;
string originTab = _activeTab;
ChatConversation conv;
lock (_convLock)
{
if (_currentConversation == null)
{
_currentConversation = new ChatConversation
{
Tab = _activeTab
};
}
conv = _currentConversation;
}
ChatMessage userMsg = new ChatMessage
{
Role = "user",
Content = text
};
lock (_convLock)
{
conv.Messages.Add(userMsg);
}
if (conv.Messages.Count((ChatMessage m) => m.Role == "user") == 1)
{
conv.Title = ((text.Length > 30) ? (text.Substring(0, 30) + "…") : text);
}
UpdateChatTitle();
AddMessageBubble("user", text);
InputBox.Text = "";
EmptyState.Visibility = Visibility.Collapsed;
UsageStatisticsService.RecordChat(_activeTab);
ForceScrollToEnd();
PlayRainbowGlow();
_isStreaming = true;
BtnSend.IsEnabled = false;
BtnSend.Visibility = Visibility.Collapsed;
BtnStop.Visibility = Visibility.Visible;
if (_activeTab == "Cowork" || _activeTab == "Code")
{
BtnPause.Visibility = Visibility.Visible;
}
_streamCts = new CancellationTokenSource();
ChatMessage assistantMsg = new ChatMessage
{
Role = "assistant",
Content = ""
};
lock (_convLock)
{
conv.Messages.Add(assistantMsg);
}
TextBlock streamText;
StackPanel streamContainer = CreateStreamingContainer(out streamText);
MessagePanel.Children.Add(streamContainer);
ForceScrollToEnd();
StringBuilder sb = new StringBuilder();
_activeStreamText = streamText;
_cachedStreamContent = "";
_displayedLength = 0;
_cursorVisible = true;
_aiIconPulseStopped = false;
_cursorTimer.Start();
_typingTimer.Start();
_streamStartTime = DateTime.UtcNow;
_elapsedTimer.Start();
SetStatus("응답 생성 중...", spinning: true);
ModelRouteResult routeResult = null;
bool queueContinuationAllowed = true;
try
{
List<ChatMessage> sendMessages;
lock (_convLock)
{
sendMessages = conv.Messages.SkipLast(1).ToList();
}
if (!string.IsNullOrEmpty(conv.SystemCommand))
{
sendMessages.Insert(0, new ChatMessage
{
Role = "system",
Content = conv.SystemCommand
});
}
if (!string.IsNullOrEmpty(slashSystem))
{
sendMessages.Insert(0, new ChatMessage
{
Role = "system",
Content = slashSystem
});
}
if (_attachedFiles.Count > 0)
{
string fileContext = BuildFileContextPrompt();
if (!string.IsNullOrEmpty(fileContext))
{
int lastUserIdx = sendMessages.FindLastIndex((ChatMessage m) => m.Role == "user");
if (lastUserIdx >= 0)
{
sendMessages[lastUserIdx] = new ChatMessage
{
Role = "user",
Content = sendMessages[lastUserIdx].Content + fileContext
};
}
}
userMsg.AttachedFiles = _attachedFiles.ToList();
_attachedFiles.Clear();
RefreshAttachedFilesUI();
}
if (_pendingImages.Count > 0)
{
userMsg.Images = _pendingImages.ToList();
int lastUserIdx2 = sendMessages.FindLastIndex((ChatMessage m) => m.Role == "user");
if (lastUserIdx2 >= 0)
{
sendMessages[lastUserIdx2] = new ChatMessage
{
Role = "user",
Content = sendMessages[lastUserIdx2].Content,
Images = _pendingImages.ToList()
};
}
_pendingImages.Clear();
AttachedFilesPanel.Items.Clear();
if (_attachedFiles.Count == 0)
{
AttachedFilesPanel.Visibility = Visibility.Collapsed;
}
}
if (_settings.Settings.Llm.EnableAutoRouter)
{
routeResult = _router.Route(text);
if (routeResult != null)
{
_llm.PushRouteOverride(routeResult.Service, routeResult.Model);
SetStatus("라우팅: " + routeResult.DetectedIntent + " → " + routeResult.DisplayName, spinning: true);
}
}
if (_activeTab == "Cowork")
{
OpenWorkflowAnalyzerIfEnabled();
_agentCumulativeInputTokens = 0;
_agentCumulativeOutputTokens = 0;
_agentLoop.EventOccurred += OnAgentEvent;
_agentLoop.UserDecisionCallback = CreatePlanDecisionCallback();
try
{
string coworkSystem = BuildCoworkSystemPrompt();
if (!string.IsNullOrEmpty(coworkSystem))
{
sendMessages.Insert(0, new ChatMessage
{
Role = "system",
Content = coworkSystem
});
}
_agentLoop.ActiveTab = _activeTab;
string response = await _agentLoop.RunAsync(sendMessages, _streamCts.Token);
sb.Append(response);
assistantMsg.Content = response;
StopAiIconPulse();
_cachedStreamContent = response;
if (_settings.Settings.Llm.NotifyOnComplete)
{
NotificationService.Notify("AX Cowork Agent", "코워크 작업이 완료되었습니다.");
}
}
finally
{
_agentLoop.EventOccurred -= OnAgentEvent;
_agentLoop.UserDecisionCallback = null;
}
}
else if (_activeTab == "Code")
{
OpenWorkflowAnalyzerIfEnabled();
_agentCumulativeInputTokens = 0;
_agentCumulativeOutputTokens = 0;
_agentLoop.EventOccurred += OnAgentEvent;
_agentLoop.UserDecisionCallback = CreatePlanDecisionCallback();
try
{
string codeSystem = BuildCodeSystemPrompt();
if (!string.IsNullOrEmpty(codeSystem))
{
sendMessages.Insert(0, new ChatMessage
{
Role = "system",
Content = codeSystem
});
}
_agentLoop.ActiveTab = "Code";
string response2 = await _agentLoop.RunAsync(sendMessages, _streamCts.Token);
sb.Append(response2);
assistantMsg.Content = response2;
StopAiIconPulse();
_cachedStreamContent = response2;
if (_settings.Settings.Llm.NotifyOnComplete)
{
NotificationService.Notify("AX Code Agent", "코드 작업이 완료되었습니다.");
}
}
finally
{
_agentLoop.EventOccurred -= OnAgentEvent;
_agentLoop.UserDecisionCallback = null;
}
}
else if (_settings.Settings.Llm.Streaming)
{
await foreach (string chunk in _llm.StreamAsync(sendMessages, _streamCts.Token))
{
sb.Append(chunk);
StopAiIconPulse();
_cachedStreamContent = sb.ToString();
await ((DispatcherObject)this).Dispatcher.InvokeAsync((Action)delegate
{
}, (DispatcherPriority)4);
}
_cachedStreamContent = sb.ToString();
assistantMsg.Content = _cachedStreamContent;
DateTime drainStart = DateTime.UtcNow;
while (_displayedLength < _cachedStreamContent.Length && (DateTime.UtcNow - drainStart).TotalMilliseconds < 600.0)
{
await ((DispatcherObject)this).Dispatcher.InvokeAsync((Action)delegate
{
}, (DispatcherPriority)4);
}
}
else
{
string response3 = await _llm.SendAsync(sendMessages, _streamCts.Token);
sb.Append(response3);
assistantMsg.Content = response3;
}
}
catch (OperationCanceledException)
{
if (sb.Length == 0)
{
sb.Append("(취소됨)");
}
queueContinuationAllowed = false;
assistantMsg.Content = sb.ToString();
}
catch (Exception ex2)
{
Exception ex3 = ex2;
string errMsg = "⚠ 오류: " + ex3.Message;
sb.Clear();
sb.Append(errMsg);
queueContinuationAllowed = false;
assistantMsg.Content = errMsg;
AddRetryButton();
}
finally
{
if (routeResult != null)
{
_llm.ClearRouteOverride();
UpdateModelLabel();
}
_cursorTimer.Stop();
_elapsedTimer.Stop();
_typingTimer.Stop();
HideStickyProgress();
StopRainbowGlow();
_activeStreamText = null;
_elapsedLabel = null;
_cachedStreamContent = "";
_isStreaming = false;
BtnSend.IsEnabled = true;
BtnStop.Visibility = Visibility.Collapsed;
BtnSend.Visibility = Visibility.Visible;
_streamCts?.Dispose();
_streamCts = null;
SetStatusIdle();
}
FinalizeStreamingContainer(streamContainer, streamText, assistantMsg.Content, assistantMsg);
AutoScrollIfNeeded();
try
{
_storage.Save(conv);
}
catch (Exception ex4)
{
LogService.Debug("대화 저장 실패: " + ex4.Message);
}
_tabConversationId[originTab] = conv.Id;
RefreshConversationList();
if (queueContinuationAllowed)
{
StartNextQueuedDraftIfAny();
}
}
private string BuildCoworkSystemPrompt()
{
string currentWorkFolder = GetCurrentWorkFolder();
LlmSettings llm = _settings.Settings.Llm;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("You are AX Copilot Agent. You can read, write, and edit files using the provided tools.");
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(20, 3, stringBuilder2);
handler.AppendLiteral("Today's date: ");
handler.AppendFormatted(DateTime.Now, "yyyy년 M월 d일");
handler.AppendLiteral(" (");
handler.AppendFormatted(DateTime.Now, "yyyy-MM-dd");
handler.AppendLiteral(", ");
handler.AppendFormatted(DateTime.Now, "dddd");
handler.AppendLiteral(").");
stringBuilder3.AppendLine(ref handler);
stringBuilder.AppendLine("Available skills: excel_create (.xlsx), docx_create (.docx), csv_create (.csv), markdown_create (.md), html_create (.html), script_create (.bat/.ps1), document_review (품질 검증), format_convert (포맷 변환).");
stringBuilder.AppendLine("Parallel tools: spawn_agent, wait_agents. Use them for independent side research, codebase inspection, or document evidence gathering.");
stringBuilder.AppendLine("Always explain your plan step by step BEFORE executing tools. After creating files, summarize what was created.");
stringBuilder.AppendLine("IMPORTANT: When creating documents with dates, always use today's actual date above. Never use placeholder or fictional dates.");
stringBuilder.AppendLine("IMPORTANT: When asked to create a document with multiple sections (reports, proposals, analyses, etc.), you MUST:");
stringBuilder.AppendLine(" 1. First, plan the document: decide the exact sections (headings), their order, and key points for each section based on the topic.");
stringBuilder.AppendLine(" 2. Call document_plan with sections_hint = your planned section titles (comma-separated). Example: sections_hint=\"회사 개요, 사업 현황, 재무 분석, SWOT, 전략 제언, 결론\"");
stringBuilder.AppendLine(" This ensures the document structure matches YOUR plan, not a generic template.");
stringBuilder.AppendLine(" 3. Then immediately call html_create (or docx_create/file_write) using the scaffold from document_plan.");
stringBuilder.AppendLine(" 4. Write actual detailed content for EVERY section — no skipping, no placeholders, no minimal content.");
stringBuilder.AppendLine(" 5. Do NOT call html_create directly without document_plan for multi-section documents.");
stringBuilder.AppendLine("\n## Document Quality Review");
stringBuilder.AppendLine("After creating any document (html_create, docx_create, excel_create, etc.), you MUST perform a self-review:");
stringBuilder.AppendLine("1. Use file_read to read the generated file and verify the content is complete");
stringBuilder.AppendLine("2. Check for logical errors: incorrect dates, inconsistent data, missing sections, broken formatting");
stringBuilder.AppendLine("3. Verify all requested topics/sections from the user's original request are covered");
stringBuilder.AppendLine("4. If issues found, fix them using file_write or file_edit, then re-verify");
stringBuilder.AppendLine("5. Report the review result to the user: what was checked and whether corrections were made");
stringBuilder.Append(BuildSubAgentGuidanceSection(isCodeAgent: false));
stringBuilder.AppendLine("\n## Format Conversion");
stringBuilder.AppendLine("When the user requests format conversion (e.g., HTML→Word, Excel→CSV, Markdown→HTML):");
stringBuilder.AppendLine("1. Use file_read or document_read to read the source file content");
stringBuilder.AppendLine("2. Create a new file in the target format using the appropriate skill (docx_create, html_create, etc.)");
stringBuilder.AppendLine("3. Preserve the content structure, formatting, and data as closely as possible");
string defaultOutputFormat = llm.DefaultOutputFormat;
if (!string.IsNullOrEmpty(defaultOutputFormat) && defaultOutputFormat != "auto")
{
Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["xlsx"] = "Excel (.xlsx) using excel_create",
["docx"] = "Word (.docx) using docx_create",
["html"] = "HTML (.html) using html_create",
["md"] = "Markdown (.md) using markdown_create",
["csv"] = "CSV (.csv) using csv_create"
};
if (dictionary.TryGetValue(defaultOutputFormat, out var value))
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(93, 1, stringBuilder2);
handler.AppendLiteral("IMPORTANT: User prefers output format: ");
handler.AppendFormatted(value);
handler.AppendLiteral(". Use this format unless the user specifies otherwise.");
stringBuilder4.AppendLine(ref handler);
}
}
if (!string.IsNullOrEmpty(_selectedMood) && _selectedMood != "modern")
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(83, 1, stringBuilder2);
handler.AppendLiteral("When creating HTML documents with html_create, use mood=\"");
handler.AppendFormatted(_selectedMood);
handler.AppendLiteral("\" for the design template.");
stringBuilder5.AppendLine(ref handler);
}
else
{
stringBuilder.AppendLine("When creating HTML documents with html_create, you can set 'mood' parameter: modern, professional, creative, minimal, elegant, dark, colorful, corporate, magazine, dashboard.");
}
if (!string.IsNullOrEmpty(currentWorkFolder))
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder6 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(21, 1, stringBuilder2);
handler.AppendLiteral("Current work folder: ");
handler.AppendFormatted(currentWorkFolder);
stringBuilder6.AppendLine(ref handler);
}
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder7 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, stringBuilder2);
handler.AppendLiteral("File permission mode: ");
handler.AppendFormatted(llm.FilePermission);
stringBuilder7.AppendLine(ref handler);
string folderDataUsage = _folderDataUsage;
string text = folderDataUsage;
if (!(text == "active"))
{
if (text == "passive")
{
stringBuilder.AppendLine("Folder Data Usage = PASSIVE. You have 'document_read' and 'folder_map' tools. Only read folder documents when the user explicitly asks you to reference or use them.");
}
else
{
stringBuilder.AppendLine("Folder Data Usage = NONE. Do NOT read or reference documents in the work folder unless the user explicitly provides a file path.");
}
}
else
{
stringBuilder.AppendLine("IMPORTANT: Folder Data Usage = ACTIVE. You have 'document_read' and 'folder_map' tools available.");
stringBuilder.AppendLine("Before creating reports, use folder_map to scan the work folder structure. Then EVALUATE whether each document is RELEVANT to the user's current request topic. Only use document_read on files that are clearly related to the conversation subject. Do NOT read or reference files that are unrelated to the user's request, even if they exist in the folder. In your planning step, list which files you plan to read and explain WHY they are relevant.");
}
lock (_convLock)
{
if (_currentConversation != null && !string.IsNullOrEmpty(_currentConversation.SystemCommand))
{
stringBuilder.AppendLine("\n" + _currentConversation.SystemCommand);
}
}
stringBuilder.Append(LoadProjectContext(currentWorkFolder));
stringBuilder.Append(BuildProjectRulesSection(currentWorkFolder));
stringBuilder.Append(BuildMemorySection(currentWorkFolder));
stringBuilder.Append(BuildFeedbackContext());
return stringBuilder.ToString();
}
private string BuildCodeSystemPrompt()
{
string currentWorkFolder = GetCurrentWorkFolder();
LlmSettings llm = _settings.Settings.Llm;
CodeSettings code = llm.Code;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("You are AX Copilot Code Agent — a senior software engineer for enterprise development.");
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(18, 2, stringBuilder2);
handler.AppendLiteral("Today's date: ");
handler.AppendFormatted(DateTime.Now, "yyyy년 M월 d일");
handler.AppendLiteral(" (");
handler.AppendFormatted(DateTime.Now, "yyyy-MM-dd");
handler.AppendLiteral(").");
stringBuilder3.AppendLine(ref handler);
stringBuilder.AppendLine("Available tools: file_read, file_write, file_edit (supports replace_all), glob, grep (supports context_lines, case_sensitive), folder_map, process, dev_env_detect, build_run, git_tool.");
stringBuilder.AppendLine("Parallel tools: spawn_agent, wait_agents. Use them when side tasks can run independently.");
stringBuilder.AppendLine("IMPORTANT: When creating documents with dates, always use today's actual date above.");
stringBuilder.AppendLine("\n## Core Workflow (MANDATORY — follow this order)");
stringBuilder.AppendLine("1. ORIENT: Run folder_map (depth=2) to understand project structure. Check .gitignore, README, config files.");
stringBuilder.AppendLine("2. BASELINE: If tests exist, run build_run action='test' FIRST to establish baseline. Record pass/fail count.");
stringBuilder.AppendLine("3. ANALYZE: Use grep (with context_lines=2) + file_read to deeply understand the code you'll modify.");
stringBuilder.AppendLine(" - Always check callers/references: grep for function/class names to find all usage points.");
stringBuilder.AppendLine(" - Read test files related to the code you're changing to understand expected behavior.");
stringBuilder.AppendLine("4. PLAN: Present your analysis + impact assessment. List ALL files that will be modified.");
stringBuilder.AppendLine(" - Explain WHY each change is needed and what could break.");
stringBuilder.AppendLine(" - Wait for user approval before proceeding.");
stringBuilder.AppendLine("5. IMPLEMENT: Apply changes using file_edit (preferred — shows diff). Use file_write only for new files.");
stringBuilder.AppendLine(" - Make the MINIMUM changes needed. Don't refactor unrelated code.");
stringBuilder.AppendLine(" - Prefer file_edit with replace_all=false for precision edits.");
stringBuilder.AppendLine("6. VERIFY: Run build_run action='build' then action='test'. Compare results with baseline.");
stringBuilder.AppendLine(" - If tests fail that passed before, fix immediately.");
stringBuilder.AppendLine(" - If build fails, analyze error output and correct.");
stringBuilder.AppendLine("7. GIT: Use git_tool to check status, create diff, and optionally commit.");
stringBuilder.AppendLine("8. REPORT: Summarize changes, test results, and any remaining concerns.");
stringBuilder.Append(BuildSubAgentGuidanceSection(isCodeAgent: true));
stringBuilder.AppendLine("\n## Development Environment");
stringBuilder.AppendLine("Use dev_env_detect to check installed IDEs, runtimes, and build tools before running commands.");
stringBuilder.AppendLine("IMPORTANT: Do NOT attempt to install compilers, IDEs, or build tools. Only use what is already installed.");
stringBuilder.AppendLine("\n## Package Repositories");
if (!string.IsNullOrEmpty(code.NexusBaseUrl))
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(18, 1, stringBuilder2);
handler.AppendLiteral("Enterprise Nexus: ");
handler.AppendFormatted(code.NexusBaseUrl);
stringBuilder4.AppendLine(ref handler);
}
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(14, 1, stringBuilder2);
handler.AppendLiteral("NuGet (.NET): ");
handler.AppendFormatted(code.NugetSource);
stringBuilder5.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder6 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(21, 1, stringBuilder2);
handler.AppendLiteral("PyPI/Conda (Python): ");
handler.AppendFormatted(code.PypiSource);
stringBuilder6.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder7 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(14, 1, stringBuilder2);
handler.AppendLiteral("Maven (Java): ");
handler.AppendFormatted(code.MavenSource);
stringBuilder7.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder8 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(18, 1, stringBuilder2);
handler.AppendLiteral("npm (JavaScript): ");
handler.AppendFormatted(code.NpmSource);
stringBuilder8.AppendLine(ref handler);
stringBuilder.AppendLine("When adding dependencies, use these repository URLs.");
if (!string.IsNullOrEmpty(code.PreferredIdePath))
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder9 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(16, 1, stringBuilder2);
handler.AppendLiteral("\nPreferred IDE: ");
handler.AppendFormatted(code.PreferredIdePath);
stringBuilder9.AppendLine(ref handler);
}
if (_selectedLanguage != "auto")
{
string selectedLanguage = _selectedLanguage;
if (1 == 0)
{
}
string text = selectedLanguage switch
{
"python" => "Python",
"java" => "Java",
"csharp" => "C# (.NET)",
"cpp" => "C/C++",
"javascript" => "JavaScript/TypeScript",
_ => _selectedLanguage,
};
if (1 == 0)
{
}
string value = text;
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder10 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(96, 1, stringBuilder2);
handler.AppendLiteral("\nIMPORTANT: User selected language: ");
handler.AppendFormatted(value);
handler.AppendLiteral(". Prioritize this language for code analysis and generation.");
stringBuilder10.AppendLine(ref handler);
}
stringBuilder.AppendLine("\n## Language Guidelines");
stringBuilder.AppendLine("- C# (.NET): Use dotnet CLI. NuGet for packages. Follow Microsoft naming conventions.");
stringBuilder.AppendLine("- Python: Use conda/pip. Follow PEP8. Use type hints. Virtual env preferred.");
stringBuilder.AppendLine("- Java: Use Maven/Gradle. Follow Google Java Style Guide.");
stringBuilder.AppendLine("- C++: Use CMake for build. Follow C++ Core Guidelines.");
stringBuilder.AppendLine("- JavaScript/TypeScript: Use npm/yarn. Follow ESLint rules. Vue3 uses Composition API.");
stringBuilder.AppendLine("\n## Code Quality & Safety");
stringBuilder.AppendLine("- NEVER delete or overwrite files without user confirmation.");
stringBuilder.AppendLine("- ALWAYS read a file before editing it. Don't guess contents.");
stringBuilder.AppendLine("- Prefer file_edit over file_write for existing files (shows diff).");
stringBuilder.AppendLine("- Use grep to find ALL references before renaming/removing anything.");
stringBuilder.AppendLine("- If unsure about a change's impact, ask the user first.");
stringBuilder.AppendLine("- For large refactors, do them incrementally with build verification between steps.");
stringBuilder.AppendLine("- Use git_tool action='diff' to review your changes before committing.");
stringBuilder.AppendLine("\n## Lint & Format");
stringBuilder.AppendLine("After code changes, check for available linters:");
stringBuilder.AppendLine("- Python: ruff, black, flake8, pylint");
stringBuilder.AppendLine("- JavaScript: eslint, prettier");
stringBuilder.AppendLine("- C#: dotnet format");
stringBuilder.AppendLine("- C++: clang-format");
stringBuilder.AppendLine("Run the appropriate linter via process tool if detected by dev_env_detect.");
if (!string.IsNullOrEmpty(currentWorkFolder))
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder11 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, stringBuilder2);
handler.AppendLiteral("\nCurrent work folder: ");
handler.AppendFormatted(currentWorkFolder);
stringBuilder11.AppendLine(ref handler);
}
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder12 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, stringBuilder2);
handler.AppendLiteral("File permission mode: ");
handler.AppendFormatted(llm.FilePermission);
stringBuilder12.AppendLine(ref handler);
stringBuilder.AppendLine("\nFolder Data Usage = ACTIVE. Use folder_map and file_read to understand the codebase.");
stringBuilder.AppendLine("Analyze project structure before making changes. Read relevant files to understand context.");
lock (_convLock)
{
string text2 = _currentConversation?.SystemCommand;
if (text2 != null && text2.Length > 0)
{
stringBuilder.AppendLine("\n" + text2);
}
}
stringBuilder.Append(LoadProjectContext(currentWorkFolder));
stringBuilder.Append(BuildProjectRulesSection(currentWorkFolder));
stringBuilder.Append(BuildMemorySection(currentWorkFolder));
stringBuilder.Append(BuildFeedbackContext());
return stringBuilder.ToString();
}
private string BuildSubAgentGuidanceSection(bool isCodeAgent)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("\n## Sub-Agent Delegation");
stringBuilder.AppendLine("Use spawn_agent only for bounded side tasks that do not block your immediate next step.");
stringBuilder.AppendLine("Good uses:");
stringBuilder.AppendLine(isCodeAgent ? "- Inspect another module, trace references, review a diff, or summarize a subsystem while you continue main-line analysis." : "- Gather evidence from several files, inspect a side topic, or summarize background material while you continue the main task.");
stringBuilder.AppendLine("- Launch multiple independent read-only investigations in parallel when they answer different questions.");
stringBuilder.AppendLine("Avoid spawn_agent when:");
stringBuilder.AppendLine("- The very next action depends on that result.");
stringBuilder.AppendLine("- The task is tiny and faster to do directly.");
stringBuilder.AppendLine("- The task requires file edits, shell commands, or user interaction.");
stringBuilder.AppendLine("Delegation rules:");
stringBuilder.AppendLine("- Give each sub-agent a concrete task and unique id.");
stringBuilder.AppendLine("- Keep the prompt self-contained: mention target files, folders, functions, or the exact question.");
stringBuilder.AppendLine("- After launching sub-agents, continue non-overlapping work locally.");
stringBuilder.AppendLine("- Use wait_agents(ids:[...]) when you need specific results now.");
stringBuilder.AppendLine("- Use wait_agents(completed_only=true) to collect finished background results without blocking.");
stringBuilder.AppendLine("- When a sub-agent returns, integrate only the relevant findings into your final answer.");
return stringBuilder.ToString();
}
private string BuildProjectRulesSection(string? workFolder)
{
if (string.IsNullOrEmpty(workFolder))
{
return "";
}
if (!_settings.Settings.Llm.EnableProjectRules)
{
return "";
}
try
{
List<ProjectRulesService.ProjectRule> list = ProjectRulesService.LoadRules(workFolder);
if (list.Count == 0)
{
return "";
}
string when = ((_activeTab == "Code") ? "always" : "always");
List<ProjectRulesService.ProjectRule> rules = ProjectRulesService.FilterRules(list, when);
return ProjectRulesService.FormatForSystemPrompt(rules);
}
catch
{
return "";
}
}
private string BuildMemorySection(string? workFolder)
{
if (!_settings.Settings.Llm.EnableAgentMemory)
{
return "";
}
AgentMemoryService agentMemoryService = ((System.Windows.Application.Current is App app) ? app.MemoryService : null);
if (agentMemoryService == null || agentMemoryService.Count == 0)
{
return "";
}
agentMemoryService.Load(workFolder ?? "");
IReadOnlyList<MemoryEntry> all = agentMemoryService.All;
if (all.Count == 0)
{
return "";
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("\n## 프로젝트 메모리 (이전 대화에서 학습한 내용)");
stringBuilder.AppendLine("아래는 이전 대화에서 학습한 규칙과 선호도입니다. 작업 시 참고하세요.");
stringBuilder.AppendLine("새로운 규칙이나 선호도를 발견하면 memory 도구의 save 액션으로 저장하세요.");
stringBuilder.AppendLine("사용자가 이전 학습 내용과 다른 지시를 하면 memory 도구의 delete 후 새로 save 하세요.\n");
foreach (IGrouping<string, MemoryEntry> item in from e in all
group e by e.Type)
{
string key = item.Key;
if (1 == 0)
{
}
string text = key switch
{
"rule" => "프로젝트 규칙",
"preference" => "사용자 선호",
"fact" => "프로젝트 사실",
"correction" => "이전 교정",
_ => item.Key,
};
if (1 == 0)
{
}
string value = text;
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(2, 1, stringBuilder2);
handler.AppendLiteral("[");
handler.AppendFormatted(value);
handler.AppendLiteral("]");
stringBuilder3.AppendLine(ref handler);
foreach (MemoryEntry item2 in item.OrderByDescending((MemoryEntry e) => e.UseCount).Take(15))
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(2, 1, stringBuilder2);
handler.AppendLiteral("- ");
handler.AppendFormatted(item2.Content);
stringBuilder4.AppendLine(ref handler);
}
stringBuilder.AppendLine();
}
return stringBuilder.ToString();
}
private void OpenWorkflowAnalyzerIfEnabled()
{
LlmSettings llm = _settings.Settings.Llm;
if (!llm.DevMode || !llm.WorkflowVisualizer)
{
return;
}
if (_analyzerWindow == null)
{
_analyzerWindow = new WorkflowAnalyzerWindow();
_analyzerWindow.Closed += delegate
{
_analyzerWindow = null;
};
foreach (ResourceDictionary mergedDictionary in System.Windows.Application.Current.Resources.MergedDictionaries)
{
_analyzerWindow.Resources.MergedDictionaries.Add(mergedDictionary);
}
_analyzerWindow.Show();
}
else if (!_analyzerWindow.IsVisible)
{
_analyzerWindow.Show();
_analyzerWindow.Activate();
}
else
{
_analyzerWindow.Reset();
_analyzerWindow.Activate();
}
_analyzerWindow.SwitchToTimelineTab();
_agentLoop.EventOccurred -= _analyzerWindow.OnAgentEvent;
_agentLoop.EventOccurred += _analyzerWindow.OnAgentEvent;
}
private void UpdateAnalyzerButtonVisibility()
{
LlmSettings llm = _settings.Settings.Llm;
BtnShowAnalyzer.Visibility = ((!llm.DevMode || !llm.WorkflowVisualizer) ? Visibility.Collapsed : Visibility.Visible);
}
private void BtnShowAnalyzer_Click(object sender, MouseButtonEventArgs e)
{
if (_analyzerWindow == null)
{
_analyzerWindow = new WorkflowAnalyzerWindow();
_analyzerWindow.Closed += delegate
{
_analyzerWindow = null;
};
foreach (ResourceDictionary mergedDictionary in System.Windows.Application.Current.Resources.MergedDictionaries)
{
_analyzerWindow.Resources.MergedDictionaries.Add(mergedDictionary);
}
_agentLoop.EventOccurred -= _analyzerWindow.OnAgentEvent;
_agentLoop.EventOccurred += _analyzerWindow.OnAgentEvent;
_analyzerWindow.Show();
}
else if (!_analyzerWindow.IsVisible)
{
_analyzerWindow.Show();
_analyzerWindow.Activate();
}
else
{
_analyzerWindow.Activate();
}
}
private void OnAgentEvent(AgentEvent evt)
{
if (_showExecutionHistory)
{
AddAgentEventBanner(evt);
}
PersistAgentEvent(evt);
AutoScrollIfNeeded();
UpdateStatusBar(evt);
if (evt.InputTokens > 0 || evt.OutputTokens > 0)
{
_agentCumulativeInputTokens += evt.InputTokens;
_agentCumulativeOutputTokens += evt.OutputTokens;
UpdateStatusTokens(_agentCumulativeInputTokens, _agentCumulativeOutputTokens);
}
UpdateAgentProgressBar(evt);
if (evt.StepCurrent > 0 && evt.StepTotal > 0)
{
UpdatePlanViewerStep(evt);
}
if (evt.Type == AgentEventType.Complete)
{
CompletePlanViewer();
}
if (evt.Success && !string.IsNullOrEmpty(evt.FilePath))
{
RefreshFileTreeIfVisible();
}
if (evt.Type == AgentEventType.ToolResult && evt.ToolName == "suggest_actions" && evt.Success)
{
RenderSuggestActionChips(evt.Summary);
}
if (!evt.Success || string.IsNullOrEmpty(evt.FilePath) || (evt.Type != AgentEventType.ToolResult && evt.Type != AgentEventType.Complete) || !WriteToolNames.Contains(evt.ToolName))
{
return;
}
string autoPreview = _settings.Settings.Llm.AutoPreview;
if (autoPreview == "auto")
{
if (PreviewWindow.IsOpen)
{
PreviewWindow.RefreshIfOpen(evt.FilePath);
}
else
{
TryShowPreview(evt.FilePath);
}
if (!PreviewWindow.IsOpen)
{
TryShowPreview(evt.FilePath);
}
}
}
private void OnSubAgentStatusChanged(SubAgentStatusEvent evt)
{
if (!((DispatcherObject)this).Dispatcher.CheckAccess())
{
((DispatcherObject)this).Dispatcher.Invoke((Action)delegate
{
OnSubAgentStatusChanged(evt);
});
return;
}
RefreshSubAgentIndicator();
SubAgentRunStatus status = evt.Status;
if (1 == 0)
{
}
AgentEvent agentEvent = status switch
{
SubAgentRunStatus.Started => new AgentEvent
{
Timestamp = evt.Timestamp,
Type = AgentEventType.Thinking,
ToolName = "sub_agent",
Summary = "Sub-agent " + evt.Id + " started\nTask: " + evt.Task
},
SubAgentRunStatus.Completed => new AgentEvent
{
Timestamp = evt.Timestamp,
Type = AgentEventType.ToolResult,
ToolName = "sub_agent",
Summary = "Sub-agent " + evt.Id + " completed\n" + (evt.Result ?? evt.Summary),
Success = true
},
_ => new AgentEvent
{
Timestamp = evt.Timestamp,
Type = AgentEventType.Error,
ToolName = "sub_agent",
Summary = "Sub-agent " + evt.Id + " failed\n" + (evt.Result ?? evt.Summary),
Success = false
},
};
if (1 == 0)
{
}
AgentEvent evt2 = agentEvent;
OnAgentEvent(evt2);
}
private void BtnToggleExecutionLog_Click(object sender, RoutedEventArgs e)
{
_showExecutionHistory = !_showExecutionHistory;
UpdateExecutionHistoryUi();
RenderMessages();
}
private void AddPlanningCard(AgentEvent evt, bool animate = true)
{
List<string> steps = evt.Steps;
Border border = new Border
{
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#F0F4FF")),
CornerRadius = new CornerRadius(10.0),
Padding = new Thickness(14.0, 10.0, 14.0, 10.0),
Margin = new Thickness(40.0, 4.0, 80.0, 4.0),
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
MaxWidth = 560.0
};
StackPanel stackPanel = new StackPanel();
StackPanel stackPanel2 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
Margin = new Thickness(0.0, 0.0, 0.0, 6.0)
};
stackPanel2.Children.Add(new TextBlock
{
Text = "\ue9d5",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 13.0,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#4B5EFC")),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 6.0, 0.0)
});
stackPanel2.Children.Add(new TextBlock
{
Text = $"작업 계획 — {steps.Count}단계",
FontSize = 12.5,
FontWeight = FontWeights.SemiBold,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#3730A3")),
VerticalAlignment = VerticalAlignment.Center
});
stackPanel.Children.Add(stackPanel2);
Grid grid = new Grid
{
Margin = new Thickness(0.0, 0.0, 0.0, 8.0)
};
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(1.0, GridUnitType.Star)
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = GridLength.Auto
});
_planProgressBar = new System.Windows.Controls.ProgressBar
{
Minimum = 0.0,
Maximum = steps.Count,
Value = 0.0,
Height = 4.0,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#4B5EFC")),
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#D0D5FF")),
VerticalAlignment = VerticalAlignment.Center
};
_planProgressBar.BorderThickness = new Thickness(0.0);
Grid.SetColumn(_planProgressBar, 0);
grid.Children.Add(_planProgressBar);
_planProgressText = new TextBlock
{
Text = "0%",
FontSize = 10.5,
FontWeight = FontWeights.SemiBold,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#4B5EFC")),
Margin = new Thickness(8.0, 0.0, 0.0, 0.0),
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(_planProgressText, 1);
grid.Children.Add(_planProgressText);
stackPanel.Children.Add(grid);
_planStepsPanel = new StackPanel();
for (int i = 0; i < steps.Count; i++)
{
StackPanel stackPanel3 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
Margin = new Thickness(0.0, 1.0, 0.0, 1.0),
Tag = i
};
stackPanel3.Children.Add(new TextBlock
{
Text = "○",
FontSize = 11.0,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#9CA3AF")),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 6.0, 0.0),
Tag = "status"
});
stackPanel3.Children.Add(new TextBlock
{
Text = $"{i + 1}. {steps[i]}",
FontSize = 11.5,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#4B5563")),
TextWrapping = TextWrapping.Wrap,
MaxWidth = 480.0,
VerticalAlignment = VerticalAlignment.Center
});
_planStepsPanel.Children.Add(stackPanel3);
}
stackPanel.Children.Add(_planStepsPanel);
border.Child = stackPanel;
_planningCard = border;
if (animate)
{
border.Opacity = 0.0;
border.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0.0, 1.0, TimeSpan.FromMilliseconds(300.0)));
}
MessagePanel.Children.Add(border);
}
private void AddDecisionButtons(TaskCompletionSource<string?> tcs, List<string> options)
{
System.Windows.Media.Brush accentBrush = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
System.Windows.Media.Color color = ((SolidColorBrush)accentBrush).Color;
System.Windows.Media.Brush foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
Border border = new Border
{
Margin = new Thickness(40.0, 2.0, 80.0, 6.0),
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
MaxWidth = 560.0
};
StackPanel outerStack = new StackPanel();
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
Margin = new Thickness(0.0, 0.0, 0.0, 0.0)
};
Border border2 = new Border
{
Background = accentBrush,
CornerRadius = new CornerRadius(16.0),
Padding = new Thickness(16.0, 7.0, 16.0, 7.0),
Margin = new Thickness(0.0, 0.0, 8.0, 0.0),
Cursor = System.Windows.Input.Cursors.Hand
};
StackPanel stackPanel2 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel2.Children.Add(new TextBlock
{
Text = "\ue73e",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 11.0,
Foreground = System.Windows.Media.Brushes.White,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 5.0, 0.0)
});
stackPanel2.Children.Add(new TextBlock
{
Text = "승인",
FontSize = 12.5,
FontWeight = FontWeights.SemiBold,
Foreground = System.Windows.Media.Brushes.White
});
border2.Child = stackPanel2;
ApplyMenuItemHover(border2);
border2.MouseLeftButtonUp += delegate
{
CollapseDecisionButtons(outerStack, "✓ 승인됨", accentBrush);
tcs.TrySetResult(null);
};
stackPanel.Children.Add(border2);
Border border3 = new Border
{
Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, color.R, color.G, color.B)),
CornerRadius = new CornerRadius(16.0),
Padding = new Thickness(14.0, 7.0, 14.0, 7.0),
Margin = new Thickness(0.0, 0.0, 8.0, 0.0),
Cursor = System.Windows.Input.Cursors.Hand,
BorderBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(64, color.R, color.G, color.B)),
BorderThickness = new Thickness(1.0)
};
StackPanel stackPanel3 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel3.Children.Add(new TextBlock
{
Text = "\ue70f",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 11.0,
Foreground = accentBrush,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 5.0, 0.0)
});
stackPanel3.Children.Add(new TextBlock
{
Text = "수정 요청",
FontSize = 12.5,
FontWeight = FontWeights.SemiBold,
Foreground = accentBrush
});
border3.Child = stackPanel3;
ApplyMenuItemHover(border3);
Border editInputPanel = new Border
{
Visibility = Visibility.Collapsed,
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#F8F9FC")),
CornerRadius = new CornerRadius(10.0),
Padding = new Thickness(10.0, 8.0, 10.0, 8.0),
Margin = new Thickness(0.0, 8.0, 0.0, 0.0)
};
StackPanel stackPanel4 = new StackPanel();
stackPanel4.Children.Add(new TextBlock
{
Text = "수정 사항을 입력하세요:",
FontSize = 11.5,
Foreground = foreground,
Margin = new Thickness(0.0, 0.0, 0.0, 6.0)
});
System.Windows.Controls.TextBox editTextBox = new System.Windows.Controls.TextBox
{
MinHeight = 36.0,
MaxHeight = 100.0,
AcceptsReturn = true,
TextWrapping = TextWrapping.Wrap,
FontSize = 12.5,
Background = System.Windows.Media.Brushes.White,
BorderBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(64, color.R, color.G, color.B)),
BorderThickness = new Thickness(1.0),
Padding = new Thickness(8.0, 6.0, 8.0, 6.0)
};
stackPanel4.Children.Add(editTextBox);
Border border4 = new Border
{
Background = accentBrush,
CornerRadius = new CornerRadius(8.0),
Padding = new Thickness(12.0, 5.0, 12.0, 5.0),
Margin = new Thickness(0.0, 6.0, 0.0, 0.0),
Cursor = System.Windows.Input.Cursors.Hand,
HorizontalAlignment = System.Windows.HorizontalAlignment.Right
};
border4.Child = new TextBlock
{
Text = "전송",
FontSize = 12.0,
FontWeight = FontWeights.SemiBold,
Foreground = System.Windows.Media.Brushes.White
};
ApplyHoverScaleAnimation(border4, 1.05);
border4.MouseLeftButtonUp += delegate
{
string text = editTextBox.Text.Trim();
if (!string.IsNullOrEmpty(text))
{
CollapseDecisionButtons(outerStack, "✎ 수정 요청됨", accentBrush);
tcs.TrySetResult(text);
}
};
stackPanel4.Children.Add(border4);
editInputPanel.Child = stackPanel4;
border3.MouseLeftButtonUp += delegate
{
editInputPanel.Visibility = ((editInputPanel.Visibility == Visibility.Visible) ? Visibility.Collapsed : Visibility.Visible);
if (editInputPanel.Visibility == Visibility.Visible)
{
editTextBox.Focus();
}
};
stackPanel.Children.Add(border3);
Border border5 = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(16.0),
Padding = new Thickness(14.0, 7.0, 14.0, 7.0),
Cursor = System.Windows.Input.Cursors.Hand,
BorderBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(48, 220, 38, 38)),
BorderThickness = new Thickness(1.0)
};
StackPanel stackPanel5 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel5.Children.Add(new TextBlock
{
Text = "\ue711",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 11.0,
Foreground = new SolidColorBrush(System.Windows.Media.Color.FromRgb(220, 38, 38)),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 5.0, 0.0)
});
stackPanel5.Children.Add(new TextBlock
{
Text = "취소",
FontSize = 12.5,
FontWeight = FontWeights.SemiBold,
Foreground = new SolidColorBrush(System.Windows.Media.Color.FromRgb(220, 38, 38))
});
border5.Child = stackPanel5;
ApplyMenuItemHover(border5);
border5.MouseLeftButtonUp += delegate
{
CollapseDecisionButtons(outerStack, "✕ 취소됨", new SolidColorBrush(System.Windows.Media.Color.FromRgb(220, 38, 38)));
tcs.TrySetResult("취소");
};
stackPanel.Children.Add(border5);
outerStack.Children.Add(stackPanel);
outerStack.Children.Add(editInputPanel);
border.Child = outerStack;
ApplyMessageEntryAnimation(border);
MessagePanel.Children.Add(border);
ForceScrollToEnd();
StackPanel capturedOuterStack = outerStack;
System.Windows.Media.Brush capturedAccent = accentBrush;
tcs.Task.ContinueWith(delegate(Task<string?> t)
{
((DispatcherObject)this).Dispatcher.BeginInvoke((Delegate)(Action)delegate
{
if (capturedOuterStack.Children.Count > 1)
{
string resultText = ((t.Result == null) ? "✓ 승인됨" : ((t.Result == "취소") ? "✕ 취소됨" : "✎ 수정 요청됨"));
System.Windows.Media.Brush fg = ((t.Result == "취소") ? new SolidColorBrush(System.Windows.Media.Color.FromRgb(220, 38, 38)) : capturedAccent);
CollapseDecisionButtons(capturedOuterStack, resultText, fg);
}
}, Array.Empty<object>());
}, TaskScheduler.Default);
}
private void CollapseDecisionButtons(StackPanel outerStack, string resultText, System.Windows.Media.Brush fg)
{
outerStack.Children.Clear();
TextBlock element = new TextBlock
{
Text = resultText,
FontSize = 12.0,
FontWeight = FontWeights.SemiBold,
Foreground = fg,
Opacity = 0.8,
Margin = new Thickness(0.0, 2.0, 0.0, 2.0)
};
outerStack.Children.Add(element);
}
private Func<string, List<string>, Task<string?>> CreatePlanDecisionCallback()
{
return async delegate(string planSummary, List<string> options)
{
TaskCompletionSource<string?> tcs = new TaskCompletionSource<string>();
List<string> steps = TaskDecomposer.ExtractSteps(planSummary);
await ((DispatcherObject)this).Dispatcher.InvokeAsync((Action)delegate
{
if (_planViewerWindow == null || !IsWindowAlive(_planViewerWindow))
{
_planViewerWindow = new PlanViewerWindow();
_planViewerWindow.Closing += delegate(object? _, CancelEventArgs e)
{
e.Cancel = true;
_planViewerWindow.Hide();
};
}
_planViewerWindow.ShowPlanAsync(planSummary, steps, tcs);
AddDecisionButtons(tcs, options);
ShowPlanButton(show: true);
});
if (await Task.WhenAny(tcs.Task, Task.Delay(TimeSpan.FromMinutes(5.0))) != tcs.Task)
{
await ((DispatcherObject)this).Dispatcher.InvokeAsync((Action)delegate
{
_planViewerWindow?.Hide();
});
return "취소";
}
string result = await tcs.Task;
if (result != null)
{
await ((DispatcherObject)this).Dispatcher.InvokeAsync((Action)delegate
{
_planViewerWindow?.Hide();
});
}
else
{
await ((DispatcherObject)this).Dispatcher.InvokeAsync((Action)delegate
{
_planViewerWindow?.SwitchToExecutionMode();
_planViewerWindow?.Hide();
});
}
return result;
};
}
private void ShowPlanButton(bool show)
{
if (!show)
{
for (int num = MoodIconPanel.Children.Count - 1; num >= 0; num--)
{
if (MoodIconPanel.Children[num] is Border { Tag: var tag } && tag?.ToString() == "PlanBtn")
{
if (num > 0 && MoodIconPanel.Children[num - 1] is Border { Tag: var tag2 } && tag2?.ToString() == "PlanSep")
{
MoodIconPanel.Children.RemoveAt(num - 1);
}
if (num < MoodIconPanel.Children.Count)
{
MoodIconPanel.Children.RemoveAt(Math.Min(num, MoodIconPanel.Children.Count - 1));
}
break;
}
}
return;
}
foreach (object child in MoodIconPanel.Children)
{
if (child is Border { Tag: var tag3 } && tag3?.ToString() == "PlanBtn")
{
return;
}
}
Border border4 = new Border();
border4.Width = 1.0;
border4.Height = 18.0;
border4.Background = (TryFindResource("SeparatorColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
border4.Margin = new Thickness(4.0, 0.0, 4.0, 0.0);
border4.VerticalAlignment = VerticalAlignment.Center;
border4.Tag = "PlanSep";
Border element = border4;
MoodIconPanel.Children.Add(element);
Border border5 = CreateFolderBarButton("\ue9d2", "계획", "실행 계획 보기", "#10B981");
border5.Tag = "PlanBtn";
border5.MouseLeftButtonUp += delegate(object _, MouseButtonEventArgs e)
{
e.Handled = true;
if (_planViewerWindow != null && IsWindowAlive(_planViewerWindow))
{
_planViewerWindow.Show();
_planViewerWindow.Activate();
}
};
MoodIconPanel.Children.Add(border5);
}
private void UpdatePlanViewerStep(AgentEvent evt)
{
if (_planViewerWindow != null && IsWindowAlive(_planViewerWindow) && evt.StepCurrent > 0)
{
_planViewerWindow.UpdateCurrentStep(evt.StepCurrent - 1);
}
}
private void CompletePlanViewer()
{
if (_planViewerWindow != null && IsWindowAlive(_planViewerWindow))
{
_planViewerWindow.MarkComplete();
}
ShowPlanButton(show: false);
}
private static bool IsWindowAlive(Window? w)
{
if (w == null)
{
return false;
}
try
{
bool isVisible = w.IsVisible;
return true;
}
catch
{
return false;
}
}
private void RenderSuggestActionChips(string jsonSummary)
{
List<(string, string)> list = new List<(string, string)>();
try
{
if (jsonSummary.Contains("\"label\""))
{
using JsonDocument jsonDocument = JsonDocument.Parse(jsonSummary);
if (jsonDocument.RootElement.ValueKind == JsonValueKind.Array)
{
foreach (JsonElement item4 in jsonDocument.RootElement.EnumerateArray())
{
JsonElement value;
string text = (item4.TryGetProperty("label", out value) ? (value.GetString() ?? "") : "");
JsonElement value2;
string item = (item4.TryGetProperty("command", out value2) ? (value2.GetString() ?? text) : text);
if (!string.IsNullOrEmpty(text))
{
list.Add((text, item));
}
}
}
}
else
{
string[] array = jsonSummary.Split('\n');
foreach (string text2 in array)
{
string text3 = text2.Trim().TrimStart('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', ' ');
if (!string.IsNullOrEmpty(text3))
{
string[] array2 = text3.Split('→', ':', '—');
if (array2.Length >= 2)
{
list.Add((array2[0].Trim(), array2[1].Trim()));
}
else if (!string.IsNullOrEmpty(text3))
{
list.Add((text3, text3));
}
}
}
}
}
catch
{
return;
}
if (list.Count == 0)
{
return;
}
System.Windows.Media.Brush brush = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
System.Windows.Media.Brush foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush brush2 = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue));
Border container = new Border
{
Margin = new Thickness(40.0, 4.0, 40.0, 8.0),
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch
};
StackPanel stackPanel = new StackPanel
{
Margin = new Thickness(0.0, 0.0, 0.0, 6.0)
};
stackPanel.Children.Add(new TextBlock
{
Text = "\ud83d\udca1 다음 작업 제안:",
FontSize = 12.0,
Foreground = foreground
});
WrapPanel wrapPanel = new WrapPanel
{
Margin = new Thickness(0.0, 2.0, 0.0, 0.0)
};
foreach (var item5 in list.Take(5))
{
string item2 = item5.Item1;
string item3 = item5.Item2;
string capturedCmd = item3;
Border border = new Border
{
CornerRadius = new CornerRadius(16.0),
Padding = new Thickness(14.0, 7.0, 14.0, 7.0),
Margin = new Thickness(0.0, 0.0, 8.0, 6.0),
Cursor = System.Windows.Input.Cursors.Hand,
Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(21, ((SolidColorBrush)brush).Color.R, ((SolidColorBrush)brush).Color.G, ((SolidColorBrush)brush).Color.B)),
BorderBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(64, ((SolidColorBrush)brush).Color.R, ((SolidColorBrush)brush).Color.G, ((SolidColorBrush)brush).Color.B)),
BorderThickness = new Thickness(1.0)
};
border.Child = new TextBlock
{
Text = item2,
FontSize = 12.5,
Foreground = brush,
FontWeight = FontWeights.SemiBold
};
border.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
((Border)s).Opacity = 0.8;
};
border.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
((Border)s).Opacity = 1.0;
};
border.MouseLeftButtonUp += delegate
{
MessagePanel.Children.Remove(container);
if (capturedCmd.StartsWith("/"))
{
InputBox.Text = capturedCmd + " ";
InputBox.CaretIndex = InputBox.Text.Length;
InputBox.Focus();
}
else
{
InputBox.Text = capturedCmd;
SendMessageAsync();
}
};
wrapPanel.Children.Add(border);
}
StackPanel stackPanel2 = new StackPanel();
stackPanel2.Children.Add(stackPanel);
stackPanel2.Children.Add(wrapPanel);
container.Child = stackPanel2;
ApplyMessageEntryAnimation(container);
MessagePanel.Children.Add(container);
ForceScrollToEnd();
}
private string BuildFeedbackContext()
{
try
{
List<ChatConversation> list = (from m in _storage.LoadAllMeta()
orderby m.UpdatedAt descending
select m).Take(20).ToList();
List<string> list2 = new List<string>();
List<string> list3 = new List<string>();
foreach (ChatConversation item2 in list)
{
ChatConversation chatConversation = _storage.Load(item2.Id);
if (chatConversation == null)
{
continue;
}
foreach (ChatMessage item3 in chatConversation.Messages.Where((ChatMessage m) => m.Role == "assistant" && m.Feedback != null))
{
string content = item3.Content;
string item = ((content != null && content.Length > 80) ? item3.Content.Substring(0, 80) : (item3.Content ?? ""));
if (item3.Feedback == "like")
{
list2.Add(item);
}
else if (item3.Feedback == "dislike")
{
list3.Add(item);
}
}
}
if (list2.Count == 0 && list3.Count == 0)
{
return "";
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("\n[사용자 선호도 참고]");
if (list2.Count > 0)
{
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(20, 1, stringBuilder2);
handler.AppendLiteral("사용자가 좋아한 응답 스타일 (");
handler.AppendFormatted(list2.Count);
handler.AppendLiteral("건):");
stringBuilder3.AppendLine(ref handler);
foreach (string item4 in list2.Take(5))
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
handler.AppendLiteral(" - \"");
handler.AppendFormatted(item4);
handler.AppendLiteral("...\"");
stringBuilder4.AppendLine(ref handler);
}
}
if (list3.Count > 0)
{
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(20, 1, stringBuilder2);
handler.AppendLiteral("사용자가 싫어한 응답 스타일 (");
handler.AppendFormatted(list3.Count);
handler.AppendLiteral("건):");
stringBuilder5.AppendLine(ref handler);
foreach (string item5 in list3.Take(5))
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder6 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
handler.AppendLiteral(" - \"");
handler.AppendFormatted(item5);
handler.AppendLiteral("...\"");
stringBuilder6.AppendLine(ref handler);
}
}
stringBuilder.AppendLine("위 선호도를 참고하여 응답 스타일을 조정하세요.");
return stringBuilder.ToString();
}
catch
{
return "";
}
}
private void UpdateProgressBar(AgentEvent evt)
{
if (_planProgressBar == null || _planStepsPanel == null || _planProgressText == null)
{
return;
}
int num = evt.StepCurrent - 1;
int stepTotal = evt.StepTotal;
_planProgressBar.Value = evt.StepCurrent;
int value = (int)((double)evt.StepCurrent / (double)stepTotal * 100.0);
_planProgressText.Text = $"{value}%";
for (int i = 0; i < _planStepsPanel.Children.Count; i++)
{
if (!(_planStepsPanel.Children[i] is StackPanel stackPanel) || stackPanel.Children.Count < 2)
{
continue;
}
TextBlock textBlock = stackPanel.Children[0] as TextBlock;
TextBlock textBlock2 = stackPanel.Children[1] as TextBlock;
if (textBlock != null && textBlock2 != null)
{
if (i < num)
{
textBlock.Text = "●";
textBlock.Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#16A34A"));
textBlock2.Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#6B7280"));
}
else if (i == num)
{
textBlock.Text = "◉";
textBlock.Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#4B5EFC"));
textBlock2.Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#1E293B"));
textBlock2.FontWeight = FontWeights.SemiBold;
}
else
{
textBlock.Text = "○";
textBlock.Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#9CA3AF"));
textBlock2.Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#4B5563"));
textBlock2.FontWeight = FontWeights.Normal;
}
}
}
}
private static UIElement BuildDiffView(string text)
{
StackPanel stackPanel = new StackPanel
{
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#FAFAFA")),
MaxWidth = 520.0
};
bool flag = false;
string[] array = text.Split('\n');
foreach (string text2 in array)
{
string text3 = text2.TrimEnd('\r');
if (!flag && !text3.StartsWith("--- "))
{
stackPanel.Children.Add(new TextBlock
{
Text = text3,
FontSize = 11.0,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#4B5563")),
FontFamily = new System.Windows.Media.FontFamily("Consolas"),
Margin = new Thickness(0.0, 0.0, 0.0, 1.0)
});
continue;
}
flag = true;
string text4;
string value;
if (text3.StartsWith("---") || text3.StartsWith("+++"))
{
text4 = "#F3F4F6";
value = "#374151";
}
else if (text3.StartsWith("@@"))
{
text4 = "#EFF6FF";
value = "#3B82F6";
}
else if (text3.StartsWith("+"))
{
text4 = "#ECFDF5";
value = "#059669";
}
else if (text3.StartsWith("-"))
{
text4 = "#FEF2F2";
value = "#DC2626";
}
else
{
text4 = "Transparent";
value = "#6B7280";
}
TextBlock textBlock = new TextBlock
{
Text = text3,
FontSize = 10.5,
FontFamily = new System.Windows.Media.FontFamily("Consolas"),
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(value)),
Padding = new Thickness(4.0, 1.0, 4.0, 1.0)
};
if (text4 != "Transparent")
{
textBlock.Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(text4));
}
stackPanel.Children.Add(textBlock);
}
return stackPanel;
}
private void AddAgentEventBanner(AgentEvent evt, bool animate = true)
{
string agentLogLevel = _settings.Settings.Llm.AgentLogLevel;
if (evt.Type == AgentEventType.Planning)
{
List<string> steps = evt.Steps;
if (steps != null && steps.Count > 0)
{
AddPlanningCard(evt, animate);
return;
}
}
if (evt.Type == AgentEventType.StepStart && evt.StepTotal > 0)
{
UpdateProgressBar(evt);
}
else
{
if (agentLogLevel == "simple" && evt.Type == AgentEventType.ToolCall)
{
return;
}
bool flag = evt.Type == AgentEventType.StepDone && evt.ToolName == "total_stats";
string text;
string text2;
string value;
string value2;
if (flag)
{
text = "\ue9d2";
text2 = "Total Stats";
value = "#F3EEFF";
value2 = "#7C3AED";
(string, string, string, string) tuple = ("\ue9d2", "Total Stats", "#F3EEFF", "#7C3AED");
}
else
{
AgentEventType type = evt.Type;
if (1 == 0)
{
}
(string, string, string, string) tuple2 = type switch
{
AgentEventType.Thinking => ("\ue8bd", "Thinking", "#F0F0FF", "#6B7BC4"),
AgentEventType.ToolCall => ("\ue8a7", evt.ToolName, "#EEF6FF", "#3B82F6"),
AgentEventType.ToolResult => ("\ue73e", evt.ToolName, "#EEF9EE", "#16A34A"),
AgentEventType.SkillCall => ("\ue8a5", evt.ToolName, "#FFF7ED", "#EA580C"),
AgentEventType.Error => ("\ue783", "Error", "#FEF2F2", "#DC2626"),
AgentEventType.Complete => ("\ue930", "Complete", "#F0FFF4", "#15803D"),
AgentEventType.StepDone => ("\ue73e", "Step Done", "#EEF9EE", "#16A34A"),
AgentEventType.Paused => ("\ue769", "Paused", "#FFFBEB", "#D97706"),
AgentEventType.Resumed => ("\ue768", "Resumed", "#ECFDF5", "#059669"),
_ => ("\ue946", "Agent", "#F5F5F5", "#6B7280"),
};
if (1 == 0)
{
}
(string, string, string, string) tuple3 = tuple2;
text = tuple3.Item1;
text2 = tuple3.Item2;
value = tuple3.Item3;
value2 = tuple3.Item4;
(string, string, string, string) tuple = (tuple3.Item1, tuple3.Item2, tuple3.Item3, tuple3.Item4);
}
Border border = new Border
{
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(value)),
BorderBrush = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(value2)),
BorderThickness = new Thickness(1.0),
CornerRadius = new CornerRadius(16.0),
Padding = new Thickness(14.0, 10.0, 14.0, 10.0),
Margin = new Thickness(48.0, 4.0, 120.0, 4.0),
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
MaxWidth = GetMessageMaxWidth(),
Effect = new DropShadowEffect
{
BlurRadius = 12.0,
ShadowDepth = 0.0,
Opacity = 0.05
}
};
StackPanel stackPanel = new StackPanel();
Grid grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(1.0, GridUnitType.Star)
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = GridLength.Auto
});
StackPanel stackPanel2 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel2.Children.Add(new TextBlock
{
Text = text,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 11.0,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(value2)),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 6.0, 0.0)
});
stackPanel2.Children.Add(new TextBlock
{
Text = text2,
FontSize = 11.5,
FontWeight = FontWeights.SemiBold,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(value2)),
VerticalAlignment = VerticalAlignment.Center
});
Grid.SetColumn(stackPanel2, 0);
StackPanel stackPanel3 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
if (agentLogLevel != "simple" && evt.ElapsedMs > 0)
{
stackPanel3.Children.Add(new TextBlock
{
Text = ((evt.ElapsedMs < 1000) ? $"{evt.ElapsedMs}ms" : $"{(double)evt.ElapsedMs / 1000.0:F1}s"),
FontSize = 10.0,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#9CA3AF")),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(8.0, 0.0, 0.0, 0.0)
});
}
if (agentLogLevel != "simple" && (evt.InputTokens > 0 || evt.OutputTokens > 0))
{
string text3 = ((evt.InputTokens > 0 && evt.OutputTokens > 0) ? $"{evt.InputTokens}→{evt.OutputTokens}t" : ((evt.InputTokens > 0) ? $"↑{evt.InputTokens}t" : $"↓{evt.OutputTokens}t"));
stackPanel3.Children.Add(new Border
{
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#F0F0F5")),
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 = text3,
FontSize = 9.5,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#8B8FA3")),
FontFamily = new System.Windows.Media.FontFamily("Consolas")
}
});
}
Grid.SetColumn(stackPanel3, 1);
grid.Children.Add(stackPanel2);
grid.Children.Add(stackPanel3);
StackPanel stackPanel4 = stackPanel2;
stackPanel.Children.Add(grid);
if (agentLogLevel == "simple")
{
if (!string.IsNullOrEmpty(evt.Summary))
{
string text4 = ((evt.Summary.Length > 100) ? (evt.Summary.Substring(0, 100) + "…") : evt.Summary);
stackPanel.Children.Add(new TextBlock
{
Text = text4,
FontSize = 11.0,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#6B7280")),
TextWrapping = TextWrapping.NoWrap,
TextTrimming = TextTrimming.CharacterEllipsis,
Margin = new Thickness(0.0, 2.0, 0.0, 0.0)
});
}
}
else if (!string.IsNullOrEmpty(evt.Summary))
{
string summary = evt.Summary;
if ((evt.Type == AgentEventType.ToolCall || evt.Type == AgentEventType.ToolResult) && summary.Length > 60)
{
string text5 = ((summary.Length > 80) ? (summary.Substring(0, 80) + "...") : summary);
TextBlock summaryTb = new TextBlock
{
Text = text5,
FontSize = 11.5,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#4B5563")),
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(0.0, 3.0, 0.0, 0.0),
Cursor = System.Windows.Input.Cursors.Hand
};
UIElement fullContent;
if (summary.Contains("--- ") && summary.Contains("+++ "))
{
fullContent = BuildDiffView(summary);
}
else
{
fullContent = new TextBlock
{
Text = summary,
FontSize = 11.0,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#6B7280")),
TextWrapping = TextWrapping.Wrap,
FontFamily = new System.Windows.Media.FontFamily("Consolas")
};
}
fullContent.Visibility = Visibility.Collapsed;
((FrameworkElement)fullContent).Margin = new Thickness(0.0, 4.0, 0.0, 0.0);
TextBlock expandIcon = new TextBlock
{
Text = "\ue70d",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 9.0,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#9CA3AF")),
Margin = new Thickness(6.0, 0.0, 0.0, 0.0),
VerticalAlignment = VerticalAlignment.Center
};
stackPanel4.Children.Add(expandIcon);
bool isExpanded = false;
border.MouseLeftButtonDown += delegate
{
isExpanded = !isExpanded;
fullContent.Visibility = ((!isExpanded) ? Visibility.Collapsed : Visibility.Visible);
summaryTb.Visibility = (isExpanded ? Visibility.Collapsed : Visibility.Visible);
expandIcon.Text = (isExpanded ? "\ue70e" : "\ue70d");
};
stackPanel.Children.Add(summaryTb);
stackPanel.Children.Add(fullContent);
}
else
{
stackPanel.Children.Add(new TextBlock
{
Text = summary,
FontSize = 11.5,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#4B5563")),
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(0.0, 3.0, 0.0, 0.0)
});
}
}
if (agentLogLevel == "debug" && !string.IsNullOrEmpty(evt.ToolInput))
{
stackPanel.Children.Add(new Border
{
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#F8F8FC")),
CornerRadius = new CornerRadius(4.0),
Padding = new Thickness(8.0, 4.0, 8.0, 4.0),
Margin = new Thickness(0.0, 4.0, 0.0, 0.0),
Child = new TextBlock
{
Text = ((evt.ToolInput.Length > 500) ? (evt.ToolInput.Substring(0, 500) + "…") : evt.ToolInput),
FontSize = 10.0,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#7C7F93")),
FontFamily = new System.Windows.Media.FontFamily("Consolas"),
TextWrapping = TextWrapping.Wrap
}
});
}
if (!string.IsNullOrEmpty(evt.FilePath))
{
Border border2 = new Border
{
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#F8FAFC")),
CornerRadius = new CornerRadius(4.0),
Padding = new Thickness(8.0, 4.0, 8.0, 4.0),
Margin = new Thickness(0.0, 4.0, 0.0, 0.0)
};
StackPanel stackPanel5 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel5.Children.Add(new TextBlock
{
Text = "\ue8b7",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 10.0,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#9CA3AF")),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 4.0, 0.0)
});
stackPanel5.Children.Add(new TextBlock
{
Text = evt.FilePath,
FontSize = 10.5,
Foreground = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#6B7280")),
FontFamily = new System.Windows.Media.FontFamily("Consolas"),
VerticalAlignment = VerticalAlignment.Center,
TextTrimming = TextTrimming.CharacterEllipsis
});
StackPanel element = BuildFileQuickActions(evt.FilePath);
stackPanel5.Children.Add(element);
border2.Child = stackPanel5;
stackPanel.Children.Add(border2);
}
border.Child = stackPanel;
if (flag)
{
border.Cursor = System.Windows.Input.Cursors.Hand;
border.ToolTip = "클릭하여 병목 분석 보기";
border.MouseLeftButtonUp += delegate
{
OpenWorkflowAnalyzerIfEnabled();
_analyzerWindow?.SwitchToBottleneckTab();
_analyzerWindow?.Activate();
};
}
if (animate)
{
border.Opacity = 0.0;
border.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0.0, 1.0, TimeSpan.FromMilliseconds(200.0)));
}
MessagePanel.Children.Add(border);
}
}
private StackPanel BuildFileQuickActions(string filePath)
{
StackPanel panel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
Margin = new Thickness(6.0, 0.0, 0.0, 0.0),
VerticalAlignment = VerticalAlignment.Center
};
System.Windows.Media.Color color = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#3B82F6");
SolidColorBrush accentBrush = new SolidColorBrush(color);
string item = System.IO.Path.GetExtension(filePath).ToLowerInvariant();
if (_previewableExtensions.Contains(item))
{
string path1 = filePath;
panel.Children.Add(MakeBtn("\ue8a1", "프리뷰", delegate
{
ShowPreviewPanel(path1);
}));
}
string path2 = filePath;
panel.Children.Add(MakeBtn("\ue8a7", "열기", delegate
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = path2,
UseShellExecute = true
});
}
catch
{
}
}));
string path3 = filePath;
panel.Children.Add(MakeBtn("\ued25", "폴더", delegate
{
try
{
Process.Start("explorer.exe", "/select,\"" + path3 + "\"");
}
catch
{
}
}));
string path4 = filePath;
panel.Children.Add(MakeBtn("\ue8c8", "복사", delegate
{
//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Expected O, but got Unknown
try
{
System.Windows.Clipboard.SetText(path4);
UIElementCollection children = panel.Children;
if (children[children.Count - 1] is Border { Child: StackPanel child })
{
TextBlock origLabel = child.Children.OfType<TextBlock>().LastOrDefault();
if (origLabel != null)
{
string prev = origLabel.Text;
origLabel.Text = "복사됨 ✓";
DispatcherTimer timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(1500.0)
};
timer.Tick += delegate
{
origLabel.Text = prev;
timer.Stop();
};
timer.Start();
}
}
}
catch
{
}
}));
return panel;
Border MakeBtn(string mdlIcon, string label, Action action)
{
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(new TextBlock
{
Text = mdlIcon,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 9.0,
Foreground = accentBrush,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 3.0, 0.0)
});
stackPanel.Children.Add(new TextBlock
{
Text = label,
FontSize = 10.0,
Foreground = accentBrush,
VerticalAlignment = VerticalAlignment.Center
});
Border border = new Border
{
Child = stackPanel,
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(4.0),
Padding = new Thickness(5.0, 2.0, 5.0, 2.0),
Cursor = System.Windows.Input.Cursors.Hand
};
border.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(21, 59, 130, 246));
}
};
border.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = System.Windows.Media.Brushes.Transparent;
}
};
border.MouseLeftButtonUp += delegate
{
action();
};
return border;
}
}
private async Task RegenerateLastAsync()
{
if (_isStreaming)
{
return;
}
ChatConversation conv;
lock (_convLock)
{
if (_currentConversation == null)
{
return;
}
conv = _currentConversation;
}
lock (_convLock)
{
int num;
if (conv.Messages.Count > 0)
{
List<ChatMessage> messages = conv.Messages;
num = ((messages[messages.Count - 1].Role == "assistant") ? 1 : 0);
}
else
{
num = 0;
}
if (num != 0)
{
conv.Messages.RemoveAt(conv.Messages.Count - 1);
}
}
if (MessagePanel.Children.Count > 0)
{
MessagePanel.Children.RemoveAt(MessagePanel.Children.Count - 1);
}
await SendRegenerateAsync(conv);
}
private void ShowRetryWithFeedbackInput()
{
if (_isStreaming)
{
return;
}
System.Windows.Media.Brush background = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
System.Windows.Media.Brush background2 = (TryFindResource("ItemBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromRgb(42, 43, 64));
System.Windows.Media.Brush brush = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush borderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
Border container = new Border
{
Margin = new Thickness(40.0, 4.0, 40.0, 8.0),
Padding = new Thickness(14.0, 10.0, 14.0, 10.0),
CornerRadius = new CornerRadius(12.0),
Background = background2,
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch
};
StackPanel stackPanel = new StackPanel();
stackPanel.Children.Add(new TextBlock
{
Text = "어떻게 수정하면 좋을지 알려주세요:",
FontSize = 12.0,
Foreground = foreground,
Margin = new Thickness(0.0, 0.0, 0.0, 6.0)
});
System.Windows.Controls.TextBox textBox = new System.Windows.Controls.TextBox
{
MinHeight = 38.0,
MaxHeight = 80.0,
AcceptsReturn = true,
TextWrapping = TextWrapping.Wrap,
FontSize = 13.0,
Background = ((TryFindResource("LauncherBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Black),
Foreground = brush,
CaretBrush = brush,
BorderBrush = borderBrush,
BorderThickness = new Thickness(1.0),
Padding = new Thickness(10.0, 6.0, 10.0, 6.0)
};
stackPanel.Children.Add(textBox);
StackPanel stackPanel2 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
Margin = new Thickness(0.0, 8.0, 0.0, 0.0)
};
Border border = new Border
{
Background = background,
CornerRadius = new CornerRadius(8.0),
Padding = new Thickness(14.0, 6.0, 14.0, 6.0),
Cursor = System.Windows.Input.Cursors.Hand,
Margin = new Thickness(6.0, 0.0, 0.0, 0.0)
};
border.Child = new TextBlock
{
Text = "재시도",
FontSize = 12.0,
FontWeight = FontWeights.SemiBold,
Foreground = System.Windows.Media.Brushes.White
};
border.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
((Border)s).Opacity = 0.85;
};
border.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
((Border)s).Opacity = 1.0;
};
border.MouseLeftButtonUp += delegate
{
string text = textBox.Text.Trim();
if (!string.IsNullOrEmpty(text))
{
MessagePanel.Children.Remove(container);
RetryWithFeedbackAsync(text);
}
};
Border border2 = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(8.0),
Padding = new Thickness(12.0, 6.0, 12.0, 6.0),
Cursor = System.Windows.Input.Cursors.Hand
};
border2.Child = new TextBlock
{
Text = "취소",
FontSize = 12.0,
Foreground = foreground
};
border2.MouseLeftButtonUp += delegate
{
MessagePanel.Children.Remove(container);
};
stackPanel2.Children.Add(border2);
stackPanel2.Children.Add(border);
stackPanel.Children.Add(stackPanel2);
container.Child = stackPanel;
ApplyMessageEntryAnimation(container);
MessagePanel.Children.Add(container);
ForceScrollToEnd();
textBox.Focus();
}
private async Task RetryWithFeedbackAsync(string feedback)
{
if (_isStreaming)
{
return;
}
ChatConversation conv;
lock (_convLock)
{
if (_currentConversation == null)
{
return;
}
conv = _currentConversation;
}
lock (_convLock)
{
int num;
if (conv.Messages.Count > 0)
{
List<ChatMessage> messages = conv.Messages;
num = ((messages[messages.Count - 1].Role == "assistant") ? 1 : 0);
}
else
{
num = 0;
}
if (num != 0)
{
conv.Messages.RemoveAt(conv.Messages.Count - 1);
}
}
if (MessagePanel.Children.Count > 0)
{
MessagePanel.Children.RemoveAt(MessagePanel.Children.Count - 1);
}
ChatMessage feedbackMsg = new ChatMessage
{
Role = "user",
Content = "[이전 응답에 대한 수정 요청] " + feedback + "\n\n위 피드백을 반영하여 다시 작성해주세요."
};
lock (_convLock)
{
conv.Messages.Add(feedbackMsg);
}
AddMessageBubble("user", "[수정 요청] " + feedback);
await SendRegenerateAsync(conv);
}
private async Task SendRegenerateAsync(ChatConversation conv)
{
_isStreaming = true;
BtnSend.IsEnabled = false;
BtnSend.Visibility = Visibility.Collapsed;
BtnStop.Visibility = Visibility.Visible;
_streamCts = new CancellationTokenSource();
ChatMessage assistantMsg = new ChatMessage
{
Role = "assistant",
Content = ""
};
lock (_convLock)
{
conv.Messages.Add(assistantMsg);
}
TextBlock streamText;
StackPanel streamContainer = CreateStreamingContainer(out streamText);
MessagePanel.Children.Add(streamContainer);
ForceScrollToEnd();
StringBuilder sb = new StringBuilder();
_activeStreamText = streamText;
_cachedStreamContent = "";
_displayedLength = 0;
_cursorVisible = true;
_aiIconPulseStopped = false;
_cursorTimer.Start();
_typingTimer.Start();
_streamStartTime = DateTime.UtcNow;
_elapsedTimer.Start();
SetStatus("에이전트 작업 중...", spinning: true);
try
{
List<ChatMessage> sendMessages;
lock (_convLock)
{
sendMessages = conv.Messages.SkipLast(1).ToList();
}
if (!string.IsNullOrEmpty(conv.SystemCommand))
{
sendMessages.Insert(0, new ChatMessage
{
Role = "system",
Content = conv.SystemCommand
});
}
await foreach (string chunk in _llm.StreamAsync(sendMessages, _streamCts.Token))
{
sb.Append(chunk);
StopAiIconPulse();
_cachedStreamContent = sb.ToString();
await ((DispatcherObject)this).Dispatcher.InvokeAsync((Action)delegate
{
}, (DispatcherPriority)4);
}
_cachedStreamContent = sb.ToString();
assistantMsg.Content = _cachedStreamContent;
DateTime drainStart2 = DateTime.UtcNow;
while (_displayedLength < _cachedStreamContent.Length && (DateTime.UtcNow - drainStart2).TotalMilliseconds < 600.0)
{
await ((DispatcherObject)this).Dispatcher.InvokeAsync((Action)delegate
{
}, (DispatcherPriority)4);
}
}
catch (OperationCanceledException)
{
if (sb.Length == 0)
{
sb.Append("(취소됨)");
}
assistantMsg.Content = sb.ToString();
}
catch (Exception ex2)
{
Exception ex3 = ex2;
string errMsg = "⚠ 오류: " + ex3.Message;
sb.Clear();
sb.Append(errMsg);
assistantMsg.Content = errMsg;
AddRetryButton();
}
finally
{
_cursorTimer.Stop();
_elapsedTimer.Stop();
_typingTimer.Stop();
HideStickyProgress();
StopRainbowGlow();
_activeStreamText = null;
_elapsedLabel = null;
_cachedStreamContent = "";
_isStreaming = false;
BtnSend.IsEnabled = true;
BtnStop.Visibility = Visibility.Collapsed;
BtnSend.Visibility = Visibility.Visible;
_streamCts?.Dispose();
_streamCts = null;
SetStatusIdle();
}
FinalizeStreamingContainer(streamContainer, streamText, assistantMsg.Content, assistantMsg);
AutoScrollIfNeeded();
try
{
_storage.Save(conv);
}
catch (Exception ex4)
{
LogService.Debug("대화 저장 실패: " + ex4.Message);
}
_tabConversationId[conv.Tab ?? _activeTab] = conv.Id;
RefreshConversationList();
}
private double GetMessageMaxWidth()
{
double num = MessageScroll.ActualWidth;
if (num < 100.0)
{
num = 700.0;
}
double value = (num - 180.0) * 0.82;
return Math.Clamp(value, 520.0, 900.0);
}
private StackPanel CreateStreamingContainer(out TextBlock streamText)
{
double messageMaxWidth = GetMessageMaxWidth();
StackPanel stackPanel = new StackPanel
{
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
Width = messageMaxWidth,
MaxWidth = messageMaxWidth,
Margin = new Thickness(40.0, 8.0, 80.0, 8.0),
Opacity = 0.0,
RenderTransform = new TranslateTransform(0.0, 10.0)
};
stackPanel.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0.0, 1.0, TimeSpan.FromMilliseconds(280.0)));
((TranslateTransform)stackPanel.RenderTransform).BeginAnimation(TranslateTransform.YProperty, new DoubleAnimation(10.0, 0.0, TimeSpan.FromMilliseconds(300.0))
{
EasingFunction = new QuadraticEase
{
EasingMode = EasingMode.EaseOut
}
});
Grid grid = new Grid
{
Margin = new Thickness(0.0, 0.0, 0.0, 4.0)
};
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = GridLength.Auto
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = GridLength.Auto
});
grid.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(1.0, GridUnitType.Star)
});
TextBlock textBlock = new TextBlock();
textBlock.Text = "\ue8bd";
textBlock.FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets");
textBlock.FontSize = 12.0;
textBlock.Foreground = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue;
textBlock.VerticalAlignment = VerticalAlignment.Center;
TextBlock textBlock2 = textBlock;
textBlock2.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(1.0, 0.35, TimeSpan.FromMilliseconds(700.0))
{
AutoReverse = true,
RepeatBehavior = RepeatBehavior.Forever,
EasingFunction = new SineEase()
});
_activeAiIcon = textBlock2;
Grid.SetColumn(textBlock2, 0);
grid.Children.Add(textBlock2);
string item = GetAgentIdentity().Name;
textBlock = new TextBlock();
textBlock.Text = item;
textBlock.FontSize = 11.0;
textBlock.FontWeight = FontWeights.SemiBold;
textBlock.Foreground = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue;
textBlock.Margin = new Thickness(6.0, 0.0, 0.0, 0.0);
textBlock.VerticalAlignment = VerticalAlignment.Center;
TextBlock element = textBlock;
Grid.SetColumn(element, 1);
grid.Children.Add(element);
_elapsedLabel = new TextBlock
{
Text = "0s",
FontSize = 10.5,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Center,
Opacity = 0.5
};
Grid.SetColumn(_elapsedLabel, 2);
grid.Children.Add(_elapsedLabel);
stackPanel.Children.Add(grid);
streamText = new TextBlock
{
Text = "▌",
FontSize = 13.5,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
TextWrapping = TextWrapping.Wrap,
LineHeight = 22.0
};
stackPanel.Children.Add(streamText);
return stackPanel;
}
private void FinalizeStreamingContainer(StackPanel container, TextBlock streamText, string finalContent, ChatMessage? message = null)
{
//IL_06ca: Unknown result type (might be due to invalid IL or missing references)
container.Children.Remove(streamText);
System.Windows.Media.Brush textColor = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush secondaryColor = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush accentColor = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Blue;
System.Windows.Media.Brush codeBg = (TryFindResource("HintBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.DarkGray;
StackPanel stackPanel = MarkdownRenderer.Render(finalContent, textColor, secondaryColor, accentColor, codeBg);
stackPanel.Margin = new Thickness(0.0, 0.0, 0.0, 4.0);
stackPanel.Opacity = 0.0;
container.Children.Add(stackPanel);
stackPanel.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0.0, 1.0, TimeSpan.FromMilliseconds(180.0)));
System.Windows.Media.Brush brush = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
string capturedContent = finalContent;
StackPanel stackPanel2 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
Margin = new Thickness(0.0, 6.0, 0.0, 0.0)
};
stackPanel2.Children.Add(CreateActionButton("\ue8c8", "복사", brush, delegate
{
try
{
System.Windows.Clipboard.SetText(capturedContent);
}
catch
{
}
}));
stackPanel2.Children.Add(CreateActionButton("\ue72c", "다시 생성", brush, delegate
{
RegenerateLastAsync();
}));
stackPanel2.Children.Add(CreateActionButton("\ue70f", "수정 후 재시도", brush, delegate
{
ShowRetryWithFeedbackInput();
}));
AddLinkedFeedbackButtons(stackPanel2, brush, message);
container.Children.Add(stackPanel2);
TimeSpan timeSpan = DateTime.UtcNow - _streamStartTime;
string text = ((timeSpan.TotalSeconds < 60.0) ? $"{timeSpan.TotalSeconds:0.#}s" : $"{(int)timeSpan.TotalMinutes}m {timeSpan.Seconds}s");
TokenUsage lastTokenUsage = _llm.LastTokenUsage;
string activeTab = _activeTab;
bool flag = ((activeTab == "Cowork" || activeTab == "Code") ? true : false);
bool flag2 = flag;
int num = ((flag2 && _agentCumulativeInputTokens > 0) ? _agentCumulativeInputTokens : (lastTokenUsage?.PromptTokens ?? 0));
int num2 = ((flag2 && _agentCumulativeOutputTokens > 0) ? _agentCumulativeOutputTokens : (lastTokenUsage?.CompletionTokens ?? 0));
if (num > 0 || num2 > 0)
{
UpdateStatusTokens(num, num2);
UsageStatisticsService.RecordTokens(num, num2);
}
string text2 = ((num > 0 || num2 > 0) ? $"{FormatTokenCount(num)} + {FormatTokenCount(num2)} = {FormatTokenCount(num + num2)} tokens" : ((!(lastTokenUsage != null)) ? ("~" + FormatTokenCount(EstimateTokenCount(finalContent)) + " tokens") : $"{FormatTokenCount(lastTokenUsage.PromptTokens)} + {FormatTokenCount(lastTokenUsage.CompletionTokens)} = {FormatTokenCount(lastTokenUsage.TotalTokens)} tokens"));
TextBlock textBlock = new TextBlock();
textBlock.Text = text + " · " + text2;
textBlock.FontSize = 10.5;
textBlock.Foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
textBlock.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
textBlock.Margin = new Thickness(0.0, 6.0, 0.0, 0.0);
textBlock.Opacity = 0.6;
TextBlock element = textBlock;
container.Children.Add(element);
List<(string, string)> list = ParseSuggestionChips(finalContent);
if (list.Count <= 0)
{
return;
}
WrapPanel wrapPanel = new WrapPanel
{
Margin = new Thickness(0.0, 8.0, 0.0, 4.0),
HorizontalAlignment = System.Windows.HorizontalAlignment.Left
};
foreach (var item3 in list)
{
string item = item3.Item1;
string item2 = item3.Item2;
Border border = new Border();
border.Background = (TryFindResource("ItemBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Transparent;
border.BorderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
border.BorderThickness = new Thickness(1.0);
border.CornerRadius = new CornerRadius(16.0);
border.Padding = new Thickness(14.0, 7.0, 14.0, 7.0);
border.Margin = new Thickness(0.0, 0.0, 8.0, 6.0);
border.Cursor = System.Windows.Input.Cursors.Hand;
border.RenderTransformOrigin = new Point(0.5, 0.5);
border.RenderTransform = new ScaleTransform(1.0, 1.0);
Border border2 = border;
border2.Child = new TextBlock
{
Text = item + ". " + item2,
FontSize = 12.5,
Foreground = ((TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White)
};
System.Windows.Media.Brush chipHover = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Transparent;
System.Windows.Media.Brush chipNormal = (TryFindResource("ItemBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Transparent;
border2.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border { RenderTransform: ScaleTransform renderTransform } border3)
{
renderTransform.ScaleX = 1.02;
renderTransform.ScaleY = 1.02;
border3.Background = chipHover;
}
};
border2.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border { RenderTransform: ScaleTransform renderTransform } border3)
{
renderTransform.ScaleX = 1.0;
renderTransform.ScaleY = 1.0;
border3.Background = chipNormal;
}
};
string capturedLabel = item + ". " + item2;
WrapPanel capturedPanel = wrapPanel;
border2.MouseLeftButtonDown += delegate
{
if (capturedPanel.Parent is System.Windows.Controls.Panel panel)
{
panel.Children.Remove(capturedPanel);
}
InputBox.Text = capturedLabel;
SendMessageAsync();
};
wrapPanel.Children.Add(border2);
}
container.Children.Add(wrapPanel);
}
private static List<(string Num, string Label)> ParseSuggestionChips(string content)
{
List<(string, string)> list = new List<(string, string)>();
if (string.IsNullOrEmpty(content))
{
return list;
}
string[] array = content.Split('\n');
List<(string, string)> list2 = new List<(string, string)>();
int num = -1;
for (int i = 0; i < array.Length; i++)
{
string text = array[i].Trim();
Match match = Regex.Match(text, "^(\\d+)[.\\)]\\s+(.+)$");
if (match.Success)
{
if (num < 0 || i == num + list2.Count)
{
if (num < 0)
{
num = i;
list2.Clear();
}
list2.Add((match.Groups[1].Value, match.Groups[2].Value.TrimEnd()));
}
else
{
num = i;
list2.Clear();
list2.Add((match.Groups[1].Value, match.Groups[2].Value.TrimEnd()));
}
}
else if (!string.IsNullOrWhiteSpace(text))
{
num = -1;
list2.Clear();
}
}
if (list2.Count >= 2 && list2.Count <= 10)
{
list.AddRange(list2);
}
return list;
}
private static string FormatTokenCount(int count)
{
if (1 == 0)
{
}
string result = ((count >= 1000000) ? $"{(double)count / 1000000.0:0.#}m" : ((count < 1000) ? count.ToString() : $"{(double)count / 1000.0:0.#}k"));
if (1 == 0)
{
}
return result;
}
private static int EstimateTokenCount(string text)
{
if (string.IsNullOrEmpty(text))
{
return 0;
}
int num = 0;
foreach (char c in text)
{
if ((c >= '가' && c <= '힣') || (c >= '\u3000' && c <= '鿿'))
{
num++;
}
}
double num2 = ((text.Length > 0) ? ((double)num / (double)text.Length) : 0.0);
double num3 = 4.0 - num2 * 2.0;
return Math.Max(1, (int)Math.Round((double)text.Length / num3));
}
private void StopGeneration()
{
_streamCts?.Cancel();
}
private void ForkConversation(ChatConversation source, int atIndex)
{
int value = _storage.LoadAllMeta().Count((ChatConversation m) => m.ParentId == source.Id) + 1;
ChatConversation chatConversation = new ChatConversation
{
Title = $"{source.Title} (분기 {value})",
Tab = source.Tab,
Category = source.Category,
WorkFolder = source.WorkFolder,
SystemCommand = source.SystemCommand,
ParentId = source.Id,
BranchLabel = $"분기 {value}",
BranchAtIndex = atIndex
};
for (int num = 0; num <= atIndex && num < source.Messages.Count; num++)
{
ChatMessage chatMessage = source.Messages[num];
chatConversation.Messages.Add(new ChatMessage
{
Role = chatMessage.Role,
Content = chatMessage.Content,
Timestamp = chatMessage.Timestamp
});
}
try
{
_storage.Save(chatConversation);
ShowToast("분기 생성: " + chatConversation.Title);
lock (_convLock)
{
_currentConversation = chatConversation;
}
ChatTitle.Text = chatConversation.Title;
RenderMessages();
RefreshConversationList();
}
catch (Exception ex)
{
ShowToast("분기 실패: " + ex.Message, "\ue783");
}
}
private void OpenCommandPalette()
{
CommandPaletteWindow commandPaletteWindow = new CommandPaletteWindow(ExecuteCommand)
{
Owner = this
};
commandPaletteWindow.ShowDialog();
}
private void ExecuteCommand(string commandId)
{
switch (commandId)
{
case "tab:chat":
TabChat.IsChecked = true;
break;
case "tab:cowork":
TabCowork.IsChecked = true;
break;
case "tab:code":
if (TabCode.IsEnabled)
{
TabCode.IsChecked = true;
}
break;
case "new_conversation":
StartNewConversation();
break;
case "search_conversation":
ToggleMessageSearch();
break;
case "change_model":
BtnModelSelector_Click(this, new RoutedEventArgs());
break;
case "open_settings":
BtnSettings_Click(this, new RoutedEventArgs());
break;
case "open_statistics":
new StatisticsWindow().Show();
break;
case "change_folder":
FolderPathLabel_Click(FolderPathLabel, null);
break;
case "toggle_devmode":
{
LlmSettings llm = _settings.Settings.Llm;
llm.DevMode = !llm.DevMode;
_settings.Save();
UpdateAnalyzerButtonVisibility();
ShowToast(llm.DevMode ? "개발자 모드 켜짐" : "개발자 모드 꺼짐");
break;
}
case "open_audit_log":
try
{
Process.Start("explorer.exe", AuditLogService.GetAuditFolder());
break;
}
catch
{
break;
}
case "paste_clipboard":
try
{
string text = System.Windows.Clipboard.GetText();
if (!string.IsNullOrEmpty(text))
{
InputBox.Text += text;
}
break;
}
catch
{
break;
}
case "export_conversation":
ExportConversation();
break;
}
}
private void ExportConversation()
{
ChatConversation currentConversation;
lock (_convLock)
{
currentConversation = _currentConversation;
}
if (currentConversation == null || currentConversation.Messages.Count == 0)
{
return;
}
Microsoft.Win32.SaveFileDialog saveFileDialog = new Microsoft.Win32.SaveFileDialog
{
FileName = (currentConversation.Title ?? ""),
DefaultExt = ".md",
Filter = "Markdown (*.md)|*.md|JSON (*.json)|*.json|HTML (*.html)|*.html|PDF 인쇄용 HTML (*.pdf.html)|*.pdf.html|Text (*.txt)|*.txt"
};
if (saveFileDialog.ShowDialog() != true)
{
return;
}
string text = System.IO.Path.GetExtension(saveFileDialog.FileName).ToLowerInvariant();
string contents;
if (text == ".json")
{
contents = JsonSerializer.Serialize(currentConversation, new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
}
else
{
if (saveFileDialog.FileName.EndsWith(".pdf.html"))
{
contents = PdfExportService.BuildHtml(currentConversation);
File.WriteAllText(saveFileDialog.FileName, contents, Encoding.UTF8);
PdfExportService.OpenInBrowser(saveFileDialog.FileName);
ShowToast("PDF 인쇄용 HTML이 생성되어 브라우저에서 열렸습니다");
return;
}
if (text == ".html")
{
contents = ExportToHtml(currentConversation);
}
else
{
StringBuilder stringBuilder = new StringBuilder();
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(2, 1, stringBuilder2);
handler.AppendLiteral("# ");
handler.AppendFormatted(currentConversation.Title);
stringBuilder3.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(13, 2, stringBuilder2);
handler.AppendLiteral("_생성: ");
handler.AppendFormatted(currentConversation.CreatedAt, "yyyy-MM-dd HH:mm");
handler.AppendLiteral(" · 주제: ");
handler.AppendFormatted(currentConversation.Category);
handler.AppendLiteral("_");
stringBuilder4.AppendLine(ref handler);
stringBuilder.AppendLine();
foreach (ChatMessage message in currentConversation.Messages)
{
if (!(message.Role == "system"))
{
string value = ((message.Role == "user") ? "**사용자**" : "**AI**");
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(3, 2, stringBuilder2);
handler.AppendFormatted(value);
handler.AppendLiteral(" (");
handler.AppendFormatted(message.Timestamp, "HH:mm");
handler.AppendLiteral(")");
stringBuilder5.AppendLine(ref handler);
stringBuilder.AppendLine();
stringBuilder.AppendLine(message.Content);
List<string> attachedFiles = message.AttachedFiles;
if (attachedFiles != null && attachedFiles.Count > 0)
{
stringBuilder.AppendLine();
stringBuilder.AppendLine("_첨부 파일: " + string.Join(", ", message.AttachedFiles.Select(System.IO.Path.GetFileName)) + "_");
}
stringBuilder.AppendLine();
stringBuilder.AppendLine("---");
stringBuilder.AppendLine();
}
}
contents = stringBuilder.ToString();
}
}
File.WriteAllText(saveFileDialog.FileName, contents, Encoding.UTF8);
}
private static string ExportToHtml(ChatConversation conv)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("<!DOCTYPE html><html><head><meta charset=\"utf-8\">");
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(15, 1, stringBuilder2);
handler.AppendLiteral("<title>");
handler.AppendFormatted(WebUtility.HtmlEncode(conv.Title));
handler.AppendLiteral("</title>");
stringBuilder3.AppendLine(ref handler);
stringBuilder.AppendLine("<style>body{font-family:'Segoe UI',sans-serif;max-width:800px;margin:0 auto;padding:20px;background:#1a1a2e;color:#e0e0e0}");
stringBuilder.AppendLine(".msg{margin:12px 0;padding:12px 16px;border-radius:12px}.user{background:#2d2d5e;margin-left:60px}.ai{background:#1e1e3a;margin-right:60px}");
stringBuilder.AppendLine(".meta{font-size:11px;color:#888;margin-bottom:6px}.content{white-space:pre-wrap;line-height:1.6}");
stringBuilder.AppendLine("h1{text-align:center;color:#8b6dff}pre{background:#111;padding:12px;border-radius:8px;overflow-x:auto}</style></head><body>");
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
handler.AppendLiteral("<h1>");
handler.AppendFormatted(WebUtility.HtmlEncode(conv.Title));
handler.AppendLiteral("</h1>");
stringBuilder4.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(55, 2, stringBuilder2);
handler.AppendLiteral("<p style='text-align:center;color:#888'>생성: ");
handler.AppendFormatted(conv.CreatedAt, "yyyy-MM-dd HH:mm");
handler.AppendLiteral(" · 주제: ");
handler.AppendFormatted(conv.Category);
handler.AppendLiteral("</p>");
stringBuilder5.AppendLine(ref handler);
foreach (ChatMessage message in conv.Messages)
{
if (!(message.Role == "system"))
{
string value = ((message.Role == "user") ? "user" : "ai");
string value2 = ((message.Role == "user") ? "사용자" : "AI");
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder6 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(18, 1, stringBuilder2);
handler.AppendLiteral("<div class='msg ");
handler.AppendFormatted(value);
handler.AppendLiteral("'>");
stringBuilder6.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder7 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(27, 2, stringBuilder2);
handler.AppendLiteral("<div class='meta'>");
handler.AppendFormatted(value2);
handler.AppendLiteral(" · ");
handler.AppendFormatted(message.Timestamp, "HH:mm");
handler.AppendLiteral("</div>");
stringBuilder7.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder8 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(27, 1, stringBuilder2);
handler.AppendLiteral("<div class='content'>");
handler.AppendFormatted(WebUtility.HtmlEncode(message.Content));
handler.AppendLiteral("</div>");
stringBuilder8.AppendLine(ref handler);
stringBuilder.AppendLine("</div>");
}
}
stringBuilder.AppendLine("</body></html>");
return stringBuilder.ToString();
}
private void ChatWindow_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Invalid comparison between Unknown and I4
//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
//IL_01b8: Invalid comparison between Unknown and I4
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Invalid comparison between Unknown and I4
//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
//IL_02d8: Invalid comparison between Unknown and I4
//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
//IL_01d4: Invalid comparison between Unknown and I4
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_006f: Expected I4, but got Unknown
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Expected I4, but got Unknown
//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
//IL_01f0: Invalid comparison between Unknown and I4
//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
//IL_01da: Invalid comparison between Unknown and I4
//IL_033c: Unknown result type (might be due to invalid IL or missing references)
//IL_0343: Invalid comparison between Unknown and I4
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Invalid comparison between Unknown and I4
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Expected I4, but got Unknown
//IL_036c: Unknown result type (might be due to invalid IL or missing references)
//IL_0373: Invalid comparison between Unknown and I4
//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
//IL_01fb: Invalid comparison between Unknown and I4
//IL_01de: Unknown result type (might be due to invalid IL or missing references)
//IL_01e2: Invalid comparison between Unknown and I4
//IL_0391: Unknown result type (might be due to invalid IL or missing references)
//IL_0398: Invalid comparison between Unknown and I4
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Invalid comparison between Unknown and I4
//IL_03b6: Unknown result type (might be due to invalid IL or missing references)
//IL_03bc: Invalid comparison between Unknown and I4
ModifierKeys modifiers = Keyboard.Modifiers;
if ((int)modifiers == 2)
{
Key key = e.Key;
Key val = key;
if ((int)val <= 49)
{
switch (val - 35)
{
default:
switch (val - 45)
{
case 3:
ExportConversation();
e.Handled = true;
break;
case 0:
BtnToggleSidebar_Click(this, new RoutedEventArgs());
e.Handled = true;
break;
case 4:
ToggleMessageSearch();
e.Handled = true;
break;
}
break;
case 0:
TabChat.IsChecked = true;
e.Handled = true;
break;
case 1:
TabCowork.IsChecked = true;
e.Handled = true;
break;
case 2:
if (TabCode.IsEnabled)
{
TabCode.IsChecked = true;
}
e.Handled = true;
break;
}
}
else
{
switch (val - 55)
{
default:
if ((int)val != 66)
{
if ((int)val == 142)
{
BtnSettings_Click(this, new RoutedEventArgs());
e.Handled = true;
}
}
else
{
Close();
e.Handled = true;
}
break;
case 2:
BtnNewChat_Click(this, new RoutedEventArgs());
e.Handled = true;
break;
case 0:
InputBox.Text = "";
InputBox.Focus();
e.Handled = true;
break;
case 1:
BtnModelSelector_Click(this, new RoutedEventArgs());
e.Handled = true;
break;
}
}
}
if ((int)modifiers == 6)
{
Key key2 = e.Key;
Key val2 = key2;
if ((int)val2 <= 47)
{
if ((int)val2 != 46)
{
if ((int)val2 == 47)
{
BtnDeleteAll_Click(this, new RoutedEventArgs());
e.Handled = true;
}
}
else
{
ChatConversation currentConversation;
lock (_convLock)
{
currentConversation = _currentConversation;
}
if (currentConversation != null)
{
ChatMessage chatMessage = currentConversation.Messages.LastOrDefault((ChatMessage m) => m.Role == "assistant");
if (chatMessage != null)
{
try
{
System.Windows.Clipboard.SetText(chatMessage.Content);
}
catch
{
}
}
}
e.Handled = true;
}
}
else if ((int)val2 != 59)
{
if ((int)val2 == 61)
{
RegenerateLastAsync();
e.Handled = true;
}
}
else
{
OpenCommandPalette();
e.Handled = true;
}
}
if ((int)e.Key == 13)
{
if (MessageSearchBar.Visibility == Visibility.Visible)
{
CloseMessageSearch();
e.Handled = true;
}
else if (_isStreaming)
{
StopGeneration();
e.Handled = true;
}
}
if (SlashPopup.IsOpen)
{
if ((int)e.Key == 13)
{
SlashPopup.IsOpen = false;
_slashSelectedIndex = -1;
e.Handled = true;
}
else if ((int)e.Key == 24)
{
SlashPopup_ScrollByDelta(120);
e.Handled = true;
}
else if ((int)e.Key == 26)
{
SlashPopup_ScrollByDelta(-120);
e.Handled = true;
}
else if ((int)e.Key == 6 && _slashSelectedIndex >= 0)
{
e.Handled = true;
ExecuteSlashSelectedItem();
}
}
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
{
StopGeneration();
}
private void BtnPause_Click(object sender, MouseButtonEventArgs e)
{
if (_agentLoop.IsPaused)
{
_agentLoop.Resume();
PauseIcon.Text = "\ue769";
BtnPause.ToolTip = "일시정지";
}
else
{
_agentLoop.PauseAsync();
PauseIcon.Text = "\ue768";
BtnPause.ToolTip = "재개";
}
}
private void BtnExport_Click(object sender, RoutedEventArgs e)
{
ExportConversation();
}
private void ToggleMessageSearch()
{
if (MessageSearchBar.Visibility == Visibility.Visible)
{
CloseMessageSearch();
return;
}
MessageSearchBar.Visibility = Visibility.Visible;
SearchTextBox.Focus();
SearchTextBox.SelectAll();
}
private void CloseMessageSearch()
{
MessageSearchBar.Visibility = Visibility.Collapsed;
SearchTextBox.Text = "";
SearchResultCount.Text = "";
_searchMatchIndices.Clear();
_searchCurrentIndex = -1;
ClearSearchHighlights();
}
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
string value = SearchTextBox.Text.Trim();
if (string.IsNullOrEmpty(value))
{
SearchResultCount.Text = "";
_searchMatchIndices.Clear();
_searchCurrentIndex = -1;
ClearSearchHighlights();
return;
}
ChatConversation currentConversation;
lock (_convLock)
{
currentConversation = _currentConversation;
}
if (currentConversation == null)
{
return;
}
_searchMatchIndices.Clear();
for (int i = 0; i < currentConversation.Messages.Count; i++)
{
if (currentConversation.Messages[i].Content.Contains(value, StringComparison.OrdinalIgnoreCase))
{
_searchMatchIndices.Add(i);
}
}
if (_searchMatchIndices.Count > 0)
{
_searchCurrentIndex = 0;
SearchResultCount.Text = $"1/{_searchMatchIndices.Count}";
HighlightSearchResult();
}
else
{
_searchCurrentIndex = -1;
SearchResultCount.Text = "결과 없음";
}
}
private void SearchPrev_Click(object sender, RoutedEventArgs e)
{
if (_searchMatchIndices.Count != 0)
{
_searchCurrentIndex = (_searchCurrentIndex - 1 + _searchMatchIndices.Count) % _searchMatchIndices.Count;
SearchResultCount.Text = $"{_searchCurrentIndex + 1}/{_searchMatchIndices.Count}";
HighlightSearchResult();
}
}
private void SearchNext_Click(object sender, RoutedEventArgs e)
{
if (_searchMatchIndices.Count != 0)
{
_searchCurrentIndex = (_searchCurrentIndex + 1) % _searchMatchIndices.Count;
SearchResultCount.Text = $"{_searchCurrentIndex + 1}/{_searchMatchIndices.Count}";
HighlightSearchResult();
}
}
private void SearchClose_Click(object sender, RoutedEventArgs e)
{
CloseMessageSearch();
}
private void HighlightSearchResult()
{
if (_searchCurrentIndex < 0 || _searchCurrentIndex >= _searchMatchIndices.Count)
{
return;
}
int num = _searchMatchIndices[_searchCurrentIndex];
if (num < MessagePanel.Children.Count)
{
if (MessagePanel.Children[num] is FrameworkElement frameworkElement)
{
frameworkElement.BringIntoView();
}
}
else if (MessagePanel.Children.Count > 0)
{
UIElementCollection children = MessagePanel.Children;
(children[children.Count - 1] as FrameworkElement)?.BringIntoView();
}
}
private void ClearSearchHighlights()
{
}
private void AddRetryButton()
{
((DispatcherObject)this).Dispatcher.Invoke((Action)delegate
{
Border border = new Border
{
Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, 239, 68, 68)),
CornerRadius = new CornerRadius(8.0),
Padding = new Thickness(12.0, 8.0, 12.0, 8.0),
Margin = new Thickness(40.0, 4.0, 80.0, 4.0),
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
Cursor = System.Windows.Input.Cursors.Hand
};
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(new TextBlock
{
Text = "\ue72c",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 12.0,
Foreground = new SolidColorBrush(System.Windows.Media.Color.FromRgb(239, 68, 68)),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 6.0, 0.0)
});
stackPanel.Children.Add(new TextBlock
{
Text = "재시도",
FontSize = 12.0,
FontWeight = FontWeights.SemiBold,
Foreground = new SolidColorBrush(System.Windows.Media.Color.FromRgb(239, 68, 68)),
VerticalAlignment = VerticalAlignment.Center
});
border.Child = stackPanel;
border.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(48, 239, 68, 68));
}
};
border.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, 239, 68, 68));
}
};
border.MouseLeftButtonUp += delegate
{
lock (_convLock)
{
if (_currentConversation != null)
{
int num = _currentConversation.Messages.Count - 1;
if (num >= 0 && _currentConversation.Messages[num].Role == "assistant")
{
_currentConversation.Messages.RemoveAt(num);
}
}
}
RegenerateLastAsync();
};
MessagePanel.Children.Add(border);
ForceScrollToEnd();
});
}
private void ShowMessageContextMenu(string content, string role)
{
ContextMenu menu = CreateThemedContextMenu();
System.Windows.Media.Brush primaryText = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush secondaryText = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
AddItem("\ue8c8", "텍스트 복사", delegate
{
try
{
System.Windows.Clipboard.SetText(content);
ShowToast("복사되었습니다");
}
catch
{
}
});
AddItem("\ue943", "마크다운 복사", delegate
{
try
{
System.Windows.Clipboard.SetText(content);
ShowToast("마크다운으로 복사됨");
}
catch
{
}
});
AddItem("\ue97a", "인용하여 답장", delegate
{
string text = ((content.Length > 200) ? (content.Substring(0, 200) + "...") : content);
string[] source = text.Split('\n');
string text2 = string.Join("\n", source.Select((string l) => "> " + l));
InputBox.Text = text2 + "\n\n";
InputBox.Focus();
InputBox.CaretIndex = InputBox.Text.Length;
});
menu.Items.Add(new Separator());
if (role == "assistant")
{
AddItem("\ue72c", "응답 재생성", delegate
{
RegenerateLastAsync();
});
}
AddItem("\ue8a5", "여기서 분기", delegate
{
ChatConversation currentConversation;
lock (_convLock)
{
currentConversation = _currentConversation;
}
if (currentConversation != null)
{
int num = currentConversation.Messages.FindLastIndex((ChatMessage m) => m.Role == role && m.Content == content);
if (num >= 0)
{
ForkConversation(currentConversation, num);
}
}
});
menu.Items.Add(new Separator());
string msgContent = content;
string msgRole = role;
AddItem("\ue74d", "이후 메시지 모두 삭제", delegate
{
ChatConversation currentConversation;
lock (_convLock)
{
currentConversation = _currentConversation;
}
if (currentConversation != null)
{
int num = currentConversation.Messages.FindLastIndex((ChatMessage m) => m.Role == msgRole && m.Content == msgContent);
if (num >= 0)
{
int num2 = currentConversation.Messages.Count - num;
if (System.Windows.MessageBox.Show($"이 메시지 포함 {num2}개 메시지를 삭제하시겠습니까?", "메시지 삭제", MessageBoxButton.YesNo, MessageBoxImage.Exclamation) == MessageBoxResult.Yes)
{
currentConversation.Messages.RemoveRange(num, num2);
try
{
_storage.Save(currentConversation);
}
catch (Exception ex)
{
LogService.Debug("대화 저장 실패: " + ex.Message);
}
RenderMessages();
ShowToast($"{num2}개 메시지 삭제됨");
}
}
}
});
menu.IsOpen = true;
void AddItem(string icon, string label, Action action)
{
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(new TextBlock
{
Text = icon,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 12.0,
Foreground = secondaryText,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 8.0, 0.0)
});
stackPanel.Children.Add(new TextBlock
{
Text = label,
FontSize = 12.0,
Foreground = primaryText,
VerticalAlignment = VerticalAlignment.Center
});
MenuItem menuItem = new MenuItem
{
Header = stackPanel,
Padding = new Thickness(8.0, 6.0, 16.0, 6.0)
};
menuItem.Click += delegate
{
action();
};
menu.Items.Add(menuItem);
}
}
private void ShowRandomTip()
{
if (_settings.Settings.Llm.ShowTips && (!(_activeTab != "Cowork") || !(_activeTab != "Code")))
{
string message = Tips[_tipIndex % Tips.Length];
_tipIndex++;
ShowTip(message);
}
}
private void ShowTip(string message)
{
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Expected O, but got Unknown
DispatcherTimer? tipDismissTimer = _tipDismissTimer;
if (tipDismissTimer != null)
{
tipDismissTimer.Stop();
}
ToastText.Text = message;
ToastIcon.Text = "\ue82f";
ToastBorder.Visibility = Visibility.Visible;
ToastBorder.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0.0, 1.0, TimeSpan.FromMilliseconds(300.0)));
int tipDurationSeconds = _settings.Settings.Llm.TipDurationSeconds;
if (tipDurationSeconds <= 0)
{
return;
}
_tipDismissTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(tipDurationSeconds)
};
_tipDismissTimer.Tick += delegate
{
_tipDismissTimer.Stop();
DoubleAnimation doubleAnimation = new DoubleAnimation(1.0, 0.0, TimeSpan.FromMilliseconds(300.0));
doubleAnimation.Completed += delegate
{
ToastBorder.Visibility = Visibility.Collapsed;
};
ToastBorder.BeginAnimation(UIElement.OpacityProperty, doubleAnimation);
};
_tipDismissTimer.Start();
}
private static string LoadProjectContext(string workFolder)
{
if (string.IsNullOrEmpty(workFolder))
{
return "";
}
List<string> list = new List<string>();
string text = workFolder;
for (int i = 0; i < 3; i++)
{
if (string.IsNullOrEmpty(text))
{
break;
}
list.Add(text);
text = Directory.GetParent(text)?.FullName;
}
list.Reverse();
HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
List<string> orderedFiles = new List<string>();
foreach (string item in list)
{
AddIfExists(System.IO.Path.Combine(item, "AX.md"));
AddIfExists(System.IO.Path.Combine(item, "AX.local.md"));
AddIfExists(System.IO.Path.Combine(item, ".ax", "AX.md"));
string path = System.IO.Path.Combine(item, ".ax", "rules");
if (!Directory.Exists(path))
{
continue;
}
foreach (string item2 in Directory.GetFiles(path, "*.md").OrderBy<string, string>((string p) => p, StringComparer.OrdinalIgnoreCase))
{
if (seen.Add(item2))
{
orderedFiles.Add(item2);
}
}
}
if (orderedFiles.Count == 0)
{
return "";
}
StringBuilder stringBuilder = new StringBuilder();
foreach (string item3 in orderedFiles)
{
try
{
string text2 = File.ReadAllText(item3);
if (!string.IsNullOrWhiteSpace(text2))
{
if (text2.Length > 6000)
{
text2 = text2.Substring(0, 6000) + "\n... (truncated)";
}
string fileName = System.IO.Path.GetFileName(item3);
string value = (item3.StartsWith(workFolder, StringComparison.OrdinalIgnoreCase) ? System.IO.Path.GetRelativePath(workFolder, item3) : item3);
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, stringBuilder2);
handler.AppendLiteral("\n## Project Context (");
handler.AppendFormatted(fileName);
handler.AppendLiteral(")");
stringBuilder3.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(8, 1, stringBuilder2);
handler.AppendLiteral("Source: ");
handler.AppendFormatted(value);
stringBuilder4.AppendLine(ref handler);
stringBuilder.AppendLine(text2);
}
}
catch
{
}
}
return stringBuilder.ToString();
void AddIfExists(string text3)
{
if (File.Exists(text3) && seen.Add(text3))
{
orderedFiles.Add(text3);
}
}
}
private void PlayRainbowGlow()
{
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Expected O, but got Unknown
if (!_settings.Settings.Llm.EnableChatRainbowGlow)
{
return;
}
DispatcherTimer? rainbowTimer = _rainbowTimer;
if (rainbowTimer != null)
{
rainbowTimer.Stop();
}
_rainbowStartTime = DateTime.UtcNow;
InputGlowBorder.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0.0, 0.9, TimeSpan.FromMilliseconds(150.0)));
_rainbowTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(16.0)
};
_rainbowTimer.Tick += delegate
{
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
double totalMilliseconds = (DateTime.UtcNow - _rainbowStartTime).TotalMilliseconds;
double num = totalMilliseconds / 1500.0 % 1.0;
if (InputGlowBorder.BorderBrush is LinearGradientBrush linearGradientBrush)
{
double num2 = num * Math.PI * 2.0;
linearGradientBrush.StartPoint = new Point(0.5 + 0.5 * Math.Cos(num2), 0.5 + 0.5 * Math.Sin(num2));
linearGradientBrush.EndPoint = new Point(0.5 - 0.5 * Math.Cos(num2), 0.5 - 0.5 * Math.Sin(num2));
}
};
_rainbowTimer.Start();
}
private void StopRainbowGlow()
{
DispatcherTimer? rainbowTimer = _rainbowTimer;
if (rainbowTimer != null)
{
rainbowTimer.Stop();
}
_rainbowTimer = null;
if (InputGlowBorder.Opacity > 0.0)
{
DoubleAnimation doubleAnimation = new DoubleAnimation(InputGlowBorder.Opacity, 0.0, TimeSpan.FromMilliseconds(600.0));
doubleAnimation.Completed += delegate
{
InputGlowBorder.Opacity = 0.0;
};
InputGlowBorder.BeginAnimation(UIElement.OpacityProperty, doubleAnimation);
}
}
private void ShowToast(string message, string icon = "\ue73e", int durationMs = 2000)
{
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Expected O, but got Unknown
DispatcherTimer? toastHideTimer = _toastHideTimer;
if (toastHideTimer != null)
{
toastHideTimer.Stop();
}
ToastText.Text = message;
ToastIcon.Text = icon;
ToastBorder.Visibility = Visibility.Visible;
ToastBorder.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0.0, 1.0, TimeSpan.FromMilliseconds(200.0)));
_toastHideTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(durationMs)
};
_toastHideTimer.Tick += delegate
{
_toastHideTimer.Stop();
DoubleAnimation doubleAnimation = new DoubleAnimation(1.0, 0.0, TimeSpan.FromMilliseconds(300.0));
doubleAnimation.Completed += delegate
{
ToastBorder.Visibility = Visibility.Collapsed;
};
ToastBorder.BeginAnimation(UIElement.OpacityProperty, doubleAnimation);
};
_toastHideTimer.Start();
}
private void BuildTopicButtons()
{
//IL_022e: Unknown result type (might be due to invalid IL or missing references)
//IL_07bc: Unknown result type (might be due to invalid IL or missing references)
//IL_0bd7: Unknown result type (might be due to invalid IL or missing references)
TopicButtonPanel.Children.Clear();
TopicButtonPanel.Visibility = Visibility.Visible;
if (_activeTab == "Cowork" || _activeTab == "Code")
{
if (EmptyStateTitle != null)
{
EmptyStateTitle.Text = "작업 유형을 선택하세요";
}
if (EmptyStateDesc != null)
{
EmptyStateDesc.Text = ((_activeTab == "Code") ? "코딩 에이전트가 코드 분석, 수정, 빌드, 테스트를 수행합니다" : "에이전트가 상세한 데이터를 작성합니다");
}
}
else
{
if (EmptyStateTitle != null)
{
EmptyStateTitle.Text = "대화 주제를 선택하세요";
}
if (EmptyStateDesc != null)
{
EmptyStateDesc.Text = "주제에 맞는 전문 프리셋이 자동 적용됩니다";
}
}
IReadOnlyList<TopicPreset> byTabWithCustom = PresetService.GetByTabWithCustom(_activeTab, _settings.Settings.Llm.CustomPresets);
Border border;
foreach (TopicPreset item in byTabWithCustom)
{
TopicPreset capturedPreset = item;
SolidColorBrush solidColorBrush = BrushFromHex(item.Color);
border = new Border();
border.Background = (TryFindResource("ItemBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Transparent;
border.CornerRadius = new CornerRadius(14.0);
border.Padding = new Thickness(14.0, 12.0, 14.0, 12.0);
border.Margin = new Thickness(4.0, 4.0, 4.0, 8.0);
border.Cursor = System.Windows.Input.Cursors.Hand;
border.Width = 120.0;
border.Height = 105.0;
border.ClipToBounds = true;
border.RenderTransformOrigin = new Point(0.5, 0.5);
border.RenderTransform = new ScaleTransform(1.0, 1.0);
Border border2 = border;
StackPanel stackPanel = new StackPanel
{
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
};
Border border3 = new Border
{
Width = 40.0,
Height = 40.0,
CornerRadius = new CornerRadius(20.0),
Background = new SolidColorBrush(solidColorBrush.Color)
{
Opacity = 0.15
},
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 0.0, 10.0)
};
TextBlock child = new TextBlock
{
Text = item.Symbol,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 18.0,
Foreground = solidColorBrush,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
border3.Child = child;
stackPanel.Children.Add(border3);
stackPanel.Children.Add(new TextBlock
{
Text = item.Label,
FontSize = 13.0,
FontWeight = FontWeights.SemiBold,
Foreground = ((TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White),
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
});
stackPanel.Children.Add(new TextBlock
{
Text = item.Description,
FontSize = 9.0,
TextWrapping = TextWrapping.Wrap,
TextTrimming = TextTrimming.CharacterEllipsis,
MaxHeight = 28.0,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
Margin = new Thickness(0.0, 2.0, 0.0, 0.0),
TextAlignment = TextAlignment.Center
});
if (capturedPreset.IsCustom)
{
Grid grid = new Grid();
grid.Children.Add(stackPanel);
Border border4 = new Border
{
Width = 16.0,
Height = 16.0,
CornerRadius = new CornerRadius(4.0),
Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(96, byte.MaxValue, byte.MaxValue, byte.MaxValue)),
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(2.0, 2.0, 0.0, 0.0),
ToolTip = "커스텀 프리셋"
};
border4.Child = new TextBlock
{
Text = "\ue710",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 8.0,
Foreground = solidColorBrush,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
grid.Children.Add(border4);
border2.Child = grid;
}
else
{
border2.Child = stackPanel;
}
System.Windows.Media.Brush hoverBg = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Transparent;
System.Windows.Media.Brush normalBg = (TryFindResource("ItemBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Transparent;
border2.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border { RenderTransform: ScaleTransform renderTransform } border8)
{
renderTransform.ScaleX = 1.03;
renderTransform.ScaleY = 1.03;
border8.Background = hoverBg;
}
};
border2.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border { RenderTransform: ScaleTransform renderTransform } border8)
{
renderTransform.ScaleX = 1.0;
renderTransform.ScaleY = 1.0;
border8.Background = normalBg;
}
};
border2.MouseLeftButtonDown += delegate
{
SelectTopic(capturedPreset);
};
if (capturedPreset.IsCustom)
{
border2.MouseRightButtonUp += delegate(object s, MouseButtonEventArgs e)
{
e.Handled = true;
ShowCustomPresetContextMenu(s as Border, capturedPreset);
};
}
TopicButtonPanel.Children.Add(border2);
}
SolidColorBrush solidColorBrush2 = BrushFromHex("#6B7280");
border = new Border();
border.Background = (TryFindResource("ItemBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Transparent;
border.CornerRadius = new CornerRadius(14.0);
border.Padding = new Thickness(14.0, 12.0, 14.0, 12.0);
border.Margin = new Thickness(4.0, 4.0, 4.0, 8.0);
border.Cursor = System.Windows.Input.Cursors.Hand;
border.Width = 120.0;
border.Height = 105.0;
border.ClipToBounds = true;
border.RenderTransformOrigin = new Point(0.5, 0.5);
border.RenderTransform = new ScaleTransform(1.0, 1.0);
Border border5 = border;
StackPanel stackPanel2 = new StackPanel
{
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
};
Border border6 = new Border
{
Width = 40.0,
Height = 40.0,
CornerRadius = new CornerRadius(20.0),
Background = new SolidColorBrush(solidColorBrush2.Color)
{
Opacity = 0.15
},
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 0.0, 10.0)
};
border6.Child = new TextBlock
{
Text = "\ue70f",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 18.0,
Foreground = solidColorBrush2,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
stackPanel2.Children.Add(border6);
stackPanel2.Children.Add(new TextBlock
{
Text = "기타",
FontSize = 13.0,
FontWeight = FontWeights.SemiBold,
Foreground = ((TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White),
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
});
stackPanel2.Children.Add(new TextBlock
{
Text = "프리셋 없이 자유롭게 대화합니다",
FontSize = 9.0,
TextWrapping = TextWrapping.Wrap,
TextTrimming = TextTrimming.CharacterEllipsis,
MaxHeight = 28.0,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
Margin = new Thickness(0.0, 2.0, 0.0, 0.0),
TextAlignment = TextAlignment.Center
});
border5.Child = stackPanel2;
System.Windows.Media.Brush hoverBg2 = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Transparent;
System.Windows.Media.Brush normalBg2 = (TryFindResource("ItemBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Transparent;
border5.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border { RenderTransform: ScaleTransform renderTransform } border8)
{
renderTransform.ScaleX = 1.03;
renderTransform.ScaleY = 1.03;
border8.Background = hoverBg2;
}
};
border5.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border { RenderTransform: ScaleTransform renderTransform } border8)
{
renderTransform.ScaleX = 1.0;
renderTransform.ScaleY = 1.0;
border8.Background = normalBg2;
}
};
border5.MouseLeftButtonDown += delegate
{
EmptyState.Visibility = Visibility.Collapsed;
InputBox.Focus();
};
TopicButtonPanel.Children.Add(border5);
SolidColorBrush solidColorBrush3 = BrushFromHex("#6366F1");
border = new Border();
border.Background = System.Windows.Media.Brushes.Transparent;
border.CornerRadius = new CornerRadius(14.0);
border.Padding = new Thickness(14.0, 12.0, 14.0, 12.0);
border.Margin = new Thickness(4.0, 4.0, 4.0, 8.0);
border.Cursor = System.Windows.Input.Cursors.Hand;
border.Width = 120.0;
border.Height = 105.0;
border.ClipToBounds = true;
border.BorderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
border.BorderThickness = new Thickness(1.5);
border.RenderTransformOrigin = new Point(0.5, 0.5);
border.RenderTransform = new ScaleTransform(1.0, 1.0);
Border border7 = border;
if (border7.BorderBrush is SolidColorBrush brush)
{
System.Windows.Media.Pen pen = new System.Windows.Media.Pen(brush, 1.5)
{
DashStyle = DashStyles.Dash
};
}
StackPanel stackPanel3 = new StackPanel
{
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
TextBlock textBlock = new TextBlock();
textBlock.Text = "\ue710";
textBlock.FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets");
textBlock.FontSize = 24.0;
textBlock.Foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
textBlock.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
textBlock.Margin = new Thickness(0.0, 8.0, 0.0, 8.0);
TextBlock element = textBlock;
stackPanel3.Children.Add(element);
stackPanel3.Children.Add(new TextBlock
{
Text = "프리셋 추가",
FontSize = 12.0,
Foreground = ((TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
});
border7.Child = stackPanel3;
System.Windows.Media.Brush hoverBg3 = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Transparent;
border7.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border { RenderTransform: ScaleTransform renderTransform } border8)
{
renderTransform.ScaleX = 1.03;
renderTransform.ScaleY = 1.03;
border8.Background = hoverBg3;
}
};
border7.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border { RenderTransform: ScaleTransform renderTransform } border8)
{
renderTransform.ScaleX = 1.0;
renderTransform.ScaleY = 1.0;
border8.Background = System.Windows.Media.Brushes.Transparent;
}
};
border7.MouseLeftButtonDown += delegate
{
ShowCustomPresetDialog();
};
TopicButtonPanel.Children.Add(border7);
}
private void ShowCustomPresetDialog(CustomPresetEntry? existing = null)
{
bool flag = existing != null;
CustomPresetDialog customPresetDialog = new CustomPresetDialog(existing?.Label ?? "", existing?.Description ?? "", existing?.SystemPrompt ?? "", existing?.Color ?? "#6366F1", existing?.Symbol ?? "\ue713", existing?.Tab ?? _activeTab)
{
Owner = this
};
if (customPresetDialog.ShowDialog() == true)
{
if (flag)
{
existing.Label = customPresetDialog.PresetName;
existing.Description = customPresetDialog.PresetDescription;
existing.SystemPrompt = customPresetDialog.PresetSystemPrompt;
existing.Color = customPresetDialog.PresetColor;
existing.Symbol = customPresetDialog.PresetSymbol;
existing.Tab = customPresetDialog.PresetTab;
}
else
{
_settings.Settings.Llm.CustomPresets.Add(new CustomPresetEntry
{
Label = customPresetDialog.PresetName,
Description = customPresetDialog.PresetDescription,
SystemPrompt = customPresetDialog.PresetSystemPrompt,
Color = customPresetDialog.PresetColor,
Symbol = customPresetDialog.PresetSymbol,
Tab = customPresetDialog.PresetTab
});
}
_settings.Save();
BuildTopicButtons();
}
}
private void ShowCustomPresetContextMenu(Border? anchor, TopicPreset preset)
{
if (anchor == null || preset.CustomId == null)
{
return;
}
Popup popup = new Popup
{
PlacementTarget = anchor,
Placement = PlacementMode.Bottom,
StaysOpen = false,
AllowsTransparency = true
};
System.Windows.Media.Brush background = (TryFindResource("LauncherBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Black;
System.Windows.Media.Brush fg = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush secondaryFg = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush borderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
Border border = new Border
{
Background = background,
CornerRadius = new CornerRadius(10.0),
BorderBrush = borderBrush,
BorderThickness = new Thickness(1.0),
Padding = new Thickness(4.0),
MinWidth = 120.0,
Effect = new DropShadowEffect
{
BlurRadius = 12.0,
ShadowDepth = 2.0,
Opacity = 0.3,
Color = Colors.Black
}
};
StackPanel stackPanel = new StackPanel();
Border border2 = CreateContextMenuItem("\ue70f", "편집", fg, secondaryFg);
border2.MouseLeftButtonDown += delegate
{
popup.IsOpen = false;
CustomPresetEntry customPresetEntry = _settings.Settings.Llm.CustomPresets.FirstOrDefault((CustomPresetEntry c) => c.Id == preset.CustomId);
if (customPresetEntry != null)
{
ShowCustomPresetDialog(customPresetEntry);
}
};
stackPanel.Children.Add(border2);
Border border3 = CreateContextMenuItem("\ue74d", "삭제", new SolidColorBrush(System.Windows.Media.Color.FromRgb(239, 68, 68)), secondaryFg);
border3.MouseLeftButtonDown += delegate
{
popup.IsOpen = false;
MessageBoxResult messageBoxResult = CustomMessageBox.Show("'" + preset.Label + "' 프리셋을 삭제하시겠습니까?", "프리셋 삭제", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (messageBoxResult == MessageBoxResult.Yes)
{
_settings.Settings.Llm.CustomPresets.RemoveAll((CustomPresetEntry c) => c.Id == preset.CustomId);
_settings.Save();
BuildTopicButtons();
}
};
stackPanel.Children.Add(border3);
border.Child = stackPanel;
popup.Child = border;
popup.IsOpen = true;
}
private Border CreateContextMenuItem(string icon, string label, System.Windows.Media.Brush fg, System.Windows.Media.Brush secondaryFg)
{
Border border = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(6.0),
Padding = new Thickness(10.0, 6.0, 14.0, 6.0),
Cursor = System.Windows.Input.Cursors.Hand
};
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(new TextBlock
{
Text = icon,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 13.0,
Foreground = fg,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 8.0, 0.0)
});
stackPanel.Children.Add(new TextBlock
{
Text = label,
FontSize = 13.0,
Foreground = fg,
VerticalAlignment = VerticalAlignment.Center
});
border.Child = stackPanel;
System.Windows.Media.Brush hoverBg = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Transparent;
border.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = hoverBg;
}
};
border.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = System.Windows.Media.Brushes.Transparent;
}
};
return border;
}
private void SelectTopic(TopicPreset preset)
{
bool flag;
lock (_convLock)
{
ChatConversation? currentConversation = _currentConversation;
flag = currentConversation != null && currentConversation.Messages.Count > 0;
}
bool flag2 = !string.IsNullOrEmpty(InputBox.Text);
bool flag3 = flag || flag2;
if (!flag3)
{
StartNewConversation();
}
lock (_convLock)
{
if (_currentConversation != null)
{
_currentConversation.SystemCommand = preset.SystemPrompt;
_currentConversation.Category = preset.Category;
}
}
if (!flag3)
{
EmptyState.Visibility = Visibility.Collapsed;
}
InputBox.Focus();
if (!string.IsNullOrEmpty(preset.Placeholder))
{
_promptCardPlaceholder = preset.Placeholder;
if (!flag3)
{
ShowPlaceholder();
}
}
if (flag3)
{
ShowToast("프리셋 변경: " + preset.Label);
}
if (_activeTab == "Cowork")
{
BuildBottomBar();
}
}
private void BuildBottomBar()
{
MoodIconPanel.Children.Clear();
System.Windows.Media.Brush brush = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
string key = _settings.Settings.Llm.DefaultOutputFormat ?? "auto";
string formatLabel = GetFormatLabel(key);
Border border = CreateFolderBarButton("\ue9f9", formatLabel, "보고서 형태 선택", "#8B5CF6");
border.MouseLeftButtonUp += delegate(object _, MouseButtonEventArgs e)
{
e.Handled = true;
ShowFormatMenu();
};
try
{
RegisterName("BtnFormatMenu", border);
}
catch
{
try
{
UnregisterName("BtnFormatMenu");
RegisterName("BtnFormatMenu", border);
}
catch
{
}
}
MoodIconPanel.Children.Add(border);
MoodIconPanel.Children.Add(new Border
{
Width = 1.0,
Height = 18.0,
Background = ((TryFindResource("SeparatorColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
Margin = new Thickness(4.0, 0.0, 4.0, 0.0),
VerticalAlignment = VerticalAlignment.Center
});
TemplateMood templateMood = TemplateService.AllMoods.FirstOrDefault((TemplateMood m) => m.Key == _selectedMood);
string text = templateMood?.Label ?? "모던";
string text2 = templateMood?.Icon ?? "\ud83d\udd37";
Border border2 = CreateFolderBarButton(null, text2 + " " + text, "디자인 무드 선택");
border2.MouseLeftButtonUp += delegate(object _, MouseButtonEventArgs e)
{
e.Handled = true;
ShowMoodMenu();
};
try
{
RegisterName("BtnMoodMenu", border2);
}
catch
{
try
{
UnregisterName("BtnMoodMenu");
RegisterName("BtnMoodMenu", border2);
}
catch
{
}
}
MoodIconPanel.Children.Add(border2);
MoodIconPanel.Children.Add(new Border
{
Width = 1.0,
Height = 18.0,
Background = ((TryFindResource("SeparatorColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
Margin = new Thickness(4.0, 0.0, 4.0, 0.0),
VerticalAlignment = VerticalAlignment.Center
});
Border border3 = CreateFolderBarButton("\ued25", "파일", "파일 탐색기 열기/닫기", "#D97706");
border3.MouseLeftButtonUp += delegate(object _, MouseButtonEventArgs e)
{
e.Handled = true;
ToggleFileBrowser();
};
MoodIconPanel.Children.Add(border3);
AppendLogLevelButton();
if (FormatMoodSeparator != null)
{
FormatMoodSeparator.Visibility = Visibility.Visible;
}
}
private void BuildCodeBottomBar()
{
MoodIconPanel.Children.Clear();
System.Windows.Media.Brush brush = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
string selectedLanguage = _selectedLanguage;
if (1 == 0)
{
}
string text = selectedLanguage switch
{
"python" => "\ud83d\udc0d Python",
"java" => "☕ Java",
"csharp" => "\ud83d\udd37 C#",
"cpp" => "⚙ C++",
"javascript" => "\ud83c\udf10 JavaScript",
_ => "\ud83d\udd27 자동 감지",
};
if (1 == 0)
{
}
string label = text;
Border border = CreateFolderBarButton(null, label, "개발 언어 선택");
border.MouseLeftButtonUp += delegate(object _, MouseButtonEventArgs e)
{
e.Handled = true;
ShowLanguageMenu();
};
try
{
RegisterName("BtnLangMenu", border);
}
catch
{
try
{
UnregisterName("BtnLangMenu");
RegisterName("BtnLangMenu", border);
}
catch
{
}
}
MoodIconPanel.Children.Add(border);
MoodIconPanel.Children.Add(new Border
{
Width = 1.0,
Height = 18.0,
Background = ((TryFindResource("SeparatorColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
Margin = new Thickness(4.0, 0.0, 4.0, 0.0),
VerticalAlignment = VerticalAlignment.Center
});
Border border2 = CreateFolderBarButton("\ued25", "파일", "파일 탐색기 열기/닫기", "#D97706");
border2.MouseLeftButtonUp += delegate(object _, MouseButtonEventArgs e)
{
e.Handled = true;
ToggleFileBrowser();
};
MoodIconPanel.Children.Add(border2);
AppendLogLevelButton();
if (FormatMoodSeparator != null)
{
FormatMoodSeparator.Visibility = Visibility.Visible;
}
}
private void AppendLogLevelButton()
{
MoodIconPanel.Children.Add(new Border
{
Width = 1.0,
Height = 18.0,
Background = ((TryFindResource("SeparatorColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray),
Margin = new Thickness(4.0, 0.0, 4.0, 0.0),
VerticalAlignment = VerticalAlignment.Center
});
string text = _settings.Settings.Llm.AgentLogLevel ?? "simple";
if (1 == 0)
{
}
string text2 = ((text == "debug") ? "디버그" : ((!(text == "detailed")) ? "간략" : "상세"));
if (1 == 0)
{
}
string label = text2;
Border border = CreateFolderBarButton("\ue946", label, "실행 이력 상세도", "#059669");
border.MouseLeftButtonUp += delegate(object _, MouseButtonEventArgs e)
{
e.Handled = true;
ShowLogLevelMenu();
};
try
{
RegisterName("BtnLogLevelMenu", border);
}
catch
{
try
{
UnregisterName("BtnLogLevelMenu");
RegisterName("BtnLogLevelMenu", border);
}
catch
{
}
}
MoodIconPanel.Children.Add(border);
}
private void ShowLogLevelMenu()
{
FormatMenuItems.Children.Clear();
System.Windows.Media.Brush brush = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush brush2 = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
System.Windows.Media.Brush foreground = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
(string, string, string)[] array = new(string, string, string)[3]
{
("simple", "Simple (간략)", "도구 결과만 한 줄로 표시"),
("detailed", "Detailed (상세)", "도구 호출/결과 + 접이식 상세"),
("debug", "Debug (디버그)", "모든 정보 + 파라미터 표시")
};
string text = _settings.Settings.Llm.AgentLogLevel ?? "simple";
(string, string, string)[] array2 = array;
for (int i = 0; i < array2.Length; i++)
{
(string, string, string) tuple = array2[i];
string key = tuple.Item1;
string item = tuple.Item2;
string item2 = tuple.Item3;
bool flag = text == key;
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(CreateCheckIcon(flag, brush2));
stackPanel.Children.Add(new TextBlock
{
Text = item,
FontSize = 13.0,
Foreground = (flag ? brush2 : brush),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 8.0, 0.0)
});
stackPanel.Children.Add(new TextBlock
{
Text = item2,
FontSize = 10.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center
});
Border border = new Border
{
Child = stackPanel,
Padding = new Thickness(12.0, 8.0, 12.0, 8.0),
CornerRadius = new CornerRadius(6.0),
Background = System.Windows.Media.Brushes.Transparent,
Cursor = System.Windows.Input.Cursors.Hand
};
System.Windows.Media.Brush hoverBg = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue));
border.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
((Border)s).Background = hoverBg;
};
border.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
((Border)s).Background = System.Windows.Media.Brushes.Transparent;
};
border.MouseLeftButtonUp += delegate
{
_settings.Settings.Llm.AgentLogLevel = key;
_settings.Save();
FormatMenuPopup.IsOpen = false;
if (_activeTab == "Cowork")
{
BuildBottomBar();
}
else if (_activeTab == "Code")
{
BuildCodeBottomBar();
}
};
FormatMenuItems.Children.Add(border);
}
try
{
if (FindName("BtnLogLevelMenu") is UIElement placementTarget)
{
FormatMenuPopup.PlacementTarget = placementTarget;
}
}
catch
{
}
FormatMenuPopup.IsOpen = true;
}
private void ShowLanguageMenu()
{
FormatMenuItems.Children.Clear();
System.Windows.Media.Brush brush = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush brush2 = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
(string, string, string)[] array = new(string, string, string)[6]
{
("auto", "자동 감지", "\ud83d\udd27"),
("python", "Python", "\ud83d\udc0d"),
("java", "Java", "☕"),
("csharp", "C# (.NET)", "\ud83d\udd37"),
("cpp", "C/C++", "⚙"),
("javascript", "JavaScript / Vue", "\ud83c\udf10")
};
(string, string, string)[] array2 = array;
for (int i = 0; i < array2.Length; i++)
{
(string, string, string) tuple = array2[i];
string item = tuple.Item1;
string item2 = tuple.Item2;
string item3 = tuple.Item3;
bool flag = _selectedLanguage == item;
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(CreateCheckIcon(flag, brush2));
stackPanel.Children.Add(new TextBlock
{
Text = item3,
FontSize = 13.0,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 8.0, 0.0)
});
stackPanel.Children.Add(new TextBlock
{
Text = item2,
FontSize = 13.0,
Foreground = (flag ? brush2 : brush),
FontWeight = (flag ? FontWeights.SemiBold : FontWeights.Normal)
});
Border border = new Border
{
Child = stackPanel,
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(8.0),
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(8.0, 7.0, 12.0, 7.0)
};
ApplyMenuItemHover(border);
string capturedKey = item;
border.MouseLeftButtonUp += delegate
{
FormatMenuPopup.IsOpen = false;
_selectedLanguage = capturedKey;
BuildCodeBottomBar();
};
FormatMenuItems.Children.Add(border);
}
if (FindName("BtnLangMenu") is UIElement placementTarget)
{
FormatMenuPopup.PlacementTarget = placementTarget;
}
FormatMenuPopup.IsOpen = true;
}
private Border CreateFolderBarButton(string? mdlIcon, string label, string tooltip, string? iconColorHex = null)
{
System.Windows.Media.Brush brush = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush foreground = ((iconColorHex != null) ? BrushFromHex(iconColorHex) : brush);
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
if (mdlIcon != null)
{
stackPanel.Children.Add(new TextBlock
{
Text = mdlIcon,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 12.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 4.0, 0.0)
});
}
stackPanel.Children.Add(new TextBlock
{
Text = label,
FontSize = 12.0,
Foreground = brush,
VerticalAlignment = VerticalAlignment.Center
});
return new Border
{
Child = stackPanel,
Background = System.Windows.Media.Brushes.Transparent,
Padding = new Thickness(6.0, 4.0, 6.0, 4.0),
Cursor = System.Windows.Input.Cursors.Hand,
ToolTip = tooltip
};
}
private static string GetFormatLabel(string key)
{
if (1 == 0)
{
}
string result = key switch
{
"xlsx" => "Excel",
"html" => "HTML 보고서",
"docx" => "Word",
"md" => "Markdown",
"csv" => "CSV",
_ => "AI 자동",
};
if (1 == 0)
{
}
return result;
}
private (string Name, string Symbol, string Color) GetAgentIdentity()
{
string text = null;
lock (_convLock)
{
text = _currentConversation?.Category;
}
string text2 = text;
if (1 == 0)
{
}
(string, string, string) result = text2 switch
{
"보고서" => ("보고서 에이전트", "◆", "#3B82F6"),
"데이터" => ("데이터 분석 에이전트", "◆", "#10B981"),
"문서" => ("문서 작성 에이전트", "◆", "#6366F1"),
"논문" => ("논문 분석 에이전트", "◆", "#6366F1"),
"파일" => ("파일 관리 에이전트", "◆", "#8B5CF6"),
"자동화" => ("자동화 에이전트", "◆", "#EF4444"),
"코드개발" => ("코드 개발 에이전트", "◆", "#3B82F6"),
"리팩터링" => ("리팩터링 에이전트", "◆", "#6366F1"),
"코드리뷰" => ("코드 리뷰 에이전트", "◆", "#10B981"),
"보안점검" => ("보안 점검 에이전트", "◆", "#EF4444"),
"테스트" => ("테스트 에이전트", "◆", "#F59E0B"),
"연구개발" => ("연구개발 에이전트", "◆", "#0EA5E9"),
"시스템" => ("시스템 에이전트", "◆", "#64748B"),
"수율분석" => ("수율분석 에이전트", "◆", "#F59E0B"),
"제품분석" => ("제품분석 에이전트", "◆", "#EC4899"),
"경영" => ("경영 분석 에이전트", "◆", "#8B5CF6"),
"인사" => ("인사 관리 에이전트", "◆", "#14B8A6"),
"제조기술" => ("제조기술 에이전트", "◆", "#F97316"),
"재무" => ("재무 분석 에이전트", "◆", "#6366F1"),
_ => (_activeTab == "Code") ? ("코드 에이전트", "◆", "#3B82F6") : ((!(_activeTab == "Cowork")) ? ("AX 에이전트", "◆", "#4B5EFC") : ("코워크 에이전트", "◆", "#4B5EFC")),
};
if (1 == 0)
{
}
return result;
}
private void ShowFormatMenu()
{
FormatMenuItems.Children.Clear();
System.Windows.Media.Brush foreground = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush brush = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush accentBrush = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
string text = _settings.Settings.Llm.DefaultOutputFormat ?? "auto";
(string, string, string, string)[] array = new(string, string, string, string)[6]
{
("auto", "AI 자동 선택", "\ue8bd", "#8B5CF6"),
("xlsx", "Excel", "\ue9f9", "#217346"),
("html", "HTML 보고서", "\ue12b", "#E44D26"),
("docx", "Word", "\ue8a5", "#2B579A"),
("md", "Markdown", "\ue943", "#6B7280"),
("csv", "CSV", "\ue9d9", "#10B981")
};
(string, string, string, string)[] array2 = array;
for (int i = 0; i < array2.Length; i++)
{
(string, string, string, string) tuple = array2[i];
string item = tuple.Item1;
string item2 = tuple.Item2;
string item3 = tuple.Item3;
string item4 = tuple.Item4;
bool isChecked = item == text;
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(CreateCheckIcon(isChecked, accentBrush));
stackPanel.Children.Add(new TextBlock
{
Text = item3,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 13.0,
Foreground = BrushFromHex(item4),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 8.0, 0.0)
});
stackPanel.Children.Add(new TextBlock
{
Text = item2,
FontSize = 13.0,
Foreground = foreground,
VerticalAlignment = VerticalAlignment.Center
});
Border border = new Border
{
Child = stackPanel,
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(8.0),
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(8.0, 7.0, 12.0, 7.0)
};
ApplyMenuItemHover(border);
string capturedKey = item;
border.MouseLeftButtonUp += delegate
{
FormatMenuPopup.IsOpen = false;
_settings.Settings.Llm.DefaultOutputFormat = capturedKey;
_settings.Save();
BuildBottomBar();
};
FormatMenuItems.Children.Add(border);
}
if (FindName("BtnFormatMenu") is UIElement placementTarget)
{
FormatMenuPopup.PlacementTarget = placementTarget;
}
FormatMenuPopup.IsOpen = true;
}
private void ShowMoodMenu()
{
MoodMenuItems.Children.Clear();
System.Windows.Media.Brush foreground = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush foreground2 = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush brush = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
System.Windows.Media.Brush fill = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
UniformGrid uniformGrid = new UniformGrid
{
Columns = 2
};
foreach (TemplateMood mood in TemplateService.AllMoods)
{
bool flag = _selectedMood == mood.Key;
bool flag2 = _settings.Settings.Llm.CustomMoods.Any((CustomMoodEntry cm) => cm.Key == mood.Key);
TemplateService.MoodColors moodColors = TemplateService.GetMoodColors(mood.Key);
Border border = new Border
{
Width = 160.0,
Height = 80.0,
CornerRadius = new CornerRadius(6.0),
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(moodColors.Background)),
BorderBrush = (flag ? brush : new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(moodColors.Border))),
BorderThickness = new Thickness((!flag) ? 1 : 2),
Padding = new Thickness(8.0, 6.0, 8.0, 6.0),
Margin = new Thickness(2.0)
};
StackPanel stackPanel = new StackPanel();
stackPanel.Children.Add(new Border
{
Width = 60.0,
Height = 6.0,
CornerRadius = new CornerRadius(2.0),
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(moodColors.PrimaryText)),
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
Margin = new Thickness(0.0, 0.0, 0.0, 4.0)
});
stackPanel.Children.Add(new Border
{
Width = 40.0,
Height = 3.0,
CornerRadius = new CornerRadius(1.0),
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(moodColors.Accent)),
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
Margin = new Thickness(0.0, 0.0, 0.0, 6.0)
});
for (int num = 0; num < 3; num++)
{
stackPanel.Children.Add(new Border
{
Width = 120 - num * 20,
Height = 3.0,
CornerRadius = new CornerRadius(1.0),
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(moodColors.SecondaryText))
{
Opacity = 0.5
},
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
Margin = new Thickness(0.0, 0.0, 0.0, 3.0)
});
}
StackPanel stackPanel2 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal,
Margin = new Thickness(0.0, 2.0, 0.0, 0.0)
};
for (int num2 = 0; num2 < 2; num2++)
{
stackPanel2.Children.Add(new Border
{
Width = 28.0,
Height = 14.0,
CornerRadius = new CornerRadius(2.0),
Background = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(moodColors.CardBg)),
BorderBrush = new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(moodColors.Border)),
BorderThickness = new Thickness(0.5),
Margin = new Thickness(0.0, 0.0, 4.0, 0.0)
});
}
stackPanel.Children.Add(stackPanel2);
border.Child = stackPanel;
StackPanel stackPanel3 = new StackPanel
{
Margin = new Thickness(4.0, 2.0, 4.0, 4.0)
};
StackPanel stackPanel4 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel4.Children.Add(new TextBlock
{
Text = mood.Icon,
FontSize = 12.0,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 4.0, 0.0)
});
stackPanel4.Children.Add(new TextBlock
{
Text = mood.Label,
FontSize = 11.5,
Foreground = foreground,
FontWeight = (flag ? FontWeights.SemiBold : FontWeights.Normal),
VerticalAlignment = VerticalAlignment.Center
});
if (flag)
{
stackPanel4.Children.Add(new TextBlock
{
Text = " ✓",
FontSize = 11.0,
Foreground = brush,
VerticalAlignment = VerticalAlignment.Center
});
}
stackPanel3.Children.Add(stackPanel4);
Border border2 = new Border
{
CornerRadius = new CornerRadius(8.0),
Background = System.Windows.Media.Brushes.Transparent,
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(4.0),
Margin = new Thickness(2.0)
};
StackPanel stackPanel5 = new StackPanel();
stackPanel5.Children.Add(border);
stackPanel5.Children.Add(stackPanel3);
border2.Child = stackPanel5;
border2.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border4)
{
border4.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(18, byte.MaxValue, byte.MaxValue, byte.MaxValue));
}
};
border2.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border4)
{
border4.Background = System.Windows.Media.Brushes.Transparent;
}
};
TemplateMood capturedMood = mood;
border2.MouseLeftButtonUp += delegate
{
MoodMenuPopup.IsOpen = false;
_selectedMood = capturedMood.Key;
_settings.Settings.Llm.DefaultMood = capturedMood.Key;
_settings.Save();
BuildBottomBar();
};
if (flag2)
{
border2.MouseRightButtonUp += delegate(object s, MouseButtonEventArgs e)
{
e.Handled = true;
MoodMenuPopup.IsOpen = false;
ShowCustomMoodContextMenu(s as Border, capturedMood.Key);
};
}
uniformGrid.Children.Add(border2);
}
MoodMenuItems.Children.Add(uniformGrid);
MoodMenuItems.Children.Add(new System.Windows.Shapes.Rectangle
{
Height = 1.0,
Fill = fill,
Margin = new Thickness(8.0, 4.0, 8.0, 4.0),
Opacity = 0.4
});
StackPanel stackPanel6 = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel6.Children.Add(new TextBlock
{
Text = "\ue710",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 13.0,
Foreground = foreground2,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(4.0, 0.0, 8.0, 0.0)
});
stackPanel6.Children.Add(new TextBlock
{
Text = "커스텀 무드 추가",
FontSize = 13.0,
Foreground = foreground2,
VerticalAlignment = VerticalAlignment.Center
});
Border border3 = new Border
{
Child = stackPanel6,
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(8.0),
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(8.0, 6.0, 12.0, 6.0)
};
ApplyMenuItemHover(border3);
border3.MouseLeftButtonUp += delegate
{
MoodMenuPopup.IsOpen = false;
ShowCustomMoodDialog();
};
MoodMenuItems.Children.Add(border3);
if (FindName("BtnMoodMenu") is UIElement placementTarget)
{
MoodMenuPopup.PlacementTarget = placementTarget;
}
MoodMenuPopup.IsOpen = true;
}
private void ShowCustomMoodDialog(CustomMoodEntry? existing = null)
{
bool flag = existing != null;
CustomMoodDialog customMoodDialog = new CustomMoodDialog(existing?.Key ?? "", existing?.Label ?? "", existing?.Icon ?? "\ud83c\udfaf", existing?.Description ?? "", existing?.Css ?? "")
{
Owner = this
};
if (customMoodDialog.ShowDialog() == true)
{
if (flag)
{
existing.Label = customMoodDialog.MoodLabel;
existing.Icon = customMoodDialog.MoodIcon;
existing.Description = customMoodDialog.MoodDescription;
existing.Css = customMoodDialog.MoodCss;
}
else
{
_settings.Settings.Llm.CustomMoods.Add(new CustomMoodEntry
{
Key = customMoodDialog.MoodKey,
Label = customMoodDialog.MoodLabel,
Icon = customMoodDialog.MoodIcon,
Description = customMoodDialog.MoodDescription,
Css = customMoodDialog.MoodCss
});
}
_settings.Save();
TemplateService.LoadCustomMoods(_settings.Settings.Llm.CustomMoods);
BuildBottomBar();
}
}
private void ShowCustomMoodContextMenu(Border? anchor, string moodKey)
{
if (anchor == null)
{
return;
}
Popup popup = new Popup
{
PlacementTarget = anchor,
Placement = PlacementMode.Right,
StaysOpen = false,
AllowsTransparency = true
};
System.Windows.Media.Brush background = (TryFindResource("LauncherBackground") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Black;
System.Windows.Media.Brush fg = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush secondaryFg = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush borderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
Border border = new Border
{
Background = background,
CornerRadius = new CornerRadius(10.0),
BorderBrush = borderBrush,
BorderThickness = new Thickness(1.0),
Padding = new Thickness(4.0),
MinWidth = 120.0,
Effect = new DropShadowEffect
{
BlurRadius = 12.0,
ShadowDepth = 2.0,
Opacity = 0.3,
Color = Colors.Black
}
};
StackPanel stackPanel = new StackPanel();
Border border2 = CreateContextMenuItem("\ue70f", "편집", fg, secondaryFg);
border2.MouseLeftButtonDown += delegate
{
popup.IsOpen = false;
CustomMoodEntry customMoodEntry = _settings.Settings.Llm.CustomMoods.FirstOrDefault((CustomMoodEntry c) => c.Key == moodKey);
if (customMoodEntry != null)
{
ShowCustomMoodDialog(customMoodEntry);
}
};
stackPanel.Children.Add(border2);
Border border3 = CreateContextMenuItem("\ue74d", "삭제", new SolidColorBrush(System.Windows.Media.Color.FromRgb(239, 68, 68)), secondaryFg);
border3.MouseLeftButtonDown += delegate
{
popup.IsOpen = false;
MessageBoxResult messageBoxResult = CustomMessageBox.Show("이 디자인 무드를 삭제하시겠습니까?", "무드 삭제", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (messageBoxResult == MessageBoxResult.Yes)
{
_settings.Settings.Llm.CustomMoods.RemoveAll((CustomMoodEntry c) => c.Key == moodKey);
if (_selectedMood == moodKey)
{
_selectedMood = "modern";
}
_settings.Save();
TemplateService.LoadCustomMoods(_settings.Settings.Llm.CustomMoods);
BuildBottomBar();
}
};
stackPanel.Children.Add(border3);
border.Child = stackPanel;
popup.Child = border;
popup.IsOpen = true;
}
private void ShowPlaceholder()
{
if (!string.IsNullOrEmpty(_promptCardPlaceholder))
{
InputWatermark.Text = _promptCardPlaceholder;
InputWatermark.Visibility = Visibility.Visible;
InputBox.Text = "";
InputBox.Focus();
}
}
private void UpdateWatermarkVisibility()
{
if (_activeSlashCmd != null)
{
InputWatermark.Visibility = Visibility.Collapsed;
}
else if (_promptCardPlaceholder != null && string.IsNullOrEmpty(InputBox.Text))
{
InputWatermark.Visibility = Visibility.Visible;
}
else
{
InputWatermark.Visibility = Visibility.Collapsed;
}
}
private void ClearPromptCardPlaceholder()
{
_promptCardPlaceholder = null;
InputWatermark.Visibility = Visibility.Collapsed;
}
private void BtnSettings_Click(object sender, RoutedEventArgs e)
{
if (System.Windows.Application.Current is App app)
{
app.OpenSettingsFromChat();
}
}
private void BtnTemplateSelector_Click(object sender, RoutedEventArgs e)
{
List<PromptTemplate> promptTemplates = _settings.Settings.Llm.PromptTemplates;
TemplateItems.Items.Clear();
if (promptTemplates == null || promptTemplates.Count == 0)
{
TemplateEmptyHint.Visibility = Visibility.Visible;
TemplatePopup.IsOpen = true;
return;
}
TemplateEmptyHint.Visibility = Visibility.Collapsed;
foreach (PromptTemplate item in promptTemplates)
{
Border border = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(8.0),
Padding = new Thickness(10.0, 8.0, 10.0, 8.0),
Margin = new Thickness(2.0),
Cursor = System.Windows.Input.Cursors.Hand,
Tag = item.Content
};
StackPanel stackPanel = new StackPanel();
stackPanel.Children.Add(new TextBlock
{
Text = item.Name,
FontSize = 13.0,
FontWeight = FontWeights.SemiBold,
Foreground = (System.Windows.Media.Brush)FindResource("PrimaryText")
});
string text = ((item.Content.Length > 60) ? (item.Content.Substring(0, 60) + "…") : item.Content);
stackPanel.Children.Add(new TextBlock
{
Text = text,
FontSize = 11.0,
Foreground = (System.Windows.Media.Brush)FindResource("SecondaryText"),
TextTrimming = TextTrimming.CharacterEllipsis,
Margin = new Thickness(0.0, 2.0, 0.0, 0.0)
});
border.Child = stackPanel;
border.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = (System.Windows.Media.Brush)FindResource("ItemBackground");
}
};
border.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = System.Windows.Media.Brushes.Transparent;
}
};
border.MouseLeftButtonUp += delegate(object s, MouseButtonEventArgs _)
{
if (s is Border { Tag: string tag })
{
InputBox.Text = tag;
InputBox.CaretIndex = InputBox.Text.Length;
InputBox.Focus();
TemplatePopup.IsOpen = false;
}
};
TemplateItems.Items.Add(border);
}
TemplatePopup.IsOpen = true;
}
private string GetCurrentModelDisplayName()
{
LlmSettings llm = _settings.Settings.Llm;
string text = llm.Service.ToLowerInvariant();
if ((text == "ollama" || text == "vllm") ? true : false)
{
RegisteredModel registeredModel = llm.RegisteredModels.FirstOrDefault((RegisteredModel rm) => rm.EncryptedModelName == llm.Model);
if (registeredModel != null)
{
return registeredModel.Alias;
}
return string.IsNullOrEmpty(llm.Model) ? "(미설정)" : "••••";
}
if (text == "gemini")
{
return GeminiModels.FirstOrDefault(((string Id, string Label) g) => g.Id == llm.Model).Label ?? llm.Model;
}
if (text == "claude")
{
return ClaudeModels.FirstOrDefault(((string Id, string Label) c) => c.Id == llm.Model).Label ?? llm.Model;
}
return string.IsNullOrEmpty(llm.Model) ? "(미설정)" : llm.Model;
}
private void UpdateModelLabel()
{
string text = _settings.Settings.Llm.Service.ToLowerInvariant();
if (1 == 0)
{
}
string text2 = text switch
{
"gemini" => "Gemini",
"claude" => "Claude",
"vllm" => "vLLM",
_ => "Ollama",
};
if (1 == 0)
{
}
string text3 = text2;
ModelLabel.Text = text3 + " · " + GetCurrentModelDisplayName();
}
private void BtnModelSelector_Click(object sender, RoutedEventArgs e)
{
LlmSettings llm = _settings.Settings.Llm;
string originalService = llm.Service;
string originalModel = llm.Model;
bool modelConfirmed = false;
System.Windows.Media.Brush background = (TryFindResource("LauncherBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromRgb(26, 27, 46));
System.Windows.Media.Brush borderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush primaryText = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush secondaryText = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush hoverBg = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromArgb(30, byte.MaxValue, byte.MaxValue, byte.MaxValue));
SolidColorBrush checkColor = new SolidColorBrush(System.Windows.Media.Color.FromRgb(56, 161, 105));
SolidColorBrush solidColorBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, 75, 94, 252));
Popup popup = new Popup
{
StaysOpen = false,
AllowsTransparency = true,
PopupAnimation = PopupAnimation.Fade,
PlacementTarget = BtnModelSelector,
Placement = PlacementMode.Top
};
Border border = new Border
{
Background = background,
BorderBrush = borderBrush,
BorderThickness = new Thickness(1.0),
CornerRadius = new CornerRadius(12.0),
Padding = new Thickness(6.0),
MinWidth = 240.0,
MaxHeight = 460.0,
Effect = new DropShadowEffect
{
BlurRadius = 16.0,
ShadowDepth = 4.0,
Opacity = 0.3,
Color = Colors.Black
}
};
ScrollViewer scrollViewer = new ScrollViewer
{
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
MaxHeight = 440.0
};
StackPanel stackPanel = new StackPanel();
StackPanel modelSection = new StackPanel();
stackPanel.Children.Add(new TextBlock
{
Text = "서비스",
FontSize = 11.0,
Foreground = secondaryText,
Margin = new Thickness(10.0, 4.0, 0.0, 4.0),
FontWeight = FontWeights.SemiBold
});
StackPanel serviceItems = new StackPanel();
(string, string)[] services = new(string, string)[4]
{
("ollama", "Ollama"),
("vllm", "vLLM"),
("gemini", "Gemini"),
("claude", "Claude")
};
BuildServiceItems();
stackPanel.Children.Add(serviceItems);
stackPanel.Children.Add(CreateSeparator());
RebuildModelList(llm.Service.ToLowerInvariant());
stackPanel.Children.Add(modelSection);
popup.Closed += delegate
{
if (!modelConfirmed)
{
_settings.Settings.Llm.Service = originalService;
_settings.Settings.Llm.Model = originalModel;
UpdateModelLabel();
}
};
scrollViewer.Content = stackPanel;
border.Child = scrollViewer;
popup.Child = border;
popup.IsOpen = true;
void BuildServiceItems()
{
serviceItems.Children.Clear();
string text = llm.Service.ToLowerInvariant();
(string, string)[] array = services;
for (int i = 0; i < array.Length; i++)
{
(string, string) tuple = array[i];
string item = tuple.Item1;
string item2 = tuple.Item2;
string capturedSvc = item;
serviceItems.Children.Add(CreateMenuItem(item2, text == item, delegate
{
_settings.Settings.Llm.Service = capturedSvc;
UpdateModelLabel();
BuildServiceItems();
RebuildModelList(capturedSvc);
}, closeOnClick: false));
}
}
Border CreateMenuItem(string text, bool isChecked, Action onClick, bool closeOnClick = true)
{
//IL_021d: Unknown result type (might be due to invalid IL or missing references)
Border item = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(8.0),
Padding = new Thickness(10.0, 7.0, 10.0, 7.0),
Margin = new Thickness(0.0, 1.0, 0.0, 1.0),
Cursor = System.Windows.Input.Cursors.Hand
};
Grid grid = new Grid
{
ColumnDefinitions =
{
new ColumnDefinition
{
Width = new GridLength(1.0, GridUnitType.Star)
},
new ColumnDefinition
{
Width = new GridLength(20.0)
}
}
};
TextBlock element = new TextBlock
{
Text = text,
FontSize = 13.0,
Foreground = primaryText,
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(element, 0);
grid.Children.Add(element);
if (isChecked)
{
Canvas canvas = new Canvas
{
Width = 14.0,
Height = 14.0,
VerticalAlignment = VerticalAlignment.Center
};
canvas.Children.Add(new System.Windows.Shapes.Path
{
Data = Geometry.Parse("M 2 7 L 5.5 10.5 L 12 4"),
Stroke = checkColor,
StrokeThickness = 2.0,
StrokeStartLineCap = PenLineCap.Round,
StrokeEndLineCap = PenLineCap.Round,
StrokeLineJoin = PenLineJoin.Round
});
Grid.SetColumn(canvas, 1);
grid.Children.Add(canvas);
}
item.Child = grid;
item.RenderTransformOrigin = new Point(0.5, 0.5);
item.RenderTransform = new ScaleTransform(1.0, 1.0);
item.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = hoverBg;
}
ScaleTransform scaleTransform = item.RenderTransform as ScaleTransform;
scaleTransform?.BeginAnimation(ScaleTransform.ScaleXProperty, new DoubleAnimation(1.02, TimeSpan.FromMilliseconds(120.0)));
scaleTransform?.BeginAnimation(ScaleTransform.ScaleYProperty, new DoubleAnimation(1.02, TimeSpan.FromMilliseconds(120.0)));
};
item.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = System.Windows.Media.Brushes.Transparent;
}
ScaleTransform scaleTransform = item.RenderTransform as ScaleTransform;
scaleTransform?.BeginAnimation(ScaleTransform.ScaleXProperty, new DoubleAnimation(1.0, TimeSpan.FromMilliseconds(150.0)));
scaleTransform?.BeginAnimation(ScaleTransform.ScaleYProperty, new DoubleAnimation(1.0, TimeSpan.FromMilliseconds(150.0)));
};
item.MouseLeftButtonUp += delegate
{
if (closeOnClick)
{
popup.IsOpen = false;
}
onClick();
};
return item;
}
Border CreateSeparator()
{
return new Border
{
Height = 1.0,
Background = borderBrush,
Opacity = 0.3,
Margin = new Thickness(8.0, 4.0, 8.0, 4.0)
};
}
void RebuildModelList(string service)
{
modelSection.Children.Clear();
modelSection.Children.Add(new TextBlock
{
Text = "모델",
FontSize = 11.0,
Foreground = secondaryText,
Margin = new Thickness(10.0, 4.0, 0.0, 4.0),
FontWeight = FontWeights.SemiBold
});
string text = service;
if ((text == "ollama" || text == "vllm") ? true : false)
{
List<RegisteredModel> list = llm.RegisteredModels.Where((RegisteredModel rm) => rm.Service == service).ToList();
if (list.Count != 0)
{
foreach (RegisteredModel item5 in list)
{
string capturedEnc = item5.EncryptedModelName;
modelSection.Children.Add(CreateMenuItem(item5.Alias, llm.Model == item5.EncryptedModelName, delegate
{
_settings.Settings.Llm.Model = capturedEnc;
_settings.Save();
modelConfirmed = true;
UpdateModelLabel();
}));
}
return;
}
modelSection.Children.Add(new TextBlock
{
Text = "등록된 모델 없음 — 설정에서 추가",
FontSize = 11.5,
Foreground = secondaryText,
FontStyle = FontStyles.Italic,
Margin = new Thickness(10.0, 4.0, 10.0, 4.0)
});
}
else if (service == "gemini")
{
(string, string)[] geminiModels = GeminiModels;
for (int num = 0; num < geminiModels.Length; num++)
{
(string, string) tuple = geminiModels[num];
string item = tuple.Item1;
string item2 = tuple.Item2;
string capturedId = item;
modelSection.Children.Add(CreateMenuItem(item2, llm.Model == item, delegate
{
_settings.Settings.Llm.Model = capturedId;
_settings.Save();
modelConfirmed = true;
UpdateModelLabel();
}));
}
}
else if (service == "claude")
{
(string, string)[] claudeModels = ClaudeModels;
for (int num2 = 0; num2 < claudeModels.Length; num2++)
{
(string, string) tuple2 = claudeModels[num2];
string item3 = tuple2.Item1;
string item4 = tuple2.Item2;
string capturedId2 = item3;
modelSection.Children.Add(CreateMenuItem(item4, llm.Model == item3, delegate
{
_settings.Settings.Llm.Model = capturedId2;
_settings.Save();
modelConfirmed = true;
UpdateModelLabel();
}));
}
}
}
}
private void BtnNewChat_Click(object sender, RoutedEventArgs e)
{
StartNewConversation();
InputBox.Focus();
}
public void ResumeConversation(string conversationId)
{
ChatConversation chatConversation = _storage.Load(conversationId);
if (chatConversation != null)
{
lock (_convLock)
{
_currentConversation = chatConversation;
}
UpdateChatTitle();
RefreshConversationList();
RenderMessages();
UpdateFolderBar();
}
InputBox.Focus();
}
public void StartNewAndFocus()
{
StartNewConversation();
InputBox.Focus();
}
private void BtnDeleteAll_Click(object sender, RoutedEventArgs e)
{
MessageBoxResult messageBoxResult = CustomMessageBox.Show("저장된 모든 대화 내역을 삭제하시겠습니까?\n이 작업은 되돌릴 수 없습니다.", "대화 전체 삭제", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if (messageBoxResult == MessageBoxResult.Yes)
{
_storage.DeleteAll();
lock (_convLock)
{
_currentConversation = null;
}
MessagePanel.Children.Clear();
EmptyState.Visibility = Visibility.Visible;
UpdateChatTitle();
RefreshConversationList();
}
}
private void TryShowPreview(string filePath)
{
if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
{
PreviewWindow.ShowPreview(filePath, _selectedMood);
}
}
private void ShowPreviewPanel(string filePath)
{
if (!_previewTabs.Contains<string>(filePath, StringComparer.OrdinalIgnoreCase))
{
_previewTabs.Add(filePath);
}
_activePreviewTab = filePath;
if (PreviewColumn.Width.Value < 100.0)
{
PreviewColumn.Width = new GridLength(420.0);
SplitterColumn.Width = new GridLength(5.0);
}
PreviewPanel.Visibility = Visibility.Visible;
PreviewSplitter.Visibility = Visibility.Visible;
BtnPreviewToggle.Visibility = Visibility.Visible;
RebuildPreviewTabs();
LoadPreviewContent(filePath);
}
private void RebuildPreviewTabs()
{
PreviewTabPanel.Children.Clear();
System.Windows.Media.Brush brush = (TryFindResource("AccentColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.CornflowerBlue;
System.Windows.Media.Brush brush2 = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush brush3 = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush background = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
foreach (string previewTab in _previewTabs)
{
string fileName = System.IO.Path.GetFileName(previewTab);
bool flag = string.Equals(previewTab, _activePreviewTab, StringComparison.OrdinalIgnoreCase);
Border border = new Border
{
Background = (flag ? new SolidColorBrush(System.Windows.Media.Color.FromArgb(21, byte.MaxValue, byte.MaxValue, byte.MaxValue)) : System.Windows.Media.Brushes.Transparent),
BorderBrush = (flag ? brush : System.Windows.Media.Brushes.Transparent),
BorderThickness = new Thickness(0.0, 0.0, 0.0, flag ? 2 : 0),
Padding = new Thickness(8.0, 6.0, 4.0, 6.0),
Cursor = System.Windows.Input.Cursors.Hand,
MaxWidth = ((_previewTabs.Count <= 3) ? 200 : ((_previewTabs.Count <= 5) ? 140 : 100))
};
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(new TextBlock
{
Text = fileName,
FontSize = 11.0,
Foreground = (flag ? brush3 : brush2),
FontWeight = (flag ? FontWeights.SemiBold : FontWeights.Normal),
VerticalAlignment = VerticalAlignment.Center,
TextTrimming = TextTrimming.CharacterEllipsis,
MaxWidth = border.MaxWidth - 30.0,
ToolTip = previewTab
});
System.Windows.Media.Brush closeFg = (flag ? brush3 : brush2);
TextBlock child = new TextBlock
{
Text = "\ue711",
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 10.0,
Foreground = closeFg,
VerticalAlignment = VerticalAlignment.Center
};
Border border2 = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(3.0),
Padding = new Thickness(3.0, 2.0, 3.0, 2.0),
Margin = new Thickness(5.0, 0.0, 0.0, 0.0),
Cursor = System.Windows.Input.Cursors.Hand,
VerticalAlignment = VerticalAlignment.Center,
Visibility = ((!flag) ? Visibility.Hidden : Visibility.Visible),
Child = child
};
string closePath = previewTab;
border2.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border3)
{
border3.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(64, byte.MaxValue, 80, 80));
if (border3.Child is TextBlock textBlock)
{
textBlock.Foreground = new SolidColorBrush(System.Windows.Media.Color.FromRgb(byte.MaxValue, 96, 96));
}
}
};
border2.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border3)
{
border3.Background = System.Windows.Media.Brushes.Transparent;
if (border3.Child is TextBlock textBlock)
{
textBlock.Foreground = closeFg;
}
}
};
border2.Tag = "close";
border2.MouseLeftButtonUp += delegate(object _, MouseButtonEventArgs e)
{
e.Handled = true;
ClosePreviewTab(closePath);
};
stackPanel.Children.Add(border2);
border.Child = stackPanel;
string clickPath = previewTab;
border.MouseLeftButtonUp += delegate(object _, MouseButtonEventArgs e)
{
if (!e.Handled)
{
e.Handled = true;
_activePreviewTab = clickPath;
RebuildPreviewTabs();
LoadPreviewContent(clickPath);
}
};
string ctxPath = previewTab;
border.MouseRightButtonUp += delegate(object _, MouseButtonEventArgs e)
{
e.Handled = true;
ShowPreviewTabContextMenu(ctxPath);
};
string dblPath = previewTab;
border.MouseLeftButtonDown += delegate(object _, MouseButtonEventArgs e)
{
if (!e.Handled && e.ClickCount == 2)
{
e.Handled = true;
OpenPreviewPopupWindow(dblPath);
}
};
bool capturedIsActive = flag;
Border capturedCloseBtn = border2;
border.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border3 && !capturedIsActive)
{
border3.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(16, byte.MaxValue, byte.MaxValue, byte.MaxValue));
}
if (!capturedIsActive)
{
capturedCloseBtn.Visibility = Visibility.Visible;
}
};
border.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border3 && !capturedIsActive)
{
border3.Background = System.Windows.Media.Brushes.Transparent;
}
if (!capturedIsActive)
{
capturedCloseBtn.Visibility = Visibility.Hidden;
}
};
PreviewTabPanel.Children.Add(border);
List<string> previewTabs = _previewTabs;
if (previewTab != previewTabs[previewTabs.Count - 1])
{
PreviewTabPanel.Children.Add(new Border
{
Width = 1.0,
Height = 14.0,
Background = background,
Margin = new Thickness(0.0, 4.0, 0.0, 4.0),
VerticalAlignment = VerticalAlignment.Center
});
}
}
}
private void ClosePreviewTab(string filePath)
{
_previewTabs.Remove(filePath);
if (_previewTabs.Count == 0)
{
HidePreviewPanel();
return;
}
if (string.Equals(filePath, _activePreviewTab, StringComparison.OrdinalIgnoreCase))
{
List<string> previewTabs = _previewTabs;
_activePreviewTab = previewTabs[previewTabs.Count - 1];
LoadPreviewContent(_activePreviewTab);
}
RebuildPreviewTabs();
}
private async void LoadPreviewContent(string filePath)
{
string ext = System.IO.Path.GetExtension(filePath).ToLowerInvariant();
PreviewWebView.Visibility = Visibility.Collapsed;
PreviewTextScroll.Visibility = Visibility.Collapsed;
PreviewDataGrid.Visibility = Visibility.Collapsed;
PreviewEmpty.Visibility = Visibility.Collapsed;
if (!File.Exists(filePath))
{
PreviewEmpty.Text = "파일을 찾을 수 없습니다";
PreviewEmpty.Visibility = Visibility.Visible;
return;
}
try
{
switch (ext)
{
case ".html":
case ".htm":
await EnsureWebViewInitializedAsync();
PreviewWebView.Source = new Uri(filePath);
PreviewWebView.Visibility = Visibility.Visible;
break;
case ".csv":
LoadCsvPreview(filePath);
PreviewDataGrid.Visibility = Visibility.Visible;
break;
case ".md":
{
await EnsureWebViewInitializedAsync();
string mdText = File.ReadAllText(filePath);
if (mdText.Length > 50000)
{
mdText = mdText.Substring(0, 50000);
}
string mdHtml = TemplateService.RenderMarkdownToHtml(mdText, _selectedMood);
PreviewWebView.NavigateToString(mdHtml);
PreviewWebView.Visibility = Visibility.Visible;
break;
}
case ".txt":
case ".json":
case ".xml":
case ".log":
{
string text = File.ReadAllText(filePath);
if (text.Length > 50000)
{
text = text.Substring(0, 50000) + "\n\n... (이후 생략)";
}
PreviewTextBlock.Text = text;
PreviewTextScroll.Visibility = Visibility.Visible;
break;
}
default:
PreviewEmpty.Text = "미리보기할 수 없는 파일 형식입니다";
PreviewEmpty.Visibility = Visibility.Visible;
break;
}
}
catch (Exception ex)
{
PreviewTextBlock.Text = "미리보기 오류: " + ex.Message;
PreviewTextScroll.Visibility = Visibility.Visible;
}
}
private async Task EnsureWebViewInitializedAsync()
{
if (_webViewInitialized)
{
return;
}
try
{
CoreWebView2Environment env = await CoreWebView2Environment.CreateAsync(null, WebView2DataFolder);
await PreviewWebView.EnsureCoreWebView2Async(env);
_webViewInitialized = true;
}
catch (Exception ex)
{
LogService.Warn("WebView2 초기화 실패: " + ex.Message);
}
}
private void LoadCsvPreview(string filePath)
{
try
{
string[] array = File.ReadAllLines(filePath);
if (array.Length == 0)
{
return;
}
DataTable dataTable = new DataTable();
string[] array2 = ParseCsvLine(array[0]);
string[] array3 = array2;
foreach (string columnName in array3)
{
dataTable.Columns.Add(columnName);
}
int num = Math.Min(array.Length, 501);
for (int j = 1; j < num; j++)
{
string[] array4 = ParseCsvLine(array[j]);
DataRow dataRow = dataTable.NewRow();
for (int k = 0; k < Math.Min(array4.Length, array2.Length); k++)
{
dataRow[k] = array4[k];
}
dataTable.Rows.Add(dataRow);
}
PreviewDataGrid.ItemsSource = dataTable.DefaultView;
}
catch (Exception ex)
{
PreviewTextBlock.Text = "CSV 로드 오류: " + ex.Message;
PreviewTextScroll.Visibility = Visibility.Visible;
PreviewDataGrid.Visibility = Visibility.Collapsed;
}
}
private static string[] ParseCsvLine(string line)
{
List<string> list = new List<string>();
StringBuilder stringBuilder = new StringBuilder();
bool flag = false;
for (int i = 0; i < line.Length; i++)
{
char c = line[i];
if (flag)
{
if (c == '"' && i + 1 < line.Length && line[i + 1] == '"')
{
stringBuilder.Append('"');
i++;
}
else if (c == '"')
{
flag = false;
}
else
{
stringBuilder.Append(c);
}
continue;
}
switch (c)
{
case '"':
flag = true;
break;
case ',':
list.Add(stringBuilder.ToString());
stringBuilder.Clear();
break;
default:
stringBuilder.Append(c);
break;
}
}
list.Add(stringBuilder.ToString());
return list.ToArray();
}
private void HidePreviewPanel()
{
_previewTabs.Clear();
_activePreviewTab = null;
PreviewColumn.Width = new GridLength(0.0);
SplitterColumn.Width = new GridLength(0.0);
PreviewPanel.Visibility = Visibility.Collapsed;
PreviewSplitter.Visibility = Visibility.Collapsed;
PreviewWebView.Visibility = Visibility.Collapsed;
PreviewTextScroll.Visibility = Visibility.Collapsed;
PreviewDataGrid.Visibility = Visibility.Collapsed;
try
{
if (_webViewInitialized)
{
PreviewWebView.CoreWebView2?.NavigateToString("<html></html>");
}
}
catch
{
}
}
private void PreviewTabBar_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if ((PreviewWebView.IsFocused || PreviewWebView.IsKeyboardFocusWithin) && sender is Border border)
{
border.Focus();
}
}
private void BtnClosePreview_Click(object sender, RoutedEventArgs e)
{
HidePreviewPanel();
BtnPreviewToggle.Visibility = Visibility.Collapsed;
}
private void BtnPreviewToggle_Click(object sender, RoutedEventArgs e)
{
if (PreviewPanel.Visibility == Visibility.Visible)
{
PreviewPanel.Visibility = Visibility.Collapsed;
PreviewSplitter.Visibility = Visibility.Collapsed;
PreviewColumn.Width = new GridLength(0.0);
SplitterColumn.Width = new GridLength(0.0);
}
else if (_previewTabs.Count > 0)
{
PreviewPanel.Visibility = Visibility.Visible;
PreviewSplitter.Visibility = Visibility.Visible;
PreviewColumn.Width = new GridLength(420.0);
SplitterColumn.Width = new GridLength(5.0);
RebuildPreviewTabs();
if (_activePreviewTab != null)
{
LoadPreviewContent(_activePreviewTab);
}
}
}
private void BtnOpenExternal_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(_activePreviewTab) || !File.Exists(_activePreviewTab))
{
return;
}
try
{
Process.Start(new ProcessStartInfo
{
FileName = _activePreviewTab,
UseShellExecute = true
});
}
catch (Exception ex)
{
Debug.WriteLine("외부 프로그램 실행 오류: " + ex.Message);
}
}
private void ShowPreviewTabContextMenu(string filePath)
{
if (_previewTabPopup != null)
{
_previewTabPopup.IsOpen = false;
}
System.Windows.Media.Brush background = (TryFindResource("LauncherBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromRgb(30, 30, 46));
System.Windows.Media.Brush borderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush primaryText = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush secondaryText = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush hoverBg = (TryFindResource("ItemHoverBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue));
StackPanel stack = new StackPanel();
AddItem("\ue8a7", "#64B5F6", "외부 프로그램으로 열기", delegate
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = filePath,
UseShellExecute = true
});
}
catch
{
}
});
AddItem("\ue838", "#FFB74D", "파일 위치 열기", delegate
{
try
{
Process.Start("explorer.exe", "/select,\"" + filePath + "\"");
}
catch
{
}
});
AddItem("\ue8a7", "#81C784", "별도 창에서 보기", delegate
{
OpenPreviewPopupWindow(filePath);
});
AddSeparator();
AddItem("\ue8c8", "", "경로 복사", delegate
{
try
{
System.Windows.Clipboard.SetText(filePath);
}
catch
{
}
});
AddSeparator();
AddItem("\ue711", "#EF5350", "이 탭 닫기", delegate
{
ClosePreviewTab(filePath);
});
if (_previewTabs.Count > 1)
{
AddItem("\ue8bb", "#EF5350", "다른 탭 모두 닫기", delegate
{
string keep = filePath;
_previewTabs.RemoveAll((string p) => !string.Equals(p, keep, StringComparison.OrdinalIgnoreCase));
_activePreviewTab = keep;
RebuildPreviewTabs();
LoadPreviewContent(keep);
});
}
Border child = new Border
{
Background = background,
BorderBrush = borderBrush,
BorderThickness = new Thickness(1.0),
CornerRadius = new CornerRadius(12.0),
Padding = new Thickness(4.0, 6.0, 4.0, 6.0),
MinWidth = 180.0,
Effect = new DropShadowEffect
{
BlurRadius = 16.0,
Opacity = 0.4,
ShadowDepth = 4.0,
Color = Colors.Black
},
Child = stack
};
_previewTabPopup = new Popup
{
Child = child,
Placement = PlacementMode.MousePoint,
StaysOpen = false,
AllowsTransparency = true,
PopupAnimation = PopupAnimation.Fade
};
_previewTabPopup.IsOpen = true;
void AddItem(string icon, string iconColor, string label, Action action)
{
Border border = new Border
{
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(6.0),
Padding = new Thickness(10.0, 7.0, 16.0, 7.0),
Cursor = System.Windows.Input.Cursors.Hand
};
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(new TextBlock
{
Text = icon,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 12.0,
Foreground = (string.IsNullOrEmpty(iconColor) ? secondaryText : new SolidColorBrush((System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(iconColor))),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 8.0, 0.0)
});
stackPanel.Children.Add(new TextBlock
{
Text = label,
FontSize = 13.0,
Foreground = primaryText,
VerticalAlignment = VerticalAlignment.Center
});
border.Child = stackPanel;
border.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = hoverBg;
}
};
border.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = System.Windows.Media.Brushes.Transparent;
}
};
border.MouseLeftButtonUp += delegate
{
_previewTabPopup.IsOpen = false;
action();
};
stack.Children.Add(border);
}
void AddSeparator()
{
stack.Children.Add(new Border
{
Height = 1.0,
Background = borderBrush,
Margin = new Thickness(8.0, 3.0, 8.0, 3.0)
});
}
}
private void OpenPreviewPopupWindow(string filePath)
{
if (!File.Exists(filePath))
{
return;
}
string text = System.IO.Path.GetExtension(filePath).ToLowerInvariant();
string fileName = System.IO.Path.GetFileName(filePath);
System.Windows.Media.Brush background = (TryFindResource("LauncherBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromRgb(30, 30, 46));
System.Windows.Media.Brush foreground = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
Window window = new Window
{
Title = "미리보기 — " + fileName,
Width = 900.0,
Height = 700.0,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
Background = background
};
FrameworkElement content;
switch (text)
{
case ".html":
case ".htm":
{
WebView2 wv = new WebView2();
wv.Loaded += async delegate
{
try
{
CoreWebView2Environment env = await CoreWebView2Environment.CreateAsync(null, WebView2DataFolder);
await wv.EnsureCoreWebView2Async(env);
wv.Source = new Uri(filePath);
}
catch
{
}
};
content = wv;
break;
}
case ".md":
{
WebView2 mdWv = new WebView2();
string mdMood = _selectedMood;
mdWv.Loaded += async delegate
{
try
{
CoreWebView2Environment env = await CoreWebView2Environment.CreateAsync(null, WebView2DataFolder);
await mdWv.EnsureCoreWebView2Async(env);
string mdSrc = File.ReadAllText(filePath);
if (mdSrc.Length > 100000)
{
mdSrc = mdSrc.Substring(0, 100000);
}
string html = TemplateService.RenderMarkdownToHtml(mdSrc, mdMood);
mdWv.NavigateToString(html);
}
catch
{
}
};
content = mdWv;
break;
}
case ".csv":
{
DataGrid dataGrid = new DataGrid
{
AutoGenerateColumns = true,
IsReadOnly = true,
Background = System.Windows.Media.Brushes.Transparent,
Foreground = System.Windows.Media.Brushes.White,
BorderThickness = new Thickness(0.0),
FontSize = 12.0
};
try
{
string[] array = File.ReadAllLines(filePath);
if (array.Length != 0)
{
DataTable dataTable = new DataTable();
string[] array2 = ParseCsvLine(array[0]);
string[] array3 = array2;
foreach (string columnName in array3)
{
dataTable.Columns.Add(columnName);
}
for (int num2 = 1; num2 < Math.Min(array.Length, 1001); num2++)
{
string[] array4 = ParseCsvLine(array[num2]);
DataRow dataRow = dataTable.NewRow();
for (int num3 = 0; num3 < Math.Min(array4.Length, dataTable.Columns.Count); num3++)
{
dataRow[num3] = array4[num3];
}
dataTable.Rows.Add(dataRow);
}
dataGrid.ItemsSource = dataTable.DefaultView;
}
}
catch
{
}
content = dataGrid;
break;
}
default:
{
string text2 = File.ReadAllText(filePath);
if (text2.Length > 100000)
{
text2 = text2.Substring(0, 100000) + "\n\n... (이후 생략)";
}
ScrollViewer scrollViewer = new ScrollViewer
{
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
Padding = new Thickness(20.0),
Content = new TextBlock
{
Text = text2,
TextWrapping = TextWrapping.Wrap,
FontFamily = new System.Windows.Media.FontFamily("Consolas"),
FontSize = 13.0,
Foreground = foreground
}
};
content = scrollViewer;
break;
}
}
window.Content = content;
window.Show();
}
private void UpdateAgentProgressBar(AgentEvent evt)
{
switch (evt.Type)
{
case AgentEventType.Planning:
{
List<string> steps = evt.Steps;
if (steps != null && steps.Count > 0)
{
ShowStickyProgress(evt.Steps.Count);
}
break;
}
case AgentEventType.StepStart:
if (evt.StepTotal > 0)
{
UpdateStickyProgress(evt.StepCurrent, evt.StepTotal, evt.Summary);
}
break;
case AgentEventType.Complete:
HideStickyProgress();
break;
}
}
private void ShowStickyProgress(int totalSteps)
{
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Expected O, but got Unknown
_progressStartTime = DateTime.Now;
AgentProgressBar.Visibility = Visibility.Visible;
ProgressIcon.Text = "\ue768";
ProgressStepLabel.Text = $"작업 준비 중... (0/{totalSteps})";
ProgressPercent.Text = "0%";
ProgressElapsed.Text = "0:00";
ProgressFill.Width = 0.0;
DispatcherTimer? progressElapsedTimer = _progressElapsedTimer;
if (progressElapsedTimer != null)
{
progressElapsedTimer.Stop();
}
_progressElapsedTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1.0)
};
_progressElapsedTimer.Tick += delegate
{
TimeSpan timeSpan = DateTime.Now - _progressStartTime;
ProgressElapsed.Text = ((timeSpan.TotalHours >= 1.0) ? timeSpan.ToString("h\\:mm\\:ss") : timeSpan.ToString("m\\:ss"));
};
_progressElapsedTimer.Start();
}
private void UpdateStickyProgress(int currentStep, int totalSteps, string stepDescription)
{
if (AgentProgressBar.Visibility == Visibility.Visible)
{
double num = ((totalSteps > 0) ? ((double)currentStep / (double)totalSteps) : 0.0);
ProgressStepLabel.Text = $"{stepDescription} ({currentStep}/{totalSteps})";
ProgressPercent.Text = $"{(int)(num * 100.0)}%";
if (ProgressFill.Parent is Border border)
{
double toValue = border.ActualWidth * num;
DoubleAnimation animation = new DoubleAnimation(ProgressFill.Width, toValue, TimeSpan.FromMilliseconds(300.0))
{
EasingFunction = new QuadraticEase()
};
ProgressFill.BeginAnimation(FrameworkElement.WidthProperty, animation);
}
}
}
private void HideStickyProgress()
{
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00ea: Expected O, but got Unknown
DispatcherTimer? progressElapsedTimer = _progressElapsedTimer;
if (progressElapsedTimer != null)
{
progressElapsedTimer.Stop();
}
_progressElapsedTimer = null;
if (AgentProgressBar.Visibility != Visibility.Visible)
{
return;
}
ProgressIcon.Text = "\ue930";
ProgressStepLabel.Text = "작업 완료";
ProgressPercent.Text = "100%";
if (ProgressFill.Parent is Border border)
{
DoubleAnimation animation = new DoubleAnimation(ProgressFill.Width, border.ActualWidth, TimeSpan.FromMilliseconds(200.0));
ProgressFill.BeginAnimation(FrameworkElement.WidthProperty, animation);
}
DispatcherTimer hideTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(3.0)
};
hideTimer.Tick += delegate
{
hideTimer.Stop();
DoubleAnimation doubleAnimation = new DoubleAnimation(1.0, 0.0, TimeSpan.FromMilliseconds(300.0));
doubleAnimation.Completed += delegate
{
AgentProgressBar.Visibility = Visibility.Collapsed;
AgentProgressBar.Opacity = 1.0;
ProgressFill.BeginAnimation(FrameworkElement.WidthProperty, null);
ProgressFill.Width = 0.0;
};
AgentProgressBar.BeginAnimation(UIElement.OpacityProperty, doubleAnimation);
};
hideTimer.Start();
}
private void ToggleFileBrowser()
{
if (FileBrowserPanel.Visibility == Visibility.Visible)
{
FileBrowserPanel.Visibility = Visibility.Collapsed;
_settings.Settings.Llm.ShowFileBrowser = false;
}
else
{
FileBrowserPanel.Visibility = Visibility.Visible;
_settings.Settings.Llm.ShowFileBrowser = true;
BuildFileTree();
}
_settings.Save();
}
private void BtnFileBrowserRefresh_Click(object sender, RoutedEventArgs e)
{
BuildFileTree();
}
private void BtnFileBrowserOpenFolder_Click(object sender, RoutedEventArgs e)
{
string currentWorkFolder = GetCurrentWorkFolder();
if (string.IsNullOrEmpty(currentWorkFolder) || !Directory.Exists(currentWorkFolder))
{
return;
}
try
{
Process.Start(new ProcessStartInfo
{
FileName = currentWorkFolder,
UseShellExecute = true
});
}
catch
{
}
}
private void BtnFileBrowserClose_Click(object sender, RoutedEventArgs e)
{
FileBrowserPanel.Visibility = Visibility.Collapsed;
}
private void BuildFileTree()
{
FileTreeView.Items.Clear();
string currentWorkFolder = GetCurrentWorkFolder();
if (string.IsNullOrEmpty(currentWorkFolder) || !Directory.Exists(currentWorkFolder))
{
FileTreeView.Items.Add(new TreeViewItem
{
Header = "작업 폴더를 선택하세요",
IsEnabled = false
});
}
else
{
FileBrowserTitle.Text = "파일 탐색기 — " + System.IO.Path.GetFileName(currentWorkFolder);
int count = 0;
PopulateDirectory(new DirectoryInfo(currentWorkFolder), FileTreeView.Items, 0, ref count);
}
}
private void PopulateDirectory(DirectoryInfo dir, ItemCollection items, int depth, ref int count)
{
if (depth > 4 || count > 200)
{
return;
}
try
{
foreach (DirectoryInfo item in from d in dir.GetDirectories()
orderby d.Name
select d)
{
if (count > 200)
{
break;
}
if (_ignoredDirs.Contains(item.Name) || item.Name.StartsWith('.'))
{
continue;
}
count++;
TreeViewItem treeViewItem = new TreeViewItem
{
Header = CreateFileTreeHeader("\ued25", item.Name, null),
Tag = item.FullName,
IsExpanded = (depth < 1)
};
if (depth < 3)
{
treeViewItem.Items.Add(new TreeViewItem
{
Header = "로딩 중..."
});
DirectoryInfo capturedDir = item;
int capturedDepth = depth;
treeViewItem.Expanded += delegate(object s, RoutedEventArgs _)
{
if (s is TreeViewItem treeViewItem3 && treeViewItem3.Items.Count == 1 && treeViewItem3.Items[0] is TreeViewItem { Header: var header } && header?.ToString() == "로딩 중...")
{
treeViewItem3.Items.Clear();
int count2 = 0;
PopulateDirectory(capturedDir, treeViewItem3.Items, capturedDepth + 1, ref count2);
}
};
}
else
{
PopulateDirectory(item, treeViewItem.Items, depth + 1, ref count);
}
items.Add(treeViewItem);
}
}
catch
{
}
try
{
foreach (FileInfo item2 in from f in dir.GetFiles()
orderby f.Name
select f)
{
if (count > 200)
{
break;
}
count++;
string ext = item2.Extension.ToLowerInvariant();
string fileIcon = GetFileIcon(ext);
string sizeText = FormatFileSize(item2.Length);
TreeViewItem treeViewItem2 = new TreeViewItem
{
Header = CreateFileTreeHeader(fileIcon, item2.Name, sizeText),
Tag = item2.FullName
};
string capturedPath = item2.FullName;
treeViewItem2.MouseDoubleClick += delegate(object s, MouseButtonEventArgs e)
{
e.Handled = true;
TryShowPreview(capturedPath);
};
treeViewItem2.MouseRightButtonUp += delegate(object s, MouseButtonEventArgs e)
{
e.Handled = true;
if (s is TreeViewItem treeViewItem3)
{
treeViewItem3.IsSelected = true;
}
ShowFileTreeContextMenu(capturedPath);
};
items.Add(treeViewItem2);
}
}
catch
{
}
}
private static StackPanel CreateFileTreeHeader(string icon, string name, string? sizeText)
{
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(new TextBlock
{
Text = icon,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 11.0,
Foreground = new SolidColorBrush(System.Windows.Media.Color.FromRgb(156, 163, 175)),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 5.0, 0.0)
});
stackPanel.Children.Add(new TextBlock
{
Text = name,
FontSize = 11.5,
VerticalAlignment = VerticalAlignment.Center
});
if (sizeText != null)
{
stackPanel.Children.Add(new TextBlock
{
Text = " " + sizeText,
FontSize = 10.0,
Foreground = new SolidColorBrush(System.Windows.Media.Color.FromRgb(107, 114, 128)),
VerticalAlignment = VerticalAlignment.Center
});
}
return stackPanel;
}
private static string GetFileIcon(string ext)
{
if (1 == 0)
{
}
string result;
switch (ext)
{
case ".html":
case ".htm":
result = "\ueb41";
break;
case ".xlsx":
case ".xls":
result = "\ue9f9";
break;
case ".docx":
case ".doc":
result = "\ue8a5";
break;
case ".pdf":
result = "\uea90";
break;
case ".csv":
result = "\ue80a";
break;
case ".md":
result = "\ue70b";
break;
case ".json":
case ".xml":
result = "\ue943";
break;
case ".png":
case ".jpg":
case ".jpeg":
case ".gif":
case ".svg":
case ".webp":
result = "\ueb9f";
break;
case ".cs":
case ".py":
case ".js":
case ".ts":
case ".java":
case ".cpp":
result = "\ue943";
break;
case ".bat":
case ".cmd":
case ".ps1":
case ".sh":
result = "\ue756";
break;
case ".txt":
case ".log":
result = "\ue8a5";
break;
default:
result = "\ue7c3";
break;
}
if (1 == 0)
{
}
return result;
}
private static string FormatFileSize(long bytes)
{
if (1 == 0)
{
}
string result = ((bytes < 1024) ? $"{bytes} B" : ((bytes >= 1048576) ? $"{(double)bytes / 1048576.0:F1} MB" : $"{(double)bytes / 1024.0:F1} KB"));
if (1 == 0)
{
}
return result;
}
private void ShowFileTreeContextMenu(string filePath)
{
System.Windows.Media.Brush background = (TryFindResource("LauncherBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromRgb(30, 30, 46));
System.Windows.Media.Brush borderBrush = (TryFindResource("BorderColor") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush primaryText = (TryFindResource("PrimaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.White;
System.Windows.Media.Brush secondaryText = (TryFindResource("SecondaryText") as System.Windows.Media.Brush) ?? System.Windows.Media.Brushes.Gray;
System.Windows.Media.Brush hoverBg = (TryFindResource("HintBackground") as System.Windows.Media.Brush) ?? new SolidColorBrush(System.Windows.Media.Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue));
SolidColorBrush solidColorBrush = new SolidColorBrush(System.Windows.Media.Color.FromRgb(231, 76, 60));
Popup popup = new Popup
{
StaysOpen = false,
AllowsTransparency = true,
PopupAnimation = PopupAnimation.Fade,
Placement = PlacementMode.MousePoint
};
StackPanel panel = new StackPanel
{
Margin = new Thickness(2.0)
};
Border child = new Border
{
Background = background,
BorderBrush = borderBrush,
BorderThickness = new Thickness(1.0),
CornerRadius = new CornerRadius(10.0),
Padding = new Thickness(6.0),
MinWidth = 200.0,
Effect = new DropShadowEffect
{
BlurRadius = 16.0,
ShadowDepth = 4.0,
Opacity = 0.3,
Color = Colors.Black,
Direction = 270.0
},
Child = panel
};
popup.Child = child;
string item = System.IO.Path.GetExtension(filePath).ToLowerInvariant();
if (_previewableExtensions.Contains(item))
{
AddItem("\ue8a1", "미리보기", delegate
{
ShowPreviewPanel(filePath);
});
}
AddItem("\ue8a7", "외부 프로그램으로 열기", delegate
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = filePath,
UseShellExecute = true
});
}
catch
{
}
});
AddItem("\ued25", "폴더에서 보기", delegate
{
try
{
Process.Start("explorer.exe", "/select,\"" + filePath + "\"");
}
catch
{
}
});
AddItem("\ue8c8", "경로 복사", delegate
{
try
{
System.Windows.Clipboard.SetText(filePath);
ShowToast("경로 복사됨");
}
catch
{
}
});
AddSep();
AddItem("\ue8ac", "이름 변경", delegate
{
string path = System.IO.Path.GetDirectoryName(filePath) ?? "";
string fileName = System.IO.Path.GetFileName(filePath);
InputDialog inputDialog = new InputDialog("이름 변경", "새 파일 이름:", fileName)
{
Owner = this
};
if (inputDialog.ShowDialog() == true && !string.IsNullOrWhiteSpace(inputDialog.ResponseText))
{
string destFileName = System.IO.Path.Combine(path, inputDialog.ResponseText.Trim());
try
{
File.Move(filePath, destFileName);
BuildFileTree();
ShowToast("이름 변경: " + inputDialog.ResponseText.Trim());
}
catch (Exception ex)
{
ShowToast("이름 변경 실패: " + ex.Message, "\ue783");
}
}
});
AddItem("\ue74d", "삭제", delegate
{
MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show("파일을 삭제하시겠습니까?\n" + System.IO.Path.GetFileName(filePath), "파일 삭제 확인", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if (messageBoxResult == MessageBoxResult.Yes)
{
try
{
File.Delete(filePath);
BuildFileTree();
ShowToast("파일 삭제됨");
}
catch (Exception ex)
{
ShowToast("삭제 실패: " + ex.Message, "\ue783");
}
}
}, solidColorBrush, solidColorBrush);
((DispatcherObject)this).Dispatcher.BeginInvoke((Delegate)(Action)delegate
{
popup.IsOpen = true;
}, (DispatcherPriority)5, Array.Empty<object>());
void AddItem(string icon, string label, Action action, System.Windows.Media.Brush? labelColor = null, System.Windows.Media.Brush? iconColor = null)
{
StackPanel stackPanel = new StackPanel
{
Orientation = System.Windows.Controls.Orientation.Horizontal
};
stackPanel.Children.Add(new TextBlock
{
Text = icon,
FontFamily = new System.Windows.Media.FontFamily("Segoe MDL2 Assets"),
FontSize = 13.0,
Foreground = (iconColor ?? secondaryText),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0.0, 0.0, 10.0, 0.0)
});
stackPanel.Children.Add(new TextBlock
{
Text = label,
FontSize = 12.5,
Foreground = (labelColor ?? primaryText),
VerticalAlignment = VerticalAlignment.Center
});
Border border = new Border
{
Child = stackPanel,
Background = System.Windows.Media.Brushes.Transparent,
CornerRadius = new CornerRadius(7.0),
Cursor = System.Windows.Input.Cursors.Hand,
Padding = new Thickness(10.0, 8.0, 14.0, 8.0),
Margin = new Thickness(0.0, 1.0, 0.0, 1.0)
};
border.MouseEnter += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = hoverBg;
}
};
border.MouseLeave += delegate(object s, System.Windows.Input.MouseEventArgs _)
{
if (s is Border border2)
{
border2.Background = System.Windows.Media.Brushes.Transparent;
}
};
border.MouseLeftButtonUp += delegate
{
popup.IsOpen = false;
action();
};
panel.Children.Add(border);
}
void AddSep()
{
panel.Children.Add(new Border
{
Height = 1.0,
Margin = new Thickness(10.0, 4.0, 10.0, 4.0),
Background = borderBrush,
Opacity = 0.3
});
}
}
private void RefreshFileTreeIfVisible()
{
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Expected O, but got Unknown
if (FileBrowserPanel.Visibility == Visibility.Visible)
{
DispatcherTimer? fileBrowserRefreshTimer = _fileBrowserRefreshTimer;
if (fileBrowserRefreshTimer != null)
{
fileBrowserRefreshTimer.Stop();
}
_fileBrowserRefreshTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(500.0)
};
_fileBrowserRefreshTimer.Tick += delegate
{
_fileBrowserRefreshTimer.Stop();
BuildFileTree();
};
_fileBrowserRefreshTimer.Start();
}
}
private void UpdateStatusBar(AgentEvent evt)
{
string toolName = evt.ToolName;
if (1 == 0)
{
}
string text;
switch (toolName)
{
case "sub_agent":
text = "서브에이전트";
break;
case "file_read":
case "document_read":
text = "파일 읽기";
break;
case "file_write":
text = "파일 쓰기";
break;
case "file_edit":
text = "파일 수정";
break;
case "html_create":
text = "HTML 생성";
break;
case "xlsx_create":
text = "Excel 생성";
break;
case "docx_create":
text = "Word 생성";
break;
case "csv_create":
text = "CSV 생성";
break;
case "md_create":
text = "Markdown 생성";
break;
case "folder_map":
text = "폴더 탐색";
break;
case "glob":
text = "파일 검색";
break;
case "grep":
text = "내용 검색";
break;
case "process":
text = "명령 실행";
break;
default:
text = evt.ToolName;
break;
}
if (1 == 0)
{
}
string text2 = text;
switch (evt.Type)
{
case AgentEventType.Thinking:
SetStatus("생각 중...", spinning: true);
break;
case AgentEventType.Planning:
SetStatus($"계획 수립 중 — {evt.StepTotal}단계", spinning: true);
break;
case AgentEventType.ToolCall:
SetStatus(text2 + " 실행 중...", spinning: true);
break;
case AgentEventType.ToolResult:
SetStatus(evt.Success ? (text2 + " 완료") : (text2 + " 실패"), spinning: false);
break;
case AgentEventType.StepStart:
SetStatus($"[{evt.StepCurrent}/{evt.StepTotal}] {TruncateForStatus(evt.Summary)}", spinning: true);
break;
case AgentEventType.StepDone:
SetStatus($"[{evt.StepCurrent}/{evt.StepTotal}] 단계 완료", spinning: true);
break;
case AgentEventType.SkillCall:
SetStatus("스킬 실행 중: " + TruncateForStatus(evt.Summary), spinning: true);
break;
case AgentEventType.Complete:
SetStatus("작업 완료", spinning: false);
StopStatusAnimation();
break;
case AgentEventType.Error:
SetStatus("오류 발생", spinning: false);
StopStatusAnimation();
break;
case AgentEventType.Paused:
SetStatus("⏸ 일시정지", spinning: false);
break;
case AgentEventType.Resumed:
SetStatus("▶ 재개됨", spinning: true);
break;
case AgentEventType.Decision:
break;
}
}
private void SetStatus(string text, bool spinning)
{
if (StatusLabel != null)
{
StatusLabel.Text = text;
}
if (spinning)
{
StartStatusAnimation();
}
}
private void StartStatusAnimation()
{
if (_statusSpinStoryboard == null)
{
DoubleAnimation doubleAnimation = new DoubleAnimation
{
From = 0.0,
To = 360.0,
Duration = TimeSpan.FromSeconds(2.0),
RepeatBehavior = RepeatBehavior.Forever
};
_statusSpinStoryboard = new Storyboard();
Storyboard.SetTarget((DependencyObject)(object)doubleAnimation, (DependencyObject)(object)StatusDiamond);
Storyboard.SetTargetProperty((DependencyObject)(object)doubleAnimation, new PropertyPath("(UIElement.RenderTransform).(RotateTransform.Angle)"));
_statusSpinStoryboard.Children.Add(doubleAnimation);
_statusSpinStoryboard.Begin();
}
}
private void StopStatusAnimation()
{
_statusSpinStoryboard?.Stop();
_statusSpinStoryboard = null;
}
private void SetStatusIdle()
{
StopStatusAnimation();
if (StatusLabel != null)
{
StatusLabel.Text = "대기 중";
}
if (StatusElapsed != null)
{
StatusElapsed.Text = "";
}
if (StatusTokens != null)
{
StatusTokens.Text = "";
}
}
private void UpdateStatusTokens(int inputTokens, int outputTokens)
{
if (StatusTokens != null)
{
LlmSettings llm = _settings.Settings.Llm;
(double InputCost, double OutputCost) tuple = TokenEstimator.EstimateCost(inputTokens, outputTokens, llm.Service, llm.Model);
double item = tuple.InputCost;
double item2 = tuple.OutputCost;
double num = item + item2;
string value = ((num > 0.0) ? (" · " + TokenEstimator.FormatCost(num)) : "");
StatusTokens.Text = $"↑{TokenEstimator.Format(inputTokens)} ↓{TokenEstimator.Format(outputTokens)}{value}";
}
}
private static string TruncateForStatus(string? text, int max = 40)
{
if (string.IsNullOrEmpty(text))
{
return "";
}
return (text.Length <= max) ? text : (text.Substring(0, max) + "…");
}
private static SolidColorBrush BrushFromHex(string hex)
{
System.Windows.Media.Color color = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(hex);
return new SolidColorBrush(color);
}
[DebuggerNonUserCode]
[GeneratedCode("PresentationBuildTasks", "10.0.5.0")]
public void InitializeComponent()
{
if (!_contentLoaded)
{
_contentLoaded = true;
Uri resourceLocator = new Uri("/AxCopilot;component/views/chatwindow.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:
IconBarColumn = (ColumnDefinition)target;
break;
case 2:
SidebarColumn = (ColumnDefinition)target;
break;
case 3:
SplitterColumn = (ColumnDefinition)target;
break;
case 4:
PreviewColumn = (ColumnDefinition)target;
break;
case 5:
IconBarPanel = (Border)target;
break;
case 6:
((System.Windows.Controls.Button)target).Click += BtnNewChat_Click;
break;
case 7:
((System.Windows.Controls.Button)target).Click += BtnToggleSidebar_Click;
break;
case 8:
((System.Windows.Controls.Button)target).Click += BtnCategoryDrop_Click;
break;
case 9:
BtnUserIconBar = (System.Windows.Controls.Button)target;
break;
case 10:
UserInitialIconBar = (TextBlock)target;
break;
case 11:
SidebarPanel = (Border)target;
break;
case 12:
BtnNewChat = (System.Windows.Controls.Button)target;
BtnNewChat.Click += BtnNewChat_Click;
break;
case 13:
SearchBox = (System.Windows.Controls.TextBox)target;
SearchBox.TextChanged += SearchBox_TextChanged;
break;
case 14:
BtnCategoryDrop = (System.Windows.Controls.Button)target;
BtnCategoryDrop.Click += BtnCategoryDrop_Click;
break;
case 15:
CategoryIcon = (TextBlock)target;
break;
case 16:
CategoryLabel = (TextBlock)target;
break;
case 17:
ConversationPanel = (StackPanel)target;
break;
case 18:
BtnDeleteAll = (System.Windows.Controls.Button)target;
BtnDeleteAll.Click += BtnDeleteAll_Click;
break;
case 19:
UserInitialSidebar = (TextBlock)target;
break;
case 20:
UserNameText = (TextBlock)target;
break;
case 21:
UserPcText = (TextBlock)target;
break;
case 22:
ChatTitle = (TextBlock)target;
ChatTitle.MouseLeftButtonDown += ChatTitle_MouseDown;
break;
case 23:
ChatTitleEdit = (System.Windows.Controls.TextBox)target;
ChatTitleEdit.LostFocus += ChatTitleEdit_LostFocus;
ChatTitleEdit.KeyDown += ChatTitleEdit_KeyDown;
break;
case 24:
BtnPreviewToggle = (System.Windows.Controls.Button)target;
BtnPreviewToggle.Click += BtnPreviewToggle_Click;
break;
case 25:
PreviewDot = (Ellipse)target;
break;
case 26:
AgentProgressBar = (Border)target;
break;
case 27:
ProgressIcon = (TextBlock)target;
break;
case 28:
ProgressStepLabel = (TextBlock)target;
break;
case 29:
ProgressFill = (Border)target;
break;
case 30:
ProgressPercent = (TextBlock)target;
break;
case 31:
ProgressElapsed = (TextBlock)target;
break;
case 32:
BtnToggleSidebar = (System.Windows.Controls.Button)target;
BtnToggleSidebar.Click += BtnToggleSidebar_Click;
break;
case 33:
ToggleSidebarIcon = (TextBlock)target;
break;
case 34:
TabChat = (System.Windows.Controls.RadioButton)target;
TabChat.Checked += TabChat_Checked;
break;
case 35:
TabCowork = (System.Windows.Controls.RadioButton)target;
TabCowork.Checked += TabCowork_Checked;
break;
case 36:
TabCode = (System.Windows.Controls.RadioButton)target;
TabCode.Checked += TabCode_Checked;
break;
case 37:
((System.Windows.Controls.Button)target).Click += BtnSettings_Click;
break;
case 38:
((System.Windows.Controls.Button)target).Click += BtnMinimize_Click;
break;
case 39:
((System.Windows.Controls.Button)target).Click += BtnMaximize_Click;
break;
case 40:
MaximizeIcon = (TextBlock)target;
break;
case 41:
((System.Windows.Controls.Button)target).Click += BtnClose_Click;
break;
case 42:
MessageSearchBar = (Border)target;
break;
case 43:
SearchTextBox = (System.Windows.Controls.TextBox)target;
SearchTextBox.TextChanged += SearchTextBox_TextChanged;
break;
case 44:
SearchResultCount = (TextBlock)target;
break;
case 45:
((System.Windows.Controls.Button)target).Click += SearchPrev_Click;
break;
case 46:
((System.Windows.Controls.Button)target).Click += SearchNext_Click;
break;
case 47:
((System.Windows.Controls.Button)target).Click += SearchClose_Click;
break;
case 48:
MessageScroll = (ScrollViewer)target;
break;
case 49:
MessagePanel = (StackPanel)target;
break;
case 50:
EmptyState = (StackPanel)target;
break;
case 51:
EmptyIcon = (Border)target;
break;
case 52:
EmptyIconTranslate = (TranslateTransform)target;
break;
case 53:
EmptyStateTitle = (TextBlock)target;
break;
case 54:
EmptyStateDesc = (TextBlock)target;
break;
case 55:
TopicButtonPanel = (WrapPanel)target;
break;
case 56:
TemplatePopup = (Popup)target;
break;
case 57:
TemplateItems = (ItemsControl)target;
break;
case 58:
TemplateEmptyHint = (TextBlock)target;
break;
case 59:
PermissionPopup = (Popup)target;
break;
case 60:
PermissionItems = (StackPanel)target;
break;
case 61:
DataUsagePopup = (Popup)target;
break;
case 62:
DataUsageItems = (StackPanel)target;
break;
case 63:
SlashPopup = (Popup)target;
break;
case 64:
((Border)target).PreviewMouseWheel += SlashPopup_PreviewMouseWheel;
break;
case 65:
SlashPopupTitle = (TextBlock)target;
break;
case 66:
SlashPopupHint = (TextBlock)target;
break;
case 67:
SlashNavUp = (Border)target;
break;
case 68:
SlashNavUpText = (TextBlock)target;
break;
case 69:
SlashItems = (ItemsControl)target;
break;
case 70:
SlashNavDown = (Border)target;
break;
case 71:
SlashNavDownText = (TextBlock)target;
break;
case 72:
FolderMenuPopup = (Popup)target;
break;
case 73:
FolderMenuItems = (StackPanel)target;
break;
case 74:
ToastBorder = (Border)target;
break;
case 75:
ToastIcon = (TextBlock)target;
break;
case 76:
ToastText = (TextBlock)target;
break;
case 77:
DraftPreviewCard = (Border)target;
break;
case 78:
DraftPreviewText = (TextBlock)target;
break;
case 79:
BtnDraftEnqueue = (System.Windows.Controls.Button)target;
BtnDraftEnqueue.Click += BtnDraftEnqueue_Click;
break;
case 80:
BtnDraftEdit = (System.Windows.Controls.Button)target;
BtnDraftEdit.Click += BtnDraftEdit_Click;
break;
case 81:
BtnDraftClear = (System.Windows.Controls.Button)target;
BtnDraftClear.Click += BtnDraftClear_Click;
break;
case 82:
DraftQueuePanel = (StackPanel)target;
break;
case 83:
InputGlowBorder = (Border)target;
break;
case 84:
RainbowBrush = (LinearGradientBrush)target;
break;
case 85:
InputBorder = (Border)target;
break;
case 86:
BtnModelSelector = (System.Windows.Controls.Button)target;
BtnModelSelector.Click += BtnModelSelector_Click;
break;
case 87:
ModelLabel = (TextBlock)target;
break;
case 88:
BtnTemplateSelector = (System.Windows.Controls.Button)target;
BtnTemplateSelector.Click += BtnTemplateSelector_Click;
break;
case 89:
AttachedFilesPanel = (ItemsControl)target;
break;
case 90:
InputBox = (System.Windows.Controls.TextBox)target;
InputBox.PreviewKeyDown += InputBox_PreviewKeyDown;
InputBox.TextChanged += InputBox_TextChanged;
break;
case 91:
InputWatermark = (TextBlock)target;
break;
case 92:
SlashCommandChip = (Border)target;
break;
case 93:
SlashChipText = (TextBlock)target;
break;
case 94:
SlashChipClose = (Border)target;
break;
case 95:
BtnAttach = (System.Windows.Controls.Button)target;
BtnAttach.Click += BtnAttach_Click;
break;
case 96:
((System.Windows.Controls.Button)target).Click += BtnExport_Click;
break;
case 97:
BtnPause = (Border)target;
BtnPause.MouseLeftButtonUp += BtnPause_Click;
break;
case 98:
PauseIcon = (TextBlock)target;
break;
case 99:
BtnStop = (System.Windows.Controls.Button)target;
BtnStop.Click += BtnStop_Click;
break;
case 100:
BtnSend = (System.Windows.Controls.Button)target;
BtnSend.Click += BtnSend_Click;
break;
case 101:
FolderBar = (Border)target;
break;
case 102:
FolderPathLabel = (TextBlock)target;
FolderPathLabel.MouseLeftButtonUp += FolderPathLabel_Click;
break;
case 103:
((System.Windows.Controls.Button)target).Click += BtnFolderClear_Click;
break;
case 104:
MoodIconPanel = (StackPanel)target;
break;
case 105:
FormatMoodSeparator = (Border)target;
break;
case 106:
BtnDataUsage = (Border)target;
BtnDataUsage.MouseLeftButtonUp += BtnDataUsage_Click;
break;
case 107:
DataUsageIcon = (TextBlock)target;
break;
case 108:
DataUsageLabel = (TextBlock)target;
break;
case 109:
BtnPermission = (System.Windows.Controls.Button)target;
BtnPermission.Click += BtnPermission_Click;
break;
case 110:
PermissionIcon = (TextBlock)target;
break;
case 111:
PermissionLabel = (TextBlock)target;
break;
case 112:
FormatMenuPopup = (Popup)target;
break;
case 113:
FormatMenuItems = (StackPanel)target;
break;
case 114:
MoodMenuPopup = (Popup)target;
break;
case 115:
MoodMenuItems = (StackPanel)target;
break;
case 116:
AutoPermissionWarning = (Border)target;
break;
case 117:
BtnAutoWarningClose = (System.Windows.Controls.Button)target;
BtnAutoWarningClose.Click += BtnAutoWarningClose_Click;
break;
case 118:
FileBrowserPanel = (Border)target;
break;
case 119:
FileBrowserTitle = (TextBlock)target;
break;
case 120:
((System.Windows.Controls.Button)target).Click += BtnFileBrowserRefresh_Click;
break;
case 121:
((System.Windows.Controls.Button)target).Click += BtnFileBrowserOpenFolder_Click;
break;
case 122:
((System.Windows.Controls.Button)target).Click += BtnFileBrowserClose_Click;
break;
case 123:
FileTreeView = (System.Windows.Controls.TreeView)target;
break;
case 124:
StatusDiamond = (TextBlock)target;
break;
case 125:
StatusDiamondRotate = (RotateTransform)target;
break;
case 126:
StatusLabel = (TextBlock)target;
break;
case 127:
BtnToggleExecutionLog = (System.Windows.Controls.Button)target;
BtnToggleExecutionLog.Click += BtnToggleExecutionLog_Click;
break;
case 128:
ExecutionLogIcon = (TextBlock)target;
break;
case 129:
ExecutionLogLabel = (TextBlock)target;
break;
case 130:
SubAgentIndicator = (Border)target;
break;
case 131:
SubAgentIndicatorLabel = (TextBlock)target;
break;
case 132:
BtnShowAnalyzer = (Border)target;
BtnShowAnalyzer.MouseLeftButtonUp += BtnShowAnalyzer_Click;
break;
case 133:
StatusElapsed = (TextBlock)target;
break;
case 134:
StatusTokens = (TextBlock)target;
break;
case 135:
PreviewSplitter = (GridSplitter)target;
break;
case 136:
PreviewPanel = (Border)target;
break;
case 137:
((Border)target).PreviewMouseDown += PreviewTabBar_PreviewMouseDown;
break;
case 138:
PreviewTabPanel = (StackPanel)target;
break;
case 139:
BtnOpenExternal = (System.Windows.Controls.Button)target;
BtnOpenExternal.Click += BtnOpenExternal_Click;
break;
case 140:
((System.Windows.Controls.Button)target).Click += BtnClosePreview_Click;
break;
case 141:
PreviewWebView = (WebView2)target;
break;
case 142:
PreviewTextScroll = (ScrollViewer)target;
break;
case 143:
PreviewTextBlock = (TextBlock)target;
break;
case 144:
PreviewDataGrid = (DataGrid)target;
break;
case 145:
PreviewEmpty = (TextBlock)target;
break;
case 146:
ResizeGrip = (Thumb)target;
ResizeGrip.DragDelta += ResizeGrip_DragDelta;
break;
default:
_contentLoaded = true;
break;
}
}
static ChatWindow()
{
Dictionary<string, List<(string, string, string)>> dictionary = new Dictionary<string, List<(string, string, string)>>(StringComparer.OrdinalIgnoreCase);
int num = 4;
List<(string, string, string)> list = new List<(string, string, string)>(num);
CollectionsMarshal.SetCount(list, num);
Span<(string, string, string)> span = CollectionsMarshal.AsSpan(list);
span[0] = ("코드 리뷰", "\ue943", "첨부된 코드를 리뷰해 주세요. 버그, 성능 이슈, 보안 취약점, 개선점을 찾아 구체적으로 제안하세요.");
span[1] = ("코드 설명", "\ue946", "첨부된 코드를 상세히 설명해 주세요. 주요 함수, 데이터 흐름, 설계 패턴을 포함하세요.");
span[2] = ("리팩토링 제안", "\ue70f", "첨부된 코드의 리팩토링 방안을 제안해 주세요. 가독성, 유지보수성, 성능을 고려하세요.");
span[3] = ("테스트 생성", "\ue9d5", "첨부된 코드에 대한 단위 테스트 코드를 생성해 주세요.");
dictionary["code"] = list;
num = 3;
List<(string, string, string)> list2 = new List<(string, string, string)>(num);
CollectionsMarshal.SetCount(list2, num);
Span<(string, string, string)> span2 = CollectionsMarshal.AsSpan(list2);
span2[0] = ("요약", "\ue8ab", "첨부된 문서를 핵심 포인트 위주로 간결하게 요약해 주세요.");
span2[1] = ("분석", "\ue9d9", "첨부된 문서의 내용을 분석하고 주요 인사이트를 도출해 주세요.");
span2[2] = ("번역", "\ue8c1", "첨부된 문서를 영어로 번역해 주세요. 원문의 톤과 뉘앙스를 유지하세요.");
dictionary["document"] = list2;
num = 3;
List<(string, string, string)> list3 = new List<(string, string, string)>(num);
CollectionsMarshal.SetCount(list3, num);
Span<(string, string, string)> span3 = CollectionsMarshal.AsSpan(list3);
span3[0] = ("데이터 분석", "\ue9d9", "첨부된 데이터를 분석해 주세요. 통계, 추세, 이상치를 찾아 보고하세요.");
span3[1] = ("시각화 제안", "\ue9d9", "첨부된 데이터를 시각화할 최적의 차트 유형을 제안하고 chart_create로 생성해 주세요.");
span3[2] = ("포맷 변환", "\ue8ab", "첨부된 데이터를 다른 형식으로 변환해 주세요. (CSV↔JSON↔Excel 등)");
dictionary["data"] = list3;
num = 2;
List<(string, string, string)> list4 = new List<(string, string, string)>(num);
CollectionsMarshal.SetCount(list4, num);
Span<(string, string, string)> span4 = CollectionsMarshal.AsSpan(list4);
span4[0] = ("이미지 설명", "\ue946", "첨부된 이미지를 자세히 설명해 주세요. 내용, 레이아웃, 텍스트를 분석하세요.");
span4[1] = ("UI 리뷰", "\ue70f", "첨부된 UI 스크린샷을 리뷰해 주세요. UX 개선점, 접근성, 디자인 일관성을 평가하세요.");
dictionary["image"] = list4;
DropActions = dictionary;
CodeExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
".cs", ".py", ".js", ".ts", ".tsx", ".jsx", ".java", ".cpp", ".c", ".h",
".go", ".rs", ".rb", ".php", ".swift", ".kt", ".scala", ".sh", ".ps1", ".bat",
".cmd", ".sql", ".xaml", ".vue"
};
DataExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".csv", ".json", ".xml", ".yaml", ".yml", ".tsv" };
WriteToolNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "file_write", "file_edit", "html_create", "xlsx_create", "docx_create", "csv_create", "md_create", "script_create", "diff_preview", "open_external" };
Tips = new string[24]
{
"\ud83d\udca1 작업 폴더에 AX.md 파일을 만들면 매번 시스템 프롬프트에 자동 주입됩니다. 프로젝트 설계 원칙이나 코딩 규칙을 기록하세요.", "\ud83d\udca1 Ctrl+1/2/3으로 Chat/Cowork/Code 탭을 빠르게 전환할 수 있습니다.", "\ud83d\udca1 Ctrl+F로 현재 대화 내 메시지를 검색할 수 있습니다.", "\ud83d\udca1 메시지를 우클릭하면 복사, 인용 답장, 재생성, 삭제를 할 수 있습니다.", "\ud83d\udca1 코드 블록을 더블클릭하면 전체화면으로 볼 수 있고, \ud83d\udcbe 버튼으로 파일 저장이 가능합니다.", "\ud83d\udca1 Cowork 에이전트가 만든 파일은 자동으로 날짜_시간 접미사가 붙어 덮어쓰기를 방지합니다.", "\ud83d\udca1 Code 탭에서 개발 언어를 선택하면 해당 언어 우선으로 코드를 생성합니다.", "\ud83d\udca1 파일 탐색기(하단 바 '파일' 버튼)에서 더블클릭으로 프리뷰, 우클릭으로 관리할 수 있습니다.", "\ud83d\udca1 에이전트가 계획을 제시하면 '수정 요청'으로 방향을 바꾸거나 '취소'로 중단할 수 있습니다.", "\ud83d\udca1 Code 탭은 빌드/테스트를 자동으로 실행합니다. 프로젝트 폴더를 먼저 선택하세요.",
"\ud83d\udca1 무드 갤러리에서 10가지 디자인 템플릿 중 원하는 스타일을 미리보기로 선택할 수 있습니다.", "\ud83d\udca1 Git 연동: Code 탭에서 에이전트가 git status, diff, commit을 수행합니다. (push는 직접)", "\ud83d\udca1 설정 → AX Agent → 공통에서 개발자 모드를 켜면 에이전트 동작을 스텝별로 검증할 수 있습니다.", "\ud83d\udca1 트레이 아이콘 우클릭 → '사용 통계'에서 대화 빈도와 토큰 사용량을 확인할 수 있습니다.", "\ud83d\udca1 대화 제목을 클릭하면 이름을 변경할 수 있습니다.", "\ud83d\udca1 LLM 오류 발생 시 '재시도' 버튼이 자동으로 나타납니다.", "\ud83d\udca1 검색란에서 대화 제목뿐 아니라 첫 메시지 내용까지 검색됩니다.", "\ud83d\udca1 프리셋 선택 후에도 대화가 리셋되지 않습니다. 진행 중인 대화에서 프리셋을 변경할 수 있습니다.", "\ud83d\udca1 Shift+Enter로 퍼지 검색 결과의 파일이 있는 폴더를 열 수 있습니다.", "\ud83d\udca1 최근 폴더를 우클릭하면 '폴더 열기', '경로 복사', '목록에서 삭제'가 가능합니다.",
"\ud83d\udca1 Cowork/Code 에이전트 작업 완료 시 시스템 트레이에 알림이 표시됩니다.", "\ud83d\udca1 마크다운 테이블, 인용(>), 취소선(~~), 링크([text](url))가 모두 렌더링됩니다.", "\ud83d\udca1 ⚠ 데이터 폴더를 워크스페이스로 지정할 때는 반드시 백업을 먼저 만드세요!", "\ud83d\udca1 드라이브 루트(C:\\, D:\\)는 작업공간으로 설정할 수 없습니다. 하위 폴더를 선택하세요."
};
GeminiModels = new(string, string)[5]
{
("gemini-2.5-pro", "Gemini 2.5 Pro"),
("gemini-2.5-flash", "Gemini 2.5 Flash"),
("gemini-2.5-flash-lite", "Gemini 2.5 Flash Lite"),
("gemini-2.0-flash", "Gemini 2.0 Flash"),
("gemini-2.0-flash-lite", "Gemini 2.0 Flash Lite")
};
ClaudeModels = new(string, string)[5]
{
("claude-opus-4-6", "Claude Opus 4.6"),
("claude-sonnet-4-6", "Claude Sonnet 4.6"),
("claude-haiku-4-5-20251001", "Claude Haiku 4.5"),
("claude-sonnet-4-5-20250929", "Claude Sonnet 4.5"),
("claude-opus-4-20250514", "Claude Opus 4")
};
_previewableExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".html", ".htm", ".md", ".txt", ".csv", ".json", ".xml", ".log" };
WebView2DataFolder = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "WebView2");
_ignoredDirs = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"bin", "obj", "node_modules", ".git", ".vs", ".idea", ".vscode", "__pycache__", ".mypy_cache", ".pytest_cache",
"dist", "build", ".cache", ".next", ".nuxt", "coverage", ".terraform"
};
}
}