using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; using AxCopilot.Models; using AxCopilot.Services; namespace AxCopilot.Views; public partial class ChatWindow { private void RuntimeTaskSummary_Click(object sender, MouseButtonEventArgs e) { e.Handled = true; _taskSummaryTarget = sender as UIElement ?? RuntimeActivityBadge; ShowTaskSummaryPopup(); } private void ShowTaskSummaryPopup() { if (_taskSummaryTarget == null) return; if (_taskSummaryPopup != null) _taskSummaryPopup.IsOpen = false; var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.Black; var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.DimGray; var popupBackground = TryFindResource("LauncherBackground") as Brush ?? Brushes.White; var panel = new StackPanel { Margin = new Thickness(2) }; panel.Children.Add(new TextBlock { Text = "작업 요약", FontSize = 11, FontWeight = FontWeights.SemiBold, Foreground = primaryText, Margin = new Thickness(8, 5, 8, 2), }); panel.Children.Add(new TextBlock { Text = "현재 상태 요약", FontSize = 8.5, Foreground = secondaryText, Margin = new Thickness(8, 0, 8, 5), }); ChatConversation? currentConversation; lock (_convLock) currentConversation = _currentConversation; AddTaskSummaryObservabilitySections(panel, currentConversation); if (!string.IsNullOrWhiteSpace(_appState.AgentRun.RunId)) { var currentRun = new Border { Background = BrushFromHex("#F8FAFC"), BorderBrush = BrushFromHex("#E2E8F0"), BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(8), Padding = new Thickness(8, 6, 8, 6), Margin = new Thickness(6, 0, 6, 6), Child = new StackPanel { Children = { new TextBlock { Text = $"실행 run {ShortRunId(_appState.AgentRun.RunId)}", FontWeight = FontWeights.SemiBold, Foreground = primaryText, FontSize = 9.75, }, new TextBlock { Text = $"{GetRunStatusLabel(_appState.AgentRun.Status)} · step {_appState.AgentRun.LastIteration}", Margin = new Thickness(0, 2, 0, 0), Foreground = GetRunStatusBrush(_appState.AgentRun.Status), FontSize = 9, }, new TextBlock { Text = string.IsNullOrWhiteSpace(_appState.AgentRun.Summary) ? "요약 없음" : _appState.AgentRun.Summary, Margin = new Thickness(0, 3, 0, 0), TextWrapping = TextWrapping.Wrap, Foreground = Brushes.DimGray, FontSize = 9, } } } }; panel.Children.Add(currentRun); } var recentAgentRuns = _appState.GetRecentAgentRuns(1); if (recentAgentRuns.Count > 0) { panel.Children.Add(new TextBlock { Text = "마지막 실행", FontSize = 9, FontWeight = FontWeights.SemiBold, Foreground = Brushes.DimGray, Margin = new Thickness(8, 0, 8, 2), }); foreach (var run in recentAgentRuns) { var runEvents = GetExecutionEventsForRun(run.RunId, 1); var runFilePaths = GetExecutionEventFilePaths(run.RunId, 1); var runDisplay = _appState.GetRunDisplay(run); var runCardStack = new StackPanel { Children = { new TextBlock { Text = runDisplay.HeaderText, FontWeight = FontWeights.SemiBold, Foreground = GetRunStatusBrush(run.Status), FontSize = 9.5, }, new TextBlock { Text = runDisplay.MetaText, Margin = new Thickness(0, 1, 0, 0), Foreground = secondaryText, FontSize = 8.25, }, new TextBlock { Text = TruncateForStatus(runDisplay.SummaryText, 92), Margin = new Thickness(0, 1.5, 0, 0), TextWrapping = TextWrapping.Wrap, Foreground = secondaryText, FontSize = 8.5, } } }; if (runEvents.Count > 0 || runFilePaths.Count > 0) { var activitySummary = new StackPanel(); activitySummary.Children.Add(new TextBlock { Text = $"로그 {runEvents.Count} · 파일 {runFilePaths.Count}", FontSize = 8, Foreground = secondaryText, }); if (!string.IsNullOrWhiteSpace(run.RunId)) { var capturedRunId = run.RunId; var timelineButton = CreateTaskSummaryActionButton( "타임라인", "#F8FAFC", "#CBD5E1", "#334155", (_, _) => ScrollToRunInTimeline(capturedRunId), trailingMargin: false); timelineButton.Margin = new Thickness(0, 5, 0, 0); activitySummary.Children.Add(timelineButton); } runCardStack.Children.Add(new Border { Background = BrushFromHex("#F8FAFC"), BorderBrush = BrushFromHex("#E2E8F0"), BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(7), Padding = new Thickness(6, 4, 6, 4), Margin = new Thickness(0, 5, 0, 0), Child = activitySummary }); } if (string.Equals(run.Status, "completed", StringComparison.OrdinalIgnoreCase)) { var capturedRun = run; var followUpButton = CreateTaskSummaryActionButton( "후속 큐", "#ECFDF5", "#BBF7D0", "#166534", (_, _) => EnqueueFollowUpFromRun(capturedRun), trailingMargin: false); followUpButton.Margin = new Thickness(0, 6, 0, 0); runCardStack.Children.Add(followUpButton); } if (string.Equals(run.Status, "failed", StringComparison.OrdinalIgnoreCase) && CanRetryCurrentConversation()) { var retryButton = CreateTaskSummaryActionButton( "다시 시도", "#FEF2F2", "#FCA5A5", "#991B1B", (_, _) => { _taskSummaryPopup?.SetCurrentValue(Popup.IsOpenProperty, false); RetryLastUserMessageFromConversation(); }, trailingMargin: false); retryButton.Margin = new Thickness(0, 6, 0, 0); runCardStack.Children.Add(retryButton); } panel.Children.Add(new Border { Background = popupBackground, BorderBrush = BrushFromHex("#E5E7EB"), BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(7), Padding = new Thickness(7, 5, 7, 5), Margin = new Thickness(6, 0, 6, 4), Child = runCardStack }); } } var activeTasks = FilterTaskSummaryItems(_appState.ActiveTasks).Take(3).ToList(); var recentTasks = FilterTaskSummaryItems(_appState.RecentTasks).Take(2).ToList(); foreach (var task in activeTasks) panel.Children.Add(BuildTaskSummaryCard(task, active: true)); if (ShouldIncludeRecentTaskSummary(activeTasks)) { foreach (var task in recentTasks) panel.Children.Add(BuildTaskSummaryCard(task, active: false)); } if (activeTasks.Count == 0 && recentTasks.Count == 0) { panel.Children.Add(new TextBlock { Text = "표시할 작업 이력이 없습니다.", Margin = new Thickness(10, 2, 10, 8), Foreground = secondaryText, }); } _taskSummaryPopup = new Popup { PlacementTarget = _taskSummaryTarget, Placement = PlacementMode.Top, AllowsTransparency = true, StaysOpen = false, PopupAnimation = PopupAnimation.Fade, Child = new Border { Background = popupBackground, BorderBrush = BrushFromHex("#E5E7EB"), BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(12), Padding = new Thickness(6), Child = new ScrollViewer { Content = panel, MaxHeight = 340, VerticalScrollBarVisibility = ScrollBarVisibility.Auto, } } }; _taskSummaryPopup.IsOpen = true; } private Border BuildTaskSummaryCard(TaskRunStore.TaskRun task, bool active) { var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.Black; var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.DimGray; var (kindIcon, kindColor) = GetTaskKindVisual(task.Kind); var categoryLabel = GetTranscriptTaskCategory(task); var displayTitle = string.Equals(task.Kind, "tool", StringComparison.OrdinalIgnoreCase) || string.Equals(task.Kind, "permission", StringComparison.OrdinalIgnoreCase) || string.Equals(task.Kind, "hook", StringComparison.OrdinalIgnoreCase) ? GetAgentItemDisplayName(task.Title) : task.Title; var taskStack = new StackPanel(); var headerRow = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 0, 0, 2), }; headerRow.Children.Add(new TextBlock { Text = kindIcon, FontFamily = new FontFamily("Segoe MDL2 Assets"), FontSize = 9.5, Foreground = kindColor, Margin = new Thickness(0, 0, 4, 0), VerticalAlignment = VerticalAlignment.Center, }); headerRow.Children.Add(new TextBlock { Text = active ? $"진행 중 · {displayTitle}" : $"{GetTaskStatusLabel(task.Status)} · {displayTitle}", FontSize = 9.5, FontWeight = FontWeights.SemiBold, Foreground = active ? primaryText : secondaryText, TextWrapping = TextWrapping.Wrap, }); taskStack.Children.Add(headerRow); taskStack.Children.Add(new Border { Background = BrushFromHex("#F8FAFC"), BorderBrush = BrushFromHex("#E5E7EB"), BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(999), Padding = new Thickness(6, 1, 6, 1), Margin = new Thickness(0, 0, 0, 4), HorizontalAlignment = HorizontalAlignment.Left, Child = new TextBlock { Text = categoryLabel, FontSize = 8, FontWeight = FontWeights.SemiBold, Foreground = secondaryText, }, }); if (!string.IsNullOrWhiteSpace(task.Summary)) { taskStack.Children.Add(new TextBlock { Text = TruncateForStatus(task.Summary, 96), FontSize = 8.75, Foreground = secondaryText, TextWrapping = TextWrapping.Wrap, }); } var reviewChipRow = BuildReviewSignalChipRow( kind: task.Kind, toolName: task.Title, title: displayTitle, summary: task.Summary); if (reviewChipRow != null) taskStack.Children.Add(reviewChipRow); var actionRow = BuildTaskSummaryActionRow(task, active); if (actionRow != null) taskStack.Children.Add(actionRow); return new Border { Background = active ? BrushFromHex("#F8FAFC") : (TryFindResource("LauncherBackground") as Brush ?? Brushes.White), BorderBrush = BrushFromHex("#E5E7EB"), BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(7), Padding = new Thickness(8, 5, 8, 5), Margin = new Thickness(8, 0, 8, 4), Child = taskStack }; } }