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

1198 lines
35 KiB
C#

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<WaterfallEntry> _waterfallEntries = new List<WaterfallEntry>();
private readonly Dictionary<string, long> _toolTimeAccum = new Dictionary<string, long>();
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<KeyValuePair<string, long>, (string, long)>((KeyValuePair<string, long> 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<int> 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<Point> 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;
}
}
}