using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Threading; using AxCopilot.Services.Agent; namespace AxCopilot.Views; public class WorkflowAnalyzerWindow : Window, IComponentConnector { private record WaterfallEntry(string Label, long StartMs, long DurationMs, string Type, bool IsBottleneck = false); private readonly DateTime _startTime = DateTime.Now; private int _totalToolCalls; private int _totalInputTokens; private int _totalOutputTokens; private int _maxIteration; private int _successCount; private int _failCount; private string _logLevel = "simple"; private bool _autoScroll = true; private string _activeTab = "timeline"; private readonly List _waterfallEntries = new List(); private readonly Dictionary _toolTimeAccum = new Dictionary(); private readonly List<(int Iteration, int Input, int Output)> _tokenTrend = new List<(int, int, int)>(); private long _lastEventMs; private const int WM_NCHITTEST = 132; private const int HTLEFT = 10; private const int HTRIGHT = 11; private const int HTTOP = 12; private const int HTTOPLEFT = 13; private const int HTTOPRIGHT = 14; private const int HTBOTTOM = 15; private const int HTBOTTOMLEFT = 16; private const int HTBOTTOMRIGHT = 17; internal Border BtnClear; internal Border BtnMinimize; internal Border BtnClose; internal TextBlock CardElapsed; internal TextBlock CardIterations; internal TextBlock CardTokens; internal TextBlock CardInputTokens; internal TextBlock CardOutputTokens; internal TextBlock CardToolCalls; internal Border TabTimeline; internal TextBlock TabTimelineIcon; internal TextBlock TabTimelineText; internal Border TabBottleneck; internal TextBlock TabBottleneckIcon; internal TextBlock TabBottleneckText; internal ScrollViewer TimelineScroller; internal StackPanel TimelinePanel; internal ScrollViewer BottleneckScroller; internal StackPanel BottleneckPanel; internal Canvas WaterfallCanvas; internal StackPanel ToolTimePanel; internal Canvas TokenTrendCanvas; internal Border DetailPanel; internal TextBlock DetailTitle; internal TextBlock DetailBadge; internal TextBlock DetailMeta; internal TextBlock DetailContent; internal TextBlock StatusText; internal TextBlock LogLevelBadge; private bool _contentLoaded; [DllImport("user32.dll")] private static extern nint SendMessage(nint hWnd, int msg, nint wParam, nint lParam); public WorkflowAnalyzerWindow() { InitializeComponent(); base.SourceInitialized += delegate { ((HwndSource)PresentationSource.FromVisual(this))?.AddHook(WndProc); }; _logLevel = (Application.Current as App)?.SettingsService?.Settings.Llm.AgentLogLevel ?? "simple"; TextBlock logLevelBadge = LogLevelBadge; string logLevel = _logLevel; if (1 == 0) { } string text = ((logLevel == "debug") ? "DEBUG" : ((!(logLevel == "detailed")) ? "SIMPLE" : "DETAILED")); if (1 == 0) { } logLevelBadge.Text = text; base.Loaded += delegate { //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) Rect workArea = SystemParameters.WorkArea; base.Left = ((Rect)(ref workArea)).Right - base.ActualWidth - 20.0; base.Top = ((Rect)(ref workArea)).Bottom - base.ActualHeight - 20.0; UpdateTabVisuals(); }; TimelineScroller.ScrollChanged += delegate(object _, ScrollChangedEventArgs e) { _autoScroll = Math.Abs(e.ExtentHeight - e.ViewportHeight - e.VerticalOffset) < 30.0; }; } public void OnAgentEvent(AgentEvent evt) { if (!((DispatcherObject)this).Dispatcher.CheckAccess()) { ((DispatcherObject)this).Dispatcher.Invoke((Action)delegate { OnAgentEvent(evt); }); return; } if (evt.Iteration > _maxIteration) { _maxIteration = evt.Iteration; } if (evt.InputTokens > 0) { _totalInputTokens += evt.InputTokens; } if (evt.OutputTokens > 0) { _totalOutputTokens += evt.OutputTokens; } if (evt.Type == AgentEventType.ToolCall) { _totalToolCalls++; } if (evt.Type == AgentEventType.ToolResult) { _successCount++; } if (evt.Type == AgentEventType.Error) { _failCount++; } UpdateSummaryCards(); CollectBottleneckData(evt); Border element = CreateTimelineNode(evt); TimelinePanel.Children.Add(element); if (_autoScroll) { TimelineScroller.ScrollToEnd(); } TextBlock statusText = StatusText; AgentEventType type = evt.Type; if (1 == 0) { } string text = type switch { AgentEventType.Complete => "✓ 완료", AgentEventType.Error => "⚠ 오류: " + Truncate(evt.Summary, 60), AgentEventType.ToolCall => "실행 중: " + evt.ToolName, AgentEventType.Thinking => "사고 중...", AgentEventType.Planning => "계획 수립 중...", _ => StatusText.Text, }; if (1 == 0) { } statusText.Text = text; if (evt.Type == AgentEventType.Complete) { RenderBottleneckCharts(); } } public void Reset() { TimelinePanel.Children.Clear(); DetailPanel.Visibility = Visibility.Collapsed; _totalToolCalls = 0; _totalInputTokens = 0; _totalOutputTokens = 0; _maxIteration = 0; _successCount = 0; _failCount = 0; _waterfallEntries.Clear(); _toolTimeAccum.Clear(); _tokenTrend.Clear(); _lastEventMs = 0L; UpdateSummaryCards(); ClearBottleneckCharts(); StatusText.Text = "대기 중..."; } public void SwitchToTimelineTab() { _activeTab = "timeline"; UpdateTabVisuals(); } public void SwitchToBottleneckTab() { _activeTab = "bottleneck"; UpdateTabVisuals(); RenderBottleneckCharts(); } private void TabTimeline_Click(object sender, MouseButtonEventArgs e) { _activeTab = "timeline"; UpdateTabVisuals(); } private void TabBottleneck_Click(object sender, MouseButtonEventArgs e) { _activeTab = "bottleneck"; UpdateTabVisuals(); RenderBottleneckCharts(); } private void UpdateTabVisuals() { Brush brush = (TryFindResource("AccentColor") as Brush) ?? Brushes.CornflowerBlue; Brush brush2 = (TryFindResource("ItemBackground") as Brush) ?? new SolidColorBrush(Color.FromArgb(21, byte.MaxValue, byte.MaxValue, byte.MaxValue)); Brush brush3 = (TryFindResource("PrimaryText") as Brush) ?? Brushes.White; bool flag = _activeTab == "timeline"; TabTimeline.Background = (flag ? brush : brush2); TabBottleneck.Background = ((!flag) ? brush : brush2); TabTimelineIcon.Foreground = (flag ? Brushes.White : brush3); TabTimelineText.Foreground = (flag ? Brushes.White : brush3); TabBottleneckIcon.Foreground = ((!flag) ? Brushes.White : brush3); TabBottleneckText.Foreground = ((!flag) ? Brushes.White : brush3); TimelineScroller.Visibility = ((!flag) ? Visibility.Collapsed : Visibility.Visible); BottleneckScroller.Visibility = (flag ? Visibility.Collapsed : Visibility.Visible); DetailPanel.Visibility = ((!flag || DetailPanel.Tag == null) ? Visibility.Collapsed : Visibility.Visible); } private void CollectBottleneckData(AgentEvent evt) { long num = (long)(evt.Timestamp - _startTime).TotalMilliseconds; if (evt.Type == AgentEventType.Thinking && evt.Iteration > 0) { _lastEventMs = num; } else if (evt.Type == AgentEventType.ToolCall) { if (_lastEventMs > 0) { long num2 = num - _lastEventMs; if (num2 > 100) { _waterfallEntries.Add(new WaterfallEntry($"LLM #{evt.Iteration}", _lastEventMs, num2, "llm")); } } _lastEventMs = num; } else if (evt.Type == AgentEventType.ToolResult && evt.ElapsedMs > 0) { long startMs = num - evt.ElapsedMs; _waterfallEntries.Add(new WaterfallEntry(evt.ToolName, startMs, evt.ElapsedMs, "tool")); string toolName = evt.ToolName; _toolTimeAccum[toolName] = _toolTimeAccum.GetValueOrDefault(toolName) + evt.ElapsedMs; _lastEventMs = num; } else if (evt.Type == AgentEventType.Complete && _lastEventMs > 0) { long num3 = num - _lastEventMs; if (num3 > 100) { _waterfallEntries.Add(new WaterfallEntry("LLM (마무리)", _lastEventMs, num3, "llm")); } } if ((evt.InputTokens > 0 || evt.OutputTokens > 0) && evt.Iteration > 0) { int num4 = _tokenTrend.FindIndex(((int Iteration, int Input, int Output) t) => t.Iteration == evt.Iteration); if (num4 >= 0) { (int, int, int) tuple = _tokenTrend[num4]; _tokenTrend[num4] = (evt.Iteration, tuple.Item2 + evt.InputTokens, tuple.Item3 + evt.OutputTokens); } else { _tokenTrend.Add((evt.Iteration, evt.InputTokens, evt.OutputTokens)); } } } private void ClearBottleneckCharts() { WaterfallCanvas.Children.Clear(); ToolTimePanel.Children.Clear(); TokenTrendCanvas.Children.Clear(); } private void RenderBottleneckCharts() { ClearBottleneckCharts(); RenderWaterfallChart(); RenderToolTimeChart(); RenderTokenTrendChart(); } private void RenderWaterfallChart() { if (_waterfallEntries.Count == 0) { WaterfallCanvas.Height = 40.0; TextBlock textBlock = new TextBlock(); textBlock.Text = "실행 데이터가 없습니다"; textBlock.FontSize = 11.0; textBlock.Foreground = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray; TextBlock element = textBlock; Canvas.SetLeft(element, 10.0); Canvas.SetTop(element, 10.0); WaterfallCanvas.Children.Add(element); return; } double num = _waterfallEntries.Average((WaterfallEntry e) => e.DurationMs); long num2 = _waterfallEntries.Max((WaterfallEntry e) => e.StartMs + e.DurationMs); if (num2 <= 0) { num2 = 1L; } int num3 = 22; double num4 = 90.0; int val = _waterfallEntries.Count * num3 + 10; WaterfallCanvas.Height = Math.Max(val, 60); double num5 = ((WaterfallCanvas.ActualWidth > 0.0) ? WaterfallCanvas.ActualWidth : 480.0); double num6 = num5 - num4 - 60.0; for (int num7 = 0; num7 < _waterfallEntries.Count; num7++) { WaterfallEntry waterfallEntry = _waterfallEntries[num7]; int num8 = num7 * num3 + 4; Color color = (((double)waterfallEntry.DurationMs >= num * 2.0) ? Color.FromRgb(239, 68, 68) : (((double)waterfallEntry.DurationMs >= num * 1.5) ? Color.FromRgb(249, 115, 22) : ((!(waterfallEntry.Type == "llm")) ? Color.FromRgb(52, 211, 153) : Color.FromRgb(96, 165, 250)))); TextBlock element2 = new TextBlock { Text = Truncate(waterfallEntry.Label, 12), FontSize = 10.0, FontFamily = new FontFamily("Consolas"), Foreground = new SolidColorBrush(color), Width = num4, TextAlignment = TextAlignment.Right }; Canvas.SetLeft(element2, 0.0); Canvas.SetTop(element2, num8 + 2); WaterfallCanvas.Children.Add(element2); double num9 = (double)waterfallEntry.StartMs / (double)num2 * num6; double num10 = Math.Max((double)waterfallEntry.DurationMs / (double)num2 * num6, 3.0); Rectangle element3 = new Rectangle { Width = num10, Height = 14.0, RadiusX = 3.0, RadiusY = 3.0, Fill = new SolidColorBrush(color), Opacity = 0.85, ToolTip = waterfallEntry.Label + ": " + FormatMs(waterfallEntry.DurationMs) }; Canvas.SetLeft(element3, num4 + 8.0 + num9); Canvas.SetTop(element3, num8 + 1); WaterfallCanvas.Children.Add(element3); TextBlock element4 = new TextBlock { Text = FormatMs(waterfallEntry.DurationMs), FontSize = 9.0, Foreground = new SolidColorBrush(color), FontFamily = new FontFamily("Consolas") }; Canvas.SetLeft(element4, num4 + 8.0 + num9 + num10 + 4.0); Canvas.SetTop(element4, num8 + 3); WaterfallCanvas.Children.Add(element4); if ((double)waterfallEntry.DurationMs >= num * 2.0) { TextBlock element5 = new TextBlock { Text = "\ud83d\udd34", FontSize = 9.0 }; Canvas.SetLeft(element5, num4 + 8.0 + num9 + num10 + 44.0); Canvas.SetTop(element5, num8 + 2); WaterfallCanvas.Children.Add(element5); } } } private void RenderToolTimeChart() { ToolTimePanel.Children.Clear(); if (_toolTimeAccum.Count == 0) { ToolTimePanel.Children.Add(new TextBlock { Text = "도구 실행 데이터가 없습니다", FontSize = 11.0, Foreground = ((TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray), Margin = new Thickness(0.0, 4.0, 0.0, 4.0) }); return; } long num = _waterfallEntries.Where((WaterfallEntry e) => e.Type == "llm").Sum((WaterfallEntry e) => e.DurationMs); List<(string, long)> list = _toolTimeAccum.Select, (string, long)>((KeyValuePair kv) => (Name: kv.Key, Ms: kv.Value)).ToList(); if (num > 0) { list.Add(("LLM 호출", num)); } List<(string, long)> list2 = list.OrderByDescending<(string, long), long>(((string Name, long Ms) e) => e.Ms).ToList(); long num2 = list2.Max<(string, long)>(((string Name, long Ms) e) => e.Ms); if (num2 <= 0) { num2 = 1L; } foreach (var item3 in list2) { string item = item3.Item1; long item2 = item3.Item2; bool flag = item2 == list2[0].Item2; Color color = ((item == "LLM 호출") ? Color.FromRgb(96, 165, 250) : (flag ? Color.FromRgb(239, 68, 68) : Color.FromRgb(52, 211, 153))); Grid grid = new Grid { Margin = new Thickness(0.0, 2.0, 0.0, 2.0) }; grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(90.0) }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0, GridUnitType.Star) }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(60.0) }); TextBlock element = new TextBlock { Text = Truncate(item, 12), FontSize = 11.0, FontFamily = new FontFamily("Consolas"), Foreground = new SolidColorBrush(color), TextAlignment = TextAlignment.Right, VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0.0, 0.0, 8.0, 0.0) }; Grid.SetColumn(element, 0); double barWidth = (double)item2 / (double)num2; Border barBorder = new Border { Background = new SolidColorBrush(color), CornerRadius = new CornerRadius(3.0), Height = 14.0, HorizontalAlignment = HorizontalAlignment.Left, Width = 0.0, Opacity = 0.8 }; Border barContainer = new Border { Margin = new Thickness(0.0, 2.0, 0.0, 2.0) }; barContainer.Child = barBorder; barContainer.SizeChanged += delegate { barBorder.Width = Math.Max(barContainer.ActualWidth * barWidth, 4.0); }; Grid.SetColumn(barContainer, 1); TextBlock element2 = new TextBlock { Text = FormatMs(item2), FontSize = 10.0, Foreground = new SolidColorBrush(color), FontFamily = new FontFamily("Consolas"), VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(4.0, 0.0, 0.0, 0.0) }; Grid.SetColumn(element2, 2); grid.Children.Add(element); grid.Children.Add(barContainer); grid.Children.Add(element2); ToolTimePanel.Children.Add(grid); } } private void RenderTokenTrendChart() { TokenTrendCanvas.Children.Clear(); if (_tokenTrend.Count < 2) { TokenTrendCanvas.Height = 40.0; TokenTrendCanvas.Children.Add(new TextBlock { Text = ((_tokenTrend.Count == 0) ? "토큰 데이터가 없습니다" : "2회 이상 반복이 필요합니다"), FontSize = 11.0, Foreground = ((TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray) }); Canvas.SetLeft(TokenTrendCanvas.Children[0], 10.0); Canvas.SetTop(TokenTrendCanvas.Children[0], 10.0); return; } TokenTrendCanvas.Height = 100.0; double num = ((TokenTrendCanvas.ActualWidth > 0.0) ? TokenTrendCanvas.ActualWidth : 480.0); double canvasHeight = 90.0; double marginLeft = 45.0; double num2 = 10.0; double chartWidth = num - marginLeft - num2; List<(int Iteration, int Input, int Output)> sorted = _tokenTrend.OrderBy(((int Iteration, int Input, int Output) t) => t.Iteration).ToList(); int maxToken = sorted.Max(((int Iteration, int Input, int Output) t) => Math.Max(t.Input, t.Output)); if (maxToken <= 0) { maxToken = 1; } for (int num3 = 0; num3 <= 2; num3++) { double num4 = canvasHeight * (1.0 - (double)num3 / 2.0); int num5 = maxToken * num3 / 2; Line line = new Line(); line.X1 = marginLeft; line.Y1 = num4; line.X2 = num - num2; line.Y2 = num4; line.Stroke = (TryFindResource("BorderColor") as Brush) ?? Brushes.Gray; line.StrokeThickness = 0.5; line.Opacity = 0.4; Line element = line; TokenTrendCanvas.Children.Add(element); TextBlock textBlock = new TextBlock(); textBlock.Text = ((num5 >= 1000) ? $"{(double)num5 / 1000.0:F0}k" : num5.ToString()); textBlock.FontSize = 9.0; textBlock.Foreground = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray; textBlock.TextAlignment = TextAlignment.Right; textBlock.Width = marginLeft - 6.0; TextBlock element2 = textBlock; Canvas.SetLeft(element2, 0.0); Canvas.SetTop(element2, num4 - 6.0); TokenTrendCanvas.Children.Add(element2); } DrawPolyline(((IEnumerable<(int, int, int)>)sorted).Select((Func<(int, int, int), int, Point>)(((int Iteration, int Input, int Output) t, int i) => new Point(marginLeft + (double)i / (double)(sorted.Count - 1) * chartWidth, canvasHeight * (1.0 - (double)t.Input / (double)maxToken)))).ToList(), "#3B82F6"); DrawPolyline(((IEnumerable<(int, int, int)>)sorted).Select((Func<(int, int, int), int, Point>)(((int Iteration, int Input, int Output) t, int i) => new Point(marginLeft + (double)i / (double)(sorted.Count - 1) * chartWidth, canvasHeight * (1.0 - (double)t.Output / (double)maxToken)))).ToList(), "#10B981"); foreach (var item in sorted) { int iter = item.Iteration; int num6 = sorted.FindIndex(((int Iteration, int Input, int Output) t) => t.Iteration == iter); double num7 = marginLeft + (double)num6 / (double)(sorted.Count - 1) * chartWidth; TextBlock textBlock = new TextBlock(); textBlock.Text = $"#{iter}"; textBlock.FontSize = 9.0; textBlock.Foreground = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray; TextBlock element3 = textBlock; Canvas.SetLeft(element3, num7 - 8.0); Canvas.SetTop(element3, canvasHeight + 2.0); TokenTrendCanvas.Children.Add(element3); } StackPanel stackPanel = new StackPanel { Orientation = Orientation.Horizontal }; stackPanel.Children.Add(CreateLegendDot("#3B82F6", "입력")); stackPanel.Children.Add(CreateLegendDot("#10B981", "출력")); Canvas.SetRight(stackPanel, 10.0); Canvas.SetTop(stackPanel, 0.0); TokenTrendCanvas.Children.Add(stackPanel); if (sorted.Count < 3) { return; } List list = sorted.Select(((int Iteration, int Input, int Output) t) => t.Input).ToList(); bool flag = true; for (int num8 = 1; num8 < list.Count; num8++) { if (list[num8] <= list[num8 - 1]) { flag = false; break; } } if (flag) { if (list[list.Count - 1] > list[0] * 2) { TextBlock element4 = new TextBlock { Text = "⚠ 컨텍스트 크기 급증", FontSize = 10.0, Foreground = new SolidColorBrush(Color.FromRgb(239, 68, 68)), FontWeight = FontWeights.SemiBold }; Canvas.SetLeft(element4, marginLeft + 4.0); Canvas.SetTop(element4, 0.0); TokenTrendCanvas.Children.Add(element4); } } } private void DrawPolyline(List points, string colorHex) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) if (points.Count < 2) { return; } Color color = (Color)ColorConverter.ConvertFromString(colorHex); Polyline polyline = new Polyline { Stroke = new SolidColorBrush(color), StrokeThickness = 2.0, StrokeLineJoin = PenLineJoin.Round }; foreach (Point point in points) { polyline.Points.Add(point); } TokenTrendCanvas.Children.Add(polyline); foreach (Point point2 in points) { Point current2 = point2; Ellipse element = new Ellipse { Width = 6.0, Height = 6.0, Fill = new SolidColorBrush(color) }; Canvas.SetLeft(element, ((Point)(ref current2)).X - 3.0); Canvas.SetTop(element, ((Point)(ref current2)).Y - 3.0); TokenTrendCanvas.Children.Add(element); } } private static StackPanel CreateLegendDot(string colorHex, string text) { Color color = (Color)ColorConverter.ConvertFromString(colorHex); StackPanel stackPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(8.0, 0.0, 0.0, 0.0) }; stackPanel.Children.Add(new Ellipse { Width = 6.0, Height = 6.0, Fill = new SolidColorBrush(color), VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0.0, 0.0, 3.0, 0.0) }); stackPanel.Children.Add(new TextBlock { Text = text, FontSize = 9.0, Foreground = new SolidColorBrush(color), VerticalAlignment = VerticalAlignment.Center }); return stackPanel; } private Border CreateTimelineNode(AgentEvent evt) { (string Icon, Color Color, string Label) eventVisual = GetEventVisual(evt); string item = eventVisual.Icon; Color item2 = eventVisual.Color; string item3 = eventVisual.Label; Border node = new Border { Background = Brushes.Transparent, Margin = new Thickness(0.0, 1.0, 0.0, 1.0), Padding = new Thickness(8.0, 6.0, 8.0, 6.0), CornerRadius = new CornerRadius(8.0), Cursor = Cursors.Hand, Tag = evt }; Grid grid = new Grid(); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(28.0) }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(3.0) }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0, GridUnitType.Star) }); Border border = new Border { Width = 22.0, Height = 22.0, CornerRadius = new CornerRadius(11.0), Background = new SolidColorBrush(item2), HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top, Margin = new Thickness(0.0, 2.0, 0.0, 0.0) }; border.Child = new TextBlock { Text = item, FontFamily = new FontFamily("Segoe MDL2 Assets"), FontSize = 10.0, Foreground = Brushes.White, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center }; Grid.SetColumn(border, 0); grid.Children.Add(border); Rectangle rectangle = new Rectangle(); rectangle.Width = 2.0; rectangle.Fill = (TryFindResource("BorderColor") as Brush) ?? Brushes.Gray; rectangle.HorizontalAlignment = HorizontalAlignment.Center; rectangle.VerticalAlignment = VerticalAlignment.Stretch; rectangle.Opacity = 0.4; Rectangle element = rectangle; Grid.SetColumn(element, 1); grid.Children.Add(element); StackPanel stackPanel = new StackPanel { Margin = new Thickness(8.0, 0.0, 0.0, 0.0) }; StackPanel stackPanel2 = new StackPanel { Orientation = Orientation.Horizontal }; stackPanel2.Children.Add(new TextBlock { Text = item3, FontSize = 12.0, FontWeight = FontWeights.SemiBold, Foreground = ((TryFindResource("PrimaryText") as Brush) ?? Brushes.White), VerticalAlignment = VerticalAlignment.Center }); if (evt.ElapsedMs > 0) { stackPanel2.Children.Add(new TextBlock { Text = ((evt.ElapsedMs >= 1000) ? $" {(double)evt.ElapsedMs / 1000.0:F1}s" : $" {evt.ElapsedMs}ms"), FontSize = 10.0, Foreground = ((evt.ElapsedMs > 3000) ? Brushes.OrangeRed : ((TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray)), VerticalAlignment = VerticalAlignment.Center }); } stackPanel2.Children.Add(new TextBlock { Text = $" {evt.Timestamp:HH:mm:ss}", FontSize = 9.0, Foreground = ((TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray), VerticalAlignment = VerticalAlignment.Center, Opacity = 0.6 }); stackPanel.Children.Add(stackPanel2); if (!string.IsNullOrEmpty(evt.Summary)) { stackPanel.Children.Add(new TextBlock { Text = Truncate(evt.Summary, 120), FontSize = 11.0, Foreground = ((TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray), TextWrapping = TextWrapping.Wrap, Margin = new Thickness(0.0, 2.0, 0.0, 0.0), LineHeight = 16.0 }); } if (evt.InputTokens > 0 || evt.OutputTokens > 0) { StackPanel stackPanel3 = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0.0, 3.0, 0.0, 0.0) }; stackPanel3.Children.Add(CreateBadge($"↑{evt.InputTokens:#,0}", "#3B82F6")); stackPanel3.Children.Add(CreateBadge($"↓{evt.OutputTokens:#,0}", "#10B981")); stackPanel.Children.Add(stackPanel3); } Grid.SetColumn(stackPanel, 2); grid.Children.Add(stackPanel); node.Child = grid; Brush hoverBrush = (TryFindResource("ItemHoverBackground") as Brush) ?? new SolidColorBrush(Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue)); node.MouseEnter += delegate { node.Background = hoverBrush; }; node.MouseLeave += delegate { node.Background = Brushes.Transparent; }; node.MouseLeftButtonUp += delegate { ShowDetail(evt); }; return node; } private static (string Icon, Color Color, string Label) GetEventVisual(AgentEvent evt) { AgentEventType type = evt.Type; if (1 == 0) { } (string, Color, string) result = type switch { AgentEventType.Thinking => ("\ue7ba", Color.FromRgb(96, 165, 250), "사고"), AgentEventType.Planning => ("\ue9d5", Color.FromRgb(167, 139, 250), "계획"), AgentEventType.StepStart => ("\ue72a", Color.FromRgb(96, 165, 250), $"단계 {evt.StepCurrent}/{evt.StepTotal}"), AgentEventType.StepDone => ("\ue73e", Color.FromRgb(52, 211, 153), "단계 완료"), AgentEventType.ToolCall => ("\ue756", Color.FromRgb(251, 191, 36), evt.ToolName), AgentEventType.ToolResult => ("\ue73e", Color.FromRgb(52, 211, 153), evt.ToolName + " ✓"), AgentEventType.SkillCall => ("\ue768", Color.FromRgb(167, 139, 250), "스킬: " + evt.ToolName), AgentEventType.Error => ("\ue783", Color.FromRgb(248, 113, 113), evt.ToolName + " ✗"), AgentEventType.Complete => ("\ue930", Color.FromRgb(52, 211, 153), "완료"), AgentEventType.Decision => ("\ue8c8", Color.FromRgb(251, 191, 36), "의사결정"), _ => ("\ue946", Color.FromRgb(107, 114, 128), "이벤트"), }; if (1 == 0) { } return result; } private static Border CreateBadge(string text, string colorHex) { Color color = (Color)ColorConverter.ConvertFromString(colorHex); return new Border { Background = new SolidColorBrush(Color.FromArgb(48, color.R, color.G, color.B)), CornerRadius = new CornerRadius(4.0), Padding = new Thickness(5.0, 1.0, 5.0, 1.0), Margin = new Thickness(0.0, 0.0, 4.0, 0.0), Child = new TextBlock { Text = text, FontSize = 9.0, Foreground = new SolidColorBrush(color), FontWeight = FontWeights.SemiBold } }; } private void ShowDetail(AgentEvent evt) { DetailPanel.Visibility = Visibility.Visible; DetailPanel.Tag = evt; (string Icon, Color Color, string Label) eventVisual = GetEventVisual(evt); Color item = eventVisual.Color; string item2 = eventVisual.Label; DetailTitle.Text = item2; DetailBadge.Text = (evt.Success ? "성공" : "실패"); DetailBadge.Background = new SolidColorBrush(evt.Success ? Color.FromRgb(52, 211, 153) : Color.FromRgb(248, 113, 113)); string text = $"시간: {evt.Timestamp:HH:mm:ss.fff}"; if (evt.ElapsedMs > 0) { text = text + " | 소요: " + ((evt.ElapsedMs >= 1000) ? $"{(double)evt.ElapsedMs / 1000.0:F1}s" : $"{evt.ElapsedMs}ms"); } if (evt.InputTokens > 0 || evt.OutputTokens > 0) { text += $" | 토큰: 입력 {evt.InputTokens:#,0} / 출력 {evt.OutputTokens:#,0}"; } if (evt.Iteration > 0) { text += $" | 반복 #{evt.Iteration}"; } DetailMeta.Text = text; string text2 = evt.Summary ?? ""; if (!string.IsNullOrEmpty(evt.ToolInput)) { text2 = text2 + "\n\n파라미터:\n" + Truncate(evt.ToolInput, 500); } if (!string.IsNullOrEmpty(evt.FilePath)) { text2 = text2 + "\n파일: " + evt.FilePath; } DetailContent.Text = text2; } private void UpdateSummaryCards() { double totalSeconds = (DateTime.Now - _startTime).TotalSeconds; CardElapsed.Text = ((totalSeconds >= 60.0) ? $"{totalSeconds / 60.0:F0}m {totalSeconds % 60.0:F0}s" : $"{totalSeconds:F1}s"); CardIterations.Text = _maxIteration.ToString(); int num = _totalInputTokens + _totalOutputTokens; CardTokens.Text = ((num >= 1000) ? $"{(double)num / 1000.0:F1}k" : num.ToString()); CardInputTokens.Text = ((_totalInputTokens >= 1000) ? $"{(double)_totalInputTokens / 1000.0:F1}k" : _totalInputTokens.ToString()); CardOutputTokens.Text = ((_totalOutputTokens >= 1000) ? $"{(double)_totalOutputTokens / 1000.0:F1}k" : _totalOutputTokens.ToString()); CardToolCalls.Text = _totalToolCalls.ToString(); } private static string FormatMs(long ms) { return (ms >= 1000) ? $"{(double)ms / 1000.0:F1}s" : $"{ms}ms"; } private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { object originalSource = e.OriginalSource; DependencyObject val = (DependencyObject)((originalSource is DependencyObject) ? originalSource : null); while (val != null && val != sender) { Border border = val as Border; bool flag = border != null; bool flag2 = flag; if (flag2) { bool flag3; switch (border.Name) { case "BtnClose": case "BtnMinimize": case "BtnClear": flag3 = true; break; default: flag3 = false; break; } flag2 = flag3; } if (flag2) { return; } val = VisualTreeHelper.GetParent(val); } if (e.ClickCount == 1) { DragMove(); } } private void BtnClose_Click(object sender, MouseButtonEventArgs e) { Hide(); } private void BtnMinimize_Click(object sender, MouseButtonEventArgs e) { base.WindowState = WindowState.Minimized; } private void BtnClear_Click(object sender, MouseButtonEventArgs e) { Reset(); } private void TitleBtn_MouseEnter(object sender, MouseEventArgs e) { if (sender is Border border) { border.Background = (TryFindResource("ItemHoverBackground") as Brush) ?? new SolidColorBrush(Color.FromArgb(24, byte.MaxValue, byte.MaxValue, byte.MaxValue)); } } private void TitleBtn_MouseLeave(object sender, MouseEventArgs e) { if (sender is Border border) { border.Background = Brushes.Transparent; } } private static string Truncate(string text, int maxLen) { return (text.Length <= maxLen) ? text : (text.Substring(0, maxLen) + "…"); } private nint WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (msg == 132) { Point val = PointFromScreen(new Point((double)(short)(((IntPtr)lParam).ToInt32() & 0xFFFF), (double)(short)((((IntPtr)lParam).ToInt32() >> 16) & 0xFFFF))); double actualWidth = base.ActualWidth; double actualHeight = base.ActualHeight; if (((Point)(ref val)).X < 8.0 && ((Point)(ref val)).Y < 8.0) { handled = true; return 13; } if (((Point)(ref val)).X > actualWidth - 8.0 && ((Point)(ref val)).Y < 8.0) { handled = true; return 14; } if (((Point)(ref val)).X < 8.0 && ((Point)(ref val)).Y > actualHeight - 8.0) { handled = true; return 16; } if (((Point)(ref val)).X > actualWidth - 8.0 && ((Point)(ref val)).Y > actualHeight - 8.0) { handled = true; return 17; } if (((Point)(ref val)).X < 8.0) { handled = true; return 10; } if (((Point)(ref val)).X > actualWidth - 8.0) { handled = true; return 11; } if (((Point)(ref val)).Y < 8.0) { handled = true; return 12; } if (((Point)(ref val)).Y > actualHeight - 8.0) { handled = true; return 15; } } return IntPtr.Zero; } [DebuggerNonUserCode] [GeneratedCode("PresentationBuildTasks", "10.0.5.0")] public void InitializeComponent() { if (!_contentLoaded) { _contentLoaded = true; Uri resourceLocator = new Uri("/AxCopilot;component/views/workflowanalyzerwindow.xaml", UriKind.Relative); Application.LoadComponent(this, resourceLocator); } } [DebuggerNonUserCode] [GeneratedCode("PresentationBuildTasks", "10.0.5.0")] [EditorBrowsable(EditorBrowsableState.Never)] void IComponentConnector.Connect(int connectionId, object target) { switch (connectionId) { case 1: ((Grid)target).MouseLeftButtonDown += TitleBar_MouseLeftButtonDown; break; case 2: BtnClear = (Border)target; BtnClear.MouseLeftButtonUp += BtnClear_Click; BtnClear.MouseEnter += TitleBtn_MouseEnter; BtnClear.MouseLeave += TitleBtn_MouseLeave; break; case 3: BtnMinimize = (Border)target; BtnMinimize.MouseLeftButtonUp += BtnMinimize_Click; BtnMinimize.MouseEnter += TitleBtn_MouseEnter; BtnMinimize.MouseLeave += TitleBtn_MouseLeave; break; case 4: BtnClose = (Border)target; BtnClose.MouseLeftButtonUp += BtnClose_Click; BtnClose.MouseEnter += TitleBtn_MouseEnter; BtnClose.MouseLeave += TitleBtn_MouseLeave; break; case 5: CardElapsed = (TextBlock)target; break; case 6: CardIterations = (TextBlock)target; break; case 7: CardTokens = (TextBlock)target; break; case 8: CardInputTokens = (TextBlock)target; break; case 9: CardOutputTokens = (TextBlock)target; break; case 10: CardToolCalls = (TextBlock)target; break; case 11: TabTimeline = (Border)target; TabTimeline.MouseLeftButtonUp += TabTimeline_Click; break; case 12: TabTimelineIcon = (TextBlock)target; break; case 13: TabTimelineText = (TextBlock)target; break; case 14: TabBottleneck = (Border)target; TabBottleneck.MouseLeftButtonUp += TabBottleneck_Click; break; case 15: TabBottleneckIcon = (TextBlock)target; break; case 16: TabBottleneckText = (TextBlock)target; break; case 17: TimelineScroller = (ScrollViewer)target; break; case 18: TimelinePanel = (StackPanel)target; break; case 19: BottleneckScroller = (ScrollViewer)target; break; case 20: BottleneckPanel = (StackPanel)target; break; case 21: WaterfallCanvas = (Canvas)target; break; case 22: ToolTimePanel = (StackPanel)target; break; case 23: TokenTrendCanvas = (Canvas)target; break; case 24: DetailPanel = (Border)target; break; case 25: DetailTitle = (TextBlock)target; break; case 26: DetailBadge = (TextBlock)target; break; case 27: DetailMeta = (TextBlock)target; break; case 28: DetailContent = (TextBlock)target; break; case 29: StatusText = (TextBlock)target; break; case 30: LogLevelBadge = (TextBlock)target; break; default: _contentLoaded = true; break; } } }