From fda5c6baceca6a4d1106d7aefa07b737bcea90d0 Mon Sep 17 00:00:00 2001 From: lacvet Date: Mon, 6 Apr 2026 10:33:57 +0900 Subject: [PATCH] =?UTF-8?q?AX=20Agent=20timeline=20=EC=A1=B0=EB=A6=BD=20he?= =?UTF-8?q?lper=EB=A5=BC=20=EB=B6=84=EB=A6=AC=ED=95=B4=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EB=A0=8C=EB=8D=94=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=EB=A5=BC=20=EB=8B=A8=EC=88=9C=ED=99=94=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RenderMessages()가 직접 처리하던 visible 메시지/이벤트 필터링과 timestamp/order 기반 timeline action 조립을 ChatWindow.TimelinePresentation.cs로 이동해 메인 렌더 루프가 orchestration 중심으로 더 단순해지도록 정리했다. README와 DEVELOPMENT 문서에 2026-04-06 10:36 (KST) 기준 이력을 반영했고, dotnet build 검증 결과 경고 0 / 오류 0을 확인했다. --- README.md | 2 + docs/DEVELOPMENT.md | 2 + .../Views/ChatWindow.TimelinePresentation.cs | 52 +++++++++++++++++++ src/AxCopilot/Views/ChatWindow.xaml.cs | 28 ++-------- 4 files changed, 59 insertions(+), 25 deletions(-) create mode 100644 src/AxCopilot/Views/ChatWindow.TimelinePresentation.cs diff --git a/README.md b/README.md index d3421d9..6d3ff0c 100644 --- a/README.md +++ b/README.md @@ -1184,3 +1184,5 @@ MIT License - 좌측 대화 목록 렌더를 [ChatWindow.ConversationListPresentation.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.ConversationListPresentation.cs) 로 분리했다. `RefreshConversationList`, `RenderConversationList`, `AddLoadMoreButton`, `BuildConversationSpotlightItems`, `AddGroupHeader`, `AddConversationItem`이 메인 [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 밖으로 이동해, 메인 창은 transcript/runtime orchestration에 더 집중하고 목록 UI는 별도 presentation surface에서 관리되게 정리했다. - 업데이트: 2026-04-06 10:27 (KST) - transcript 메시지 row 조립을 [ChatWindow.MessageBubblePresentation.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.MessageBubblePresentation.cs) 로 분리했다. `AddMessageBubble(...)`가 메인 [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 밖으로 이동해, 사용자/assistant bubble, 분기 컨텍스트 카드, 액션 바와 메타 row 조립이 별도 presentation surface에서 관리되게 정리했다. +- 업데이트: 2026-04-06 10:36 (KST) + - timeline 조립 helper를 [ChatWindow.TimelinePresentation.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.TimelinePresentation.cs) 로 분리했다. `RenderMessages()`가 직접 처리하던 visible 메시지 필터링, execution event 노출 집계, timestamp/order 기반 timeline action 조립을 helper 메서드로 옮겨 메인 렌더 루프를 더 단순화했다. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 326df8c..13a62b4 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -4925,3 +4925,5 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎. - Document update: 2026-04-06 10:18 (KST) - This keeps the main chat window more focused on transcript/runtime orchestration while isolating sidebar list presentation for future UX tuning and parity work. - Document update: 2026-04-06 10:27 (KST) - Split transcript message-row assembly out of `ChatWindow.xaml.cs` into `ChatWindow.MessageBubblePresentation.cs`. The shared `AddMessageBubble(...)` path for user/assistant bubbles, branch-context cards, inline action bars, and assistant meta rows now lives in a dedicated presentation partial. - Document update: 2026-04-06 10:27 (KST) - This keeps the main chat window more orchestration-focused while making transcript row rendering easier to tune and extend independently. +- Document update: 2026-04-06 10:36 (KST) - Split timeline visibility filtering and render-action assembly out of `RenderMessages()` into `ChatWindow.TimelinePresentation.cs`. Visible message/event selection and timestamp-ordered timeline action construction now live in dedicated helpers. +- Document update: 2026-04-06 10:36 (KST) - This keeps `RenderMessages()` closer to a simple orchestration loop and reduces mixed responsibilities inside the main chat window file. diff --git a/src/AxCopilot/Views/ChatWindow.TimelinePresentation.cs b/src/AxCopilot/Views/ChatWindow.TimelinePresentation.cs new file mode 100644 index 0000000..6b18e32 --- /dev/null +++ b/src/AxCopilot/Views/ChatWindow.TimelinePresentation.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AxCopilot.Models; + +namespace AxCopilot.Views; + +public partial class ChatWindow +{ + private List GetVisibleTimelineMessages(ChatConversation? conversation) + { + return conversation?.Messages?.Where(msg => + { + if (string.Equals(msg.Role, "system", StringComparison.OrdinalIgnoreCase)) + return false; + + if (string.Equals(msg.Role, "assistant", StringComparison.OrdinalIgnoreCase) + && string.IsNullOrWhiteSpace(msg.Content)) + return false; + + return true; + }).ToList() ?? new List(); + } + + private List GetVisibleTimelineEvents(ChatConversation? conversation) + { + return (conversation?.ShowExecutionHistory ?? true) + ? conversation?.ExecutionEvents?.ToList() ?? new List() + : new List(); + } + + private List<(DateTime Timestamp, int Order, Action Render)> BuildTimelineRenderActions( + IReadOnlyCollection visibleMessages, + IReadOnlyCollection visibleEvents) + { + var timeline = new List<(DateTime Timestamp, int Order, Action Render)>(visibleMessages.Count + visibleEvents.Count); + + foreach (var msg in visibleMessages) + timeline.Add((msg.Timestamp, 0, () => AddMessageBubble(msg.Role, msg.Content, animate: false, message: msg))); + + foreach (var executionEvent in visibleEvents) + { + var restoredEvent = ToAgentEvent(executionEvent); + timeline.Add((executionEvent.Timestamp, 1, () => AddAgentEventBanner(restoredEvent))); + } + + return timeline + .OrderBy(x => x.Timestamp) + .ThenBy(x => x.Order) + .ToList(); + } +} diff --git a/src/AxCopilot/Views/ChatWindow.xaml.cs b/src/AxCopilot/Views/ChatWindow.xaml.cs index e6b828b..428b510 100644 --- a/src/AxCopilot/Views/ChatWindow.xaml.cs +++ b/src/AxCopilot/Views/ChatWindow.xaml.cs @@ -2723,20 +2723,8 @@ public partial class ChatWindow : Window lock (_convLock) conv = _currentConversation; _appState.RestoreAgentRunHistory(conv?.AgentRunHistory); - var visibleMessages = conv?.Messages?.Where(msg => - { - if (string.Equals(msg.Role, "system", StringComparison.OrdinalIgnoreCase)) - return false; - - if (string.Equals(msg.Role, "assistant", StringComparison.OrdinalIgnoreCase) - && string.IsNullOrWhiteSpace(msg.Content)) - return false; - - return true; - }).ToList() ?? new List(); - var visibleEvents = (conv?.ShowExecutionHistory ?? true) - ? conv?.ExecutionEvents?.ToList() ?? new List() - : new List(); + var visibleMessages = GetVisibleTimelineMessages(conv); + var visibleEvents = GetVisibleTimelineEvents(conv); if (conv == null || (visibleMessages.Count == 0 && visibleEvents.Count == 0)) { @@ -2752,17 +2740,7 @@ public partial class ChatWindow : Window EmptyState.Visibility = Visibility.Collapsed; - var timeline = new List<(DateTime Timestamp, int Order, Action Render)>(); - foreach (var msg in visibleMessages) - timeline.Add((msg.Timestamp, 0, () => AddMessageBubble(msg.Role, msg.Content, animate: false, message: msg))); - - foreach (var executionEvent in visibleEvents) - { - var restoredEvent = ToAgentEvent(executionEvent); - timeline.Add((executionEvent.Timestamp, 1, () => AddAgentEventBanner(restoredEvent))); - } - - var orderedTimeline = timeline.OrderBy(x => x.Timestamp).ThenBy(x => x.Order).ToList(); + var orderedTimeline = BuildTimelineRenderActions(visibleMessages, visibleEvents); var hiddenCount = Math.Max(0, orderedTimeline.Count - _timelineRenderLimit); if (hiddenCount > 0) MessagePanel.Children.Add(CreateTimelineLoadMoreCard(hiddenCount));