using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Shapes; using AxCopilot.Services; namespace AxCopilot.Views; public class AgentStatsDashboardWindow : Window, IComponentConnector { private int _currentDays = 1; internal Border BtnClose; internal Border FilterToday; internal Border Filter7d; internal Border Filter30d; internal Border FilterAll; internal UniformGrid SummaryCards; internal TextBlock DailyChartTitle; internal Canvas DailyBarChart; internal Canvas DailyBarLabels; internal StackPanel ToolFreqPanel; internal StackPanel TabBreakPanel; internal StackPanel ModelBreakPanel; internal TextBlock StatusText; private bool _contentLoaded; public AgentStatsDashboardWindow() { InitializeComponent(); base.Loaded += delegate { LoadStats(_currentDays); }; } private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) Point position = e.GetPosition(this); if (!(((Point)(ref position)).X > base.ActualWidth - 50.0)) { if (e.ClickCount == 2) { base.WindowState = ((base.WindowState != WindowState.Maximized) ? WindowState.Maximized : WindowState.Normal); } else { DragMove(); } } } private void BtnClose_Click(object sender, MouseButtonEventArgs e) { Close(); } private void Filter_Click(object sender, MouseButtonEventArgs e) { if (sender is Border { Tag: string tag } border && int.TryParse(tag, out var result)) { _currentDays = result; UpdateFilterButtons(border); LoadStats(result); } } private void BtnClearStats_Click(object sender, MouseButtonEventArgs e) { MessageBoxResult messageBoxResult = CustomMessageBox.Show("모든 통계 데이터를 삭제하시겠습니까?\n이 작업은 되돌릴 수 없습니다.", "통계 초기화", MessageBoxButton.YesNo, MessageBoxImage.Question); if (messageBoxResult == MessageBoxResult.Yes) { AgentStatsService.Clear(); LoadStats(_currentDays); } } private void LoadStats(int days) { AgentStatsService.AgentStatsSummary s = AgentStatsService.Aggregate(days); RenderSummaryCards(s); RenderDailyChart(s, days); RenderToolFreq(s); RenderBreakdowns(s); UpdateStatus(s, days); } private void RenderSummaryCards(AgentStatsService.AgentStatsSummary s) { SummaryCards.Children.Clear(); SummaryCards.Children.Add(MakeSummaryCard("\ue9d9", "총 실행", s.TotalSessions.ToString("N0"), "#A78BFA")); SummaryCards.Children.Add(MakeSummaryCard("\ue8a7", "도구 호출", s.TotalToolCalls.ToString("N0"), "#3B82F6")); SummaryCards.Children.Add(MakeSummaryCard("\ue7c8", "총 토큰", FormatTokens(s.TotalTokens), "#10B981")); SummaryCards.Children.Add(MakeSummaryCard("\ue916", "평균 시간", (s.TotalSessions > 0) ? $"{TimeSpan.FromMilliseconds((double)s.TotalDurationMs / (double)s.TotalSessions):mm\\:ss}" : "—", "#F59E0B")); } private Border MakeSummaryCard(string icon, string label, string value, string colorHex) { Brush background = (TryFindResource("ItemBackground") as Brush) ?? new SolidColorBrush(Color.FromRgb(42, 42, 64)); Brush foreground = (TryFindResource("PrimaryText") as Brush) ?? Brushes.White; Brush foreground2 = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray; Color color = (Color)ColorConverter.ConvertFromString(colorHex); Border border = new Border { Background = background, CornerRadius = new CornerRadius(10.0), Padding = new Thickness(14.0, 12.0, 14.0, 12.0), Margin = new Thickness(0.0, 0.0, 8.0, 0.0) }; StackPanel stackPanel = new StackPanel(); stackPanel.Children.Add(new TextBlock { Text = icon, FontFamily = new FontFamily("Segoe MDL2 Assets"), FontSize = 16.0, Foreground = new SolidColorBrush(color), Margin = new Thickness(0.0, 0.0, 0.0, 6.0) }); stackPanel.Children.Add(new TextBlock { Text = value, FontSize = 20.0, FontWeight = FontWeights.Bold, Foreground = foreground }); stackPanel.Children.Add(new TextBlock { Text = label, FontSize = 11.0, Foreground = foreground2, Margin = new Thickness(0.0, 2.0, 0.0, 0.0) }); border.Child = stackPanel; return border; } private void RenderDailyChart(AgentStatsService.AgentStatsSummary s, int days) { DailyBarChart.Children.Clear(); DailyBarLabels.Children.Clear(); DateTime endDate = DateTime.Today; int chartDays = days switch { 0 => 30, 1 => 1, _ => days, }; List list = (from i in Enumerable.Range(0, chartDays) select endDate.AddDays(-(chartDays - 1 - i))).ToList(); int num = list.Select((DateTime d) => s.DailySessions.GetValueOrDefault(d.ToString("yyyy-MM-dd"), 0)).DefaultIfEmpty(0).Max(); if (num == 0) { num = 1; } Color color = (Color)ColorConverter.ConvertFromString("#4B5EFC"); double num2 = 748.0; double num3 = 2.0; double num4 = Math.Max(4.0, (num2 - num3 * (double)list.Count) / (double)list.Count); double num5 = 100.0; for (int num6 = 0; num6 < list.Count; num6++) { string text = list[num6].ToString("yyyy-MM-dd"); int valueOrDefault = s.DailySessions.GetValueOrDefault(text, 0); double num7 = Math.Max(2.0, (double)valueOrDefault / (double)num * (num5 - 4.0)); double length = (double)num6 * (num4 + num3); Rectangle element = new Rectangle { Width = num4, Height = num7, Fill = new SolidColorBrush(Color.FromArgb(204, color.R, color.G, color.B)), RadiusX = 3.0, RadiusY = 3.0, ToolTip = $"{text}: {valueOrDefault}회" }; Canvas.SetLeft(element, length); Canvas.SetTop(element, num5 - num7); DailyBarChart.Children.Add(element); if (list.Count <= 7 || list[num6].DayOfWeek == DayOfWeek.Monday) { TextBlock textBlock = new TextBlock(); textBlock.Text = list[num6].ToString("MM/dd"); textBlock.FontSize = 9.0; textBlock.Foreground = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray; TextBlock element2 = textBlock; Canvas.SetLeft(element2, length); DailyBarLabels.Children.Add(element2); } } } private void RenderToolFreq(AgentStatsService.AgentStatsSummary s) { ToolFreqPanel.Children.Clear(); if (s.ToolFrequency.Count == 0) { ToolFreqPanel.Children.Add(new TextBlock { Text = "데이터 없음", FontSize = 12.0, Foreground = ((TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray) }); return; } int num = s.ToolFrequency.Values.Max(); Color color = (Color)ColorConverter.ConvertFromString("#3B82F6"); foreach (KeyValuePair item in s.ToolFrequency) { item.Deconstruct(out var key, out var value); string text = key; int num2 = value; double num3 = (double)num2 / (double)num; Grid grid = new Grid { Margin = new Thickness(0.0, 2.0, 0.0, 2.0) }; grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(120.0) }); grid.ColumnDefinitions.Add(new ColumnDefinition()); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(36.0) }); TextBlock textBlock = new TextBlock(); textBlock.Text = text; textBlock.FontSize = 11.0; textBlock.FontFamily = new FontFamily("Consolas"); textBlock.Foreground = (TryFindResource("PrimaryText") as Brush) ?? Brushes.White; textBlock.TextTrimming = TextTrimming.CharacterEllipsis; textBlock.VerticalAlignment = VerticalAlignment.Center; TextBlock element = textBlock; Grid.SetColumn(element, 0); Border border = new Border { Background = new SolidColorBrush(Color.FromArgb(32, 59, 130, 246)), CornerRadius = new CornerRadius(3.0), Height = 10.0, Margin = new Thickness(6.0, 0.0, 4.0, 0.0), VerticalAlignment = VerticalAlignment.Center }; Border border2 = new Border { Background = new SolidColorBrush(Color.FromArgb(204, color.R, color.G, color.B)), CornerRadius = new CornerRadius(3.0), Height = 10.0, HorizontalAlignment = HorizontalAlignment.Left }; border2.Width = num3 * 150.0; border.Child = border2; Grid.SetColumn(border, 1); textBlock = new TextBlock(); textBlock.Text = num2.ToString(); textBlock.FontSize = 11.0; textBlock.FontWeight = FontWeights.SemiBold; textBlock.Foreground = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray; textBlock.HorizontalAlignment = HorizontalAlignment.Right; textBlock.VerticalAlignment = VerticalAlignment.Center; TextBlock element2 = textBlock; Grid.SetColumn(element2, 2); grid.Children.Add(element); grid.Children.Add(border); grid.Children.Add(element2); ToolFreqPanel.Children.Add(grid); } } private void RenderBreakdowns(AgentStatsService.AgentStatsSummary s) { TabBreakPanel.Children.Clear(); Dictionary dictionary = new Dictionary { ["Cowork"] = "#A78BFA", ["Code"] = "#34D399", ["Chat"] = "#3B82F6" }; string key; int value; foreach (KeyValuePair item in s.TabBreakdown.OrderByDescending, int>((KeyValuePair kv) => kv.Value)) { item.Deconstruct(out key, out value); string text = key; int count = value; string valueOrDefault = dictionary.GetValueOrDefault(text, "#9CA3AF"); TabBreakPanel.Children.Add(MakeBreakRow(text, count, valueOrDefault, s.TabBreakdown.Values.DefaultIfEmpty(1).Max())); } if (s.TabBreakdown.Count == 0) { TabBreakPanel.Children.Add(MakeEmptyText()); } ModelBreakPanel.Children.Clear(); foreach (KeyValuePair item2 in s.ModelBreakdown.OrderByDescending, int>((KeyValuePair kv) => kv.Value)) { item2.Deconstruct(out key, out value); string text2 = key; int count2 = value; string label = ((text2.Length > 18) ? (text2.Substring(0, 18) + "…") : text2); ModelBreakPanel.Children.Add(MakeBreakRow(label, count2, "#F59E0B", s.ModelBreakdown.Values.DefaultIfEmpty(1).Max())); } if (s.ModelBreakdown.Count == 0) { ModelBreakPanel.Children.Add(MakeEmptyText()); } } private UIElement MakeBreakRow(string label, int count, string colorHex, int maxVal) { double num = ((maxVal > 0) ? ((double)count / (double)maxVal) : 0.0); Color color = (Color)ColorConverter.ConvertFromString(colorHex); StackPanel stackPanel = new StackPanel { Margin = new Thickness(0.0, 2.0, 0.0, 2.0) }; Grid grid = new Grid(); grid.ColumnDefinitions.Add(new ColumnDefinition()); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0, GridUnitType.Auto) }); TextBlock textBlock = new TextBlock(); textBlock.Text = label; textBlock.FontSize = 11.0; textBlock.Foreground = (TryFindResource("PrimaryText") as Brush) ?? Brushes.White; TextBlock element = textBlock; Grid.SetColumn(element, 0); textBlock = new TextBlock(); textBlock.Text = count.ToString(); textBlock.FontSize = 11.0; textBlock.FontWeight = FontWeights.SemiBold; textBlock.Foreground = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray; TextBlock element2 = textBlock; Grid.SetColumn(element2, 1); grid.Children.Add(element); grid.Children.Add(element2); stackPanel.Children.Add(grid); Border border = new Border { Background = new SolidColorBrush(Color.FromArgb(32, color.R, color.G, color.B)), CornerRadius = new CornerRadius(3.0), Height = 6.0, Margin = new Thickness(0.0, 3.0, 0.0, 0.0) }; Border border2 = new Border { Background = new SolidColorBrush(Color.FromArgb(204, color.R, color.G, color.B)), CornerRadius = new CornerRadius(3.0), Height = 6.0, HorizontalAlignment = HorizontalAlignment.Left }; border2.Width = num * 160.0; border.Child = border2; stackPanel.Children.Add(border); return stackPanel; } private TextBlock MakeEmptyText() { TextBlock textBlock = new TextBlock(); textBlock.Text = "데이터 없음"; textBlock.FontSize = 11.0; textBlock.Foreground = (TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray; return textBlock; } private void UpdateFilterButtons(Border active) { Border[] array = new Border[4] { FilterToday, Filter7d, Filter30d, FilterAll }; foreach (Border border in array) { bool flag = border == active; border.Background = (flag ? new SolidColorBrush(Color.FromRgb(75, 94, 252)) : ((TryFindResource("ItemBackground") as Brush) ?? new SolidColorBrush(Color.FromRgb(48, 48, 69)))); if (border.Child is TextBlock textBlock) { textBlock.Foreground = (flag ? Brushes.White : ((TryFindResource("SecondaryText") as Brush) ?? Brushes.Gray)); } } } private void UpdateStatus(AgentStatsService.AgentStatsSummary s, int days) { if (1 == 0) { } string text = days switch { 1 => "오늘", 7 => "최근 7일", 30 => "최근 30일", _ => "전체 기간", }; if (1 == 0) { } string value = text; long fileSize = AgentStatsService.GetFileSize(); StatusText.Text = $"{value} 기준 | 세션 {s.TotalSessions}회, 도구 호출 {s.TotalToolCalls}회, 토큰 {FormatTokens(s.TotalTokens)} | 저장 파일 {FormatFileSize(fileSize)}"; } private static string FormatTokens(int t) { return (t >= 1000000) ? $"{(double)t / 1000000.0:F1}M" : ((t >= 1000) ? $"{(double)t / 1000.0:F1}K" : t.ToString()); } private static string FormatFileSize(long bytes) { return (bytes >= 1048576) ? $"{(double)bytes / 1048576.0:F1}MB" : ((bytes >= 1024) ? $"{(double)bytes / 1024.0:F1}KB" : $"{bytes}B"); } [DebuggerNonUserCode] [GeneratedCode("PresentationBuildTasks", "10.0.5.0")] public void InitializeComponent() { if (!_contentLoaded) { _contentLoaded = true; Uri resourceLocator = new Uri("/AxCopilot;component/views/agentstatsdashboardwindow.xaml", UriKind.Relative); Application.LoadComponent(this, resourceLocator); } } [DebuggerNonUserCode] [GeneratedCode("PresentationBuildTasks", "10.0.5.0")] [EditorBrowsable(EditorBrowsableState.Never)] void IComponentConnector.Connect(int connectionId, object target) { switch (connectionId) { case 1: ((Border)target).MouseLeftButtonDown += TitleBar_MouseLeftButtonDown; break; case 2: BtnClose = (Border)target; BtnClose.MouseLeftButtonUp += BtnClose_Click; break; case 3: FilterToday = (Border)target; FilterToday.MouseLeftButtonUp += Filter_Click; break; case 4: Filter7d = (Border)target; Filter7d.MouseLeftButtonUp += Filter_Click; break; case 5: Filter30d = (Border)target; Filter30d.MouseLeftButtonUp += Filter_Click; break; case 6: FilterAll = (Border)target; FilterAll.MouseLeftButtonUp += Filter_Click; break; case 7: ((Border)target).MouseLeftButtonUp += BtnClearStats_Click; break; case 8: SummaryCards = (UniformGrid)target; break; case 9: DailyChartTitle = (TextBlock)target; break; case 10: DailyBarChart = (Canvas)target; break; case 11: DailyBarLabels = (Canvas)target; break; case 12: ToolFreqPanel = (StackPanel)target; break; case 13: TabBreakPanel = (StackPanel)target; break; case 14: ModelBreakPanel = (StackPanel)target; break; case 15: StatusText = (TextBlock)target; break; default: _contentLoaded = true; break; } } }