코워크/코드 실행 중 UI 렌더 부담 완화 및 claude-code 구조 비교 반영
Some checks failed
Release Gate / gate (push) Has been cancelled

- claude-code의 Messages, VirtualMessageList, StatusLine, StreamingToolExecutor 흐름을 다시 비교해 AX의 구조적 병목을 점검함

- Cowork/Code에서 실행 히스토리를 접어 둔 상태일 때 process feed 이벤트가 transcript 전체 재렌더를 자주 유발하던 경로를 줄임

- 경량 라이브 진행 모드를 도입해 라이브 카드와 상태 표시를 우선 사용하고 execution history render / agent UI event timer 간격을 더 느슨하게 조정함

- 완료/오류/문서 결과처럼 기록 가치가 큰 이벤트만 적극적으로 transcript 렌더를 요청하도록 정리함

- README와 DEVELOPMENT 문서를 2026-04-08 12:18 (KST) 기준으로 갱신함

- 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ (경고 0, 오류 0)
This commit is contained in:
2026-04-08 23:29:04 +09:00
parent 1b4a2bfb1c
commit 77b63e7a83
3 changed files with 55 additions and 5 deletions

View File

@@ -165,6 +165,20 @@ public partial class ChatWindow : Window
private DateTime _lastAgentProgressEventAt = DateTime.UtcNow;
private double _lastResponsiveComposerWidth;
private double _lastResponsiveMessageWidth;
private bool IsLightweightLiveProgressMode(string? runTab = null)
{
var tab = string.IsNullOrWhiteSpace(runTab) ? (_streamRunTab ?? _activeTab) : runTab!;
if (!string.Equals(tab, "Cowork", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(tab, "Code", StringComparison.OrdinalIgnoreCase))
return false;
ChatConversation? conversation;
lock (_convLock)
conversation = _currentConversation;
return !(conversation?.ShowExecutionHistory ?? true);
}
private void ApplyQuickActionVisual(Button button, bool active, string activeBg, string activeFg)
{
if (button?.Content is not string text)
@@ -255,7 +269,9 @@ public partial class ChatWindow : Window
_executionHistoryRenderTimer.Stop();
// 스트리밍 중에는 전체 재렌더링 빈도를 줄여 UI 부하 감소
_executionHistoryRenderTimer.Interval = _isStreaming
? TimeSpan.FromMilliseconds(1500)
? (IsLightweightLiveProgressMode()
? TimeSpan.FromMilliseconds(2200)
: TimeSpan.FromMilliseconds(1500))
: TimeSpan.FromMilliseconds(350);
RenderMessages(preserveViewport: true);
if (_pendingExecutionHistoryAutoScroll)
@@ -282,7 +298,9 @@ public partial class ChatWindow : Window
{
_agentUiEventTimer.Stop();
_agentUiEventTimer.Interval = _isStreaming
? TimeSpan.FromMilliseconds(300)
? (IsLightweightLiveProgressMode()
? TimeSpan.FromMilliseconds(420)
: TimeSpan.FromMilliseconds(300))
: TimeSpan.FromMilliseconds(140);
FlushPendingAgentUiEvent();
};
@@ -6819,8 +6837,16 @@ public partial class ChatWindow : Window
// AppendConversationExecutionEvent, AppendConversationAgentRun, 디스크 저장은
// 백그라운드 스레드에서 배치 처리됩니다. UI 렌더 갱신도 배치 완료 후 1회만 호출됩니다.
var shouldShowExecutionHistory = _currentConversation?.ShowExecutionHistory ?? false;
var shouldRender = (shouldShowExecutionHistory || ShouldRenderProgressEventWhenHistoryCollapsed(evt))
&& string.Equals(eventTab, _activeTab, StringComparison.OrdinalIgnoreCase);
var isActiveRunTab = string.Equals(eventTab, _activeTab, StringComparison.OrdinalIgnoreCase);
var lightweightLiveMode = isActiveRunTab
&& !shouldShowExecutionHistory
&& IsLightweightLiveProgressMode(eventTab);
var shouldRender = isActiveRunTab && (
shouldShowExecutionHistory
|| (!lightweightLiveMode && ShouldRenderProgressEventWhenHistoryCollapsed(evt))
|| evt.Type == AgentEventType.Complete
|| evt.Type == AgentEventType.Error
|| (evt.Type == AgentEventType.ToolResult && evt.Success && IsDocumentCreationTool(evt.ToolName)));
EnqueueAgentEventWork(evt, eventTab, shouldRender);
// ── 3단계: 경량 상태 추적 (UI 스레드) ───────────────────────────────
@@ -7430,7 +7456,9 @@ public partial class ChatWindow : Window
};
var runTab = string.IsNullOrWhiteSpace(_streamRunTab) ? _activeTab : _streamRunTab!;
if (MessagePanel != null && string.Equals(runTab, _activeTab, StringComparison.OrdinalIgnoreCase))
if (MessagePanel != null
&& string.Equals(runTab, _activeTab, StringComparison.OrdinalIgnoreCase)
&& !IsLightweightLiveProgressMode(runTab))
ScheduleExecutionHistoryRender(autoScroll: false);
}