diff --git a/README.md b/README.md index e04cb3d..f6fae3b 100644 --- a/README.md +++ b/README.md @@ -1368,3 +1368,6 @@ MIT License - AX Agent 루프에 남아 있던 `무료 티어 모드` 대기를 Gemini 서비스에서만 적용하도록 좁혔습니다. 이제 예전 Gemini 무료 티어용 대기 설정이 vLLM/Ollama/Claude 같은 다른 서비스 작업을 불필요하게 늦추지 않습니다. - AX Agent 내부 설정의 `Fast` 표기를 `Gemini 무료 티어 대기`로 바꾸고 설명 문구도 실제 동작 기준으로 수정했습니다. 사용자는 이제 내부 설정에서 이 대기를 명확히 끄고 켤 수 있습니다. - Cowork/Code 중간 진행 정보가 hover처럼 우연히만 보이던 문제를 줄이기 위해 에이전트 이벤트 카드 스타일을 다시 조정했습니다. 단계 시작, 도구 호출, 대기/생각 중 상태가 더 큰 글씨와 얇은 배경 카드로 기본 노출되도록 정리해, 장시간 작업 중에도 “지금 무엇을 하는지”를 본문에서 바로 읽을 수 있게 했습니다. +- 업데이트: 2026-04-06 22:31 (KST) + - AX Agent의 중간 처리 메시지 형식을 `claw-code`에 더 가깝게 재정리했습니다. `Thinking / ToolCall / StepStart / Planning` 계열은 작은 상태칩보다 `요약줄 + 본문 설명` 구조로 보이게 바꿔, 장시간 작업 중 “무슨 작업을 하고 있는지”를 일반 메시지처럼 읽을 수 있게 했습니다. + - 진행 메시지의 요약줄은 좌측 chevron과 보조 텍스트 중심으로 정리하고, 상세 설명은 바로 아래 본문 텍스트로 분리해 `claw-code / Claude Code`의 처리 메시지 밀도와 감각에 더 가깝게 맞췄습니다. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 5623ede..ff87eb8 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -5058,3 +5058,11 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎. - 단계 시작 이벤트도 progress bar만 갱신하고 끝나는 것이 아니라 transcript pill로 함께 남기도록 변경 - `ToolCall` 이벤트를 기본 transcript에서 숨기지 않도록 복원 - 생각/단계/도구 호출 카드의 글씨 크기, 배경, 테두리, 패딩을 키워 hover로 우연히 보이는 tiny 로그 느낌을 줄임 + +## 2026-04-06 22:31 (KST) + +- [ChatWindow.AgentEventRendering.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.AgentEventRendering.cs)의 중간 처리 메시지 렌더를 `claude-code` 레퍼런스에 더 가깝게 재구성했다. + - `Planning`, `StepStart`, `StepDone`, `Thinking`, `ToolCall`, `SkillCall`을 별도 `process feed event`로 분리 + - 기존의 작은 pill 카드 대신 `chevron 요약줄 + 본문 설명` 구조로 렌더 + - 장시간 작업 중에도 단계/툴 실행/생각 중 상태가 일반 assistant 진행 메시지처럼 읽히도록 정리 +- permission/result 카드와 진행 중 feed를 분리해, 실제 처리 메시지는 `claude-code / Claude Code`처럼 더 담백한 transcript 흐름으로 보이게 조정했다. diff --git a/src/AxCopilot/Views/ChatWindow.AgentEventRendering.cs b/src/AxCopilot/Views/ChatWindow.AgentEventRendering.cs index f42ea35..f1baddf 100644 --- a/src/AxCopilot/Views/ChatWindow.AgentEventRendering.cs +++ b/src/AxCopilot/Views/ChatWindow.AgentEventRendering.cs @@ -14,12 +14,12 @@ public partial class ChatWindow { return new Border { - Background = hintBg, - BorderBrush = borderBrush, - BorderThickness = new Thickness(1), - CornerRadius = new CornerRadius(999), - Padding = new Thickness(8, 4, 8, 4), - Margin = new Thickness(8, 3, 180, 3), + Background = Brushes.Transparent, + BorderBrush = Brushes.Transparent, + BorderThickness = new Thickness(0), + CornerRadius = new CornerRadius(0), + Padding = new Thickness(0), + Margin = new Thickness(12, 4, 12, 2), HorizontalAlignment = HorizontalAlignment.Left, Child = new StackPanel { @@ -28,26 +28,144 @@ public partial class ChatWindow { new TextBlock { - Text = "\uE9CE", - FontFamily = new FontFamily("Segoe MDL2 Assets"), - FontSize = 9.5, - Foreground = accentBrush, + Text = "›", + FontSize = 12, + Foreground = secondaryText, VerticalAlignment = VerticalAlignment.Center, + Margin = new Thickness(0, 0, 6, 0), }, new TextBlock { Text = summary, - FontSize = 9.5, - FontWeight = FontWeights.Medium, - Foreground = primaryText, - Margin = new Thickness(5, 0, 0, 0), + FontSize = 11, + FontWeight = FontWeights.Normal, + Foreground = secondaryText, VerticalAlignment = VerticalAlignment.Center, + TextWrapping = TextWrapping.Wrap, } } } }; } + private Border CreateProcessFeedBody(string text, Brush primaryText) + { + return new Border + { + Background = Brushes.Transparent, + BorderBrush = Brushes.Transparent, + BorderThickness = new Thickness(0), + Padding = new Thickness(0), + Margin = new Thickness(26, 3, 12, 8), + Child = new TextBlock + { + Text = text, + FontSize = 13.5, + FontWeight = FontWeights.Medium, + Foreground = primaryText, + TextWrapping = TextWrapping.Wrap, + } + }; + } + + private static bool IsProcessFeedEvent(AgentEvent evt) + { + return evt.Type is AgentEventType.Planning + or AgentEventType.StepStart + or AgentEventType.StepDone + or AgentEventType.Thinking + or AgentEventType.ToolCall + or AgentEventType.SkillCall; + } + + private static string BuildProcessFeedSummary(AgentEvent evt, string transcriptBadgeLabel, string itemDisplayName) + { + return evt.Type switch + { + AgentEventType.Planning when evt.Steps is { Count: > 0 } + => $"{evt.Steps.Count}단계 계획 정리", + AgentEventType.StepStart when evt.StepTotal > 0 + => $"{evt.StepCurrent}/{evt.StepTotal} 단계 진행", + AgentEventType.StepDone when evt.StepTotal > 0 + => $"{evt.StepCurrent}/{evt.StepTotal} 단계 완료", + AgentEventType.Thinking when !string.IsNullOrWhiteSpace(evt.Summary) + => evt.Summary, + AgentEventType.ToolCall + => string.IsNullOrWhiteSpace(itemDisplayName) + ? $"{transcriptBadgeLabel} 실행" + : $"{itemDisplayName} 실행", + AgentEventType.SkillCall + => string.IsNullOrWhiteSpace(itemDisplayName) + ? "스킬 실행" + : $"{itemDisplayName} 실행", + _ => string.IsNullOrWhiteSpace(evt.Summary) ? transcriptBadgeLabel : evt.Summary, + }; + } + + private void AddProcessFeedMessage(AgentEvent evt, string transcriptBadgeLabel, string itemDisplayName, string? eventSummaryText) + { + var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.Black; + var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.DimGray; + var hintBg = TryFindResource("HintBackground") as Brush + ?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF)); + var borderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray; + var accentBrush = TryFindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue; + + var summary = BuildProcessFeedSummary(evt, transcriptBadgeLabel, itemDisplayName).Trim(); + if (string.IsNullOrWhiteSpace(summary)) + summary = transcriptBadgeLabel; + + var stack = new StackPanel + { + Margin = new Thickness(0), + }; + + var summaryRow = CreateCompactEventPill(summary, primaryText, secondaryText, hintBg, borderBrush, accentBrush); + summaryRow.Opacity = 0; + summaryRow.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(140))); + stack.Children.Add(summaryRow); + + var body = (eventSummaryText ?? string.Empty).Trim(); + if (!string.IsNullOrWhiteSpace(body) + && !string.Equals(body, summary, StringComparison.OrdinalIgnoreCase)) + { + var bodyBlock = CreateProcessFeedBody(body, primaryText); + bodyBlock.Opacity = 0; + bodyBlock.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(180))); + stack.Children.Add(bodyBlock); + } + + if (!string.IsNullOrWhiteSpace(evt.FilePath)) + { + var compactPathRow = new StackPanel + { + Orientation = Orientation.Horizontal, + Margin = new Thickness(26, 0, 12, 8), + ToolTip = evt.FilePath, + }; + compactPathRow.Children.Add(new TextBlock + { + Text = "\uE8B7", + FontFamily = new FontFamily("Segoe MDL2 Assets"), + FontSize = 9, + Foreground = secondaryText, + VerticalAlignment = VerticalAlignment.Center, + Margin = new Thickness(0, 0, 4, 0), + }); + compactPathRow.Children.Add(new TextBlock + { + Text = System.IO.Path.GetFileName(evt.FilePath), + FontSize = 10.5, + Foreground = secondaryText, + VerticalAlignment = VerticalAlignment.Center, + TextTrimming = TextTrimming.CharacterEllipsis, + }); + stack.Children.Add(compactPathRow); + } + + MessagePanel.Children.Add(stack); + } + private Border CreateAgentMetaChip(string text, string icon, Brush foreground, Brush background, Brush borderBrush) { return new Border @@ -422,59 +540,6 @@ public partial class ChatWindow { var logLevel = _settings.Settings.Llm.AgentLogLevel; - if (evt.Type == AgentEventType.Planning && evt.Steps is { Count: > 0 }) - { - var compactPrimaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White; - var compactSecondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; - var compactHintBg = TryFindResource("HintBackground") as Brush - ?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF)); - var compactBorderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray; - var compactAccentBrush = TryFindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue; - var summary = !string.IsNullOrWhiteSpace(evt.Summary) - ? evt.Summary! - : $"계획 {evt.Steps.Count}단계"; - var pill = CreateCompactEventPill(summary, compactPrimaryText, compactSecondaryText, compactHintBg, compactBorderBrush, compactAccentBrush); - pill.Opacity = 0; - pill.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(160))); - MessagePanel.Children.Add(pill); - return; - } - - if (evt.Type == AgentEventType.StepStart && evt.StepTotal > 0) - { - UpdateProgressBar(evt); - var compactPrimaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White; - var compactSecondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; - var compactHintBg = TryFindResource("HintBackground") as Brush - ?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF)); - var compactBorderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray; - var compactAccentBrush = TryFindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue; - var stepSummary = !string.IsNullOrWhiteSpace(evt.Summary) - ? evt.Summary! - : $"단계 진행 중 ({evt.StepCurrent}/{evt.StepTotal})"; - var pill = CreateCompactEventPill(stepSummary, compactPrimaryText, compactSecondaryText, compactHintBg, compactBorderBrush, compactAccentBrush); - pill.Opacity = 0; - pill.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(160))); - MessagePanel.Children.Add(pill); - return; - } - - if (evt.Type == AgentEventType.Thinking && - ContainsAny(evt.Summary ?? "", "컨텍스트 압축", "microcompact", "session memory", "compact")) - { - var compactPrimaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White; - var compactSecondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; - var compactHintBg = TryFindResource("HintBackground") as Brush - ?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF)); - var compactBorderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray; - var compactAccentBrush = TryFindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue; - var pill = CreateCompactEventPill(string.IsNullOrWhiteSpace(evt.Summary) ? "컨텍스트 압축" : evt.Summary, compactPrimaryText, compactSecondaryText, compactHintBg, compactBorderBrush, compactAccentBrush); - pill.Opacity = 0; - pill.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(160))); - MessagePanel.Children.Add(pill); - return; - } - if (!string.Equals(logLevel, "debug", StringComparison.OrdinalIgnoreCase) && evt.Type is AgentEventType.Paused or AgentEventType.Resumed) return; @@ -524,6 +589,15 @@ public partial class ChatWindow }; } + if (evt.Type == AgentEventType.StepStart && evt.StepTotal > 0) + UpdateProgressBar(evt); + + if (IsProcessFeedEvent(evt)) + { + AddProcessFeedMessage(evt, transcriptBadgeLabel, itemDisplayName, eventSummaryText); + return; + } + var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.Black; var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.DimGray; var hintBg = TryFindResource("HintBackground") as Brush ?? BrushFromHex("#F8FAFC");