Some checks are pending
Release Gate / gate (push) Waiting to run
- AgentTranscriptDisplayCatalog를 row presentation 중심으로 재구성해 thinking/waiting/compact/tool activity/permission/tool result/status를 타입별로 분리함 - PermissionRequestPresentationCatalog와 ToolResultPresentationCatalog를 정리해 권한 요청과 결과 상태를 행위/상태 기준으로 더 명확하게 표현함 - ChatWindow.AgentEventRendering에서 process feed 계열 이벤트를 GroupKey 기준으로 병합해 append 수를 줄이고 진행 흐름이 기본 transcript에 남도록 조정함 - FooterPresentation에서 Cowork/Chat 프리셋 안내 카드가 execution event 이후 자동으로 숨겨지도록 하고 입력 워터마크와 footer 기본 문구를 정리함 - render_messages 성능 로그에 processFeed append/merge 수치와 rowKindCounts를 추가해 %APPDATA%\\AxCopilot\\perf 기준 실검증이 가능하도록 함 - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ 기준 경고 0 / 오류 0 확인
125 lines
4.9 KiB
C#
125 lines
4.9 KiB
C#
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Windows.Threading;
|
|
using AxCopilot.Models;
|
|
using AxCopilot.Services;
|
|
|
|
namespace AxCopilot.Views;
|
|
|
|
public partial class ChatWindow
|
|
{
|
|
private int GetActiveTimelineRenderLimit()
|
|
{
|
|
if (!_isStreaming)
|
|
return _timelineRenderLimit;
|
|
|
|
var streamingLimit = IsLightweightLiveProgressMode()
|
|
? TimelineLightweightStreamingRenderLimit
|
|
: TimelineStreamingRenderLimit;
|
|
return Math.Min(_timelineRenderLimit, streamingLimit);
|
|
}
|
|
|
|
private void RenderMessages(bool preserveViewport = false)
|
|
{
|
|
// B-4: 비가시 상태일 때 렌더링 차단 — 최소화/숨김 시 불필요한 UI 재구축 방지
|
|
if (this.WindowState == System.Windows.WindowState.Minimized || !IsVisible)
|
|
return;
|
|
|
|
var renderStopwatch = Stopwatch.StartNew();
|
|
var previousScrollableHeight = GetTranscriptScrollableHeight();
|
|
var previousVerticalOffset = GetTranscriptVerticalOffset();
|
|
|
|
ChatConversation? conv;
|
|
lock (_convLock) conv = _currentConversation;
|
|
_appState.RestoreAgentRunHistory(conv?.AgentRunHistory);
|
|
|
|
var visibleMessages = GetVisibleTimelineMessages(conv);
|
|
var visibleEvents = GetVisibleTimelineEvents(conv);
|
|
|
|
if (_isStreaming && preserveViewport
|
|
&& visibleMessages.Count == _lastRenderedMessageCount
|
|
&& visibleEvents.Count == _lastRenderedEventCount
|
|
&& (conv?.ShowExecutionHistory ?? true) == _lastRenderedShowHistory
|
|
&& string.Equals(_lastRenderedConversationId, conv?.Id, StringComparison.OrdinalIgnoreCase))
|
|
return;
|
|
|
|
if (conv == null || (visibleMessages.Count == 0 && visibleEvents.Count == 0))
|
|
{
|
|
ClearTranscriptElements();
|
|
_runBannerAnchors.Clear();
|
|
_lastRenderedTimelineKeys.Clear();
|
|
_lastRenderedMessageCount = 0;
|
|
_lastRenderedEventCount = 0;
|
|
EmptyState.Visibility = System.Windows.Visibility.Visible;
|
|
return;
|
|
}
|
|
|
|
if (!string.Equals(_lastRenderedConversationId, conv.Id, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
_lastRenderedConversationId = conv.Id;
|
|
_timelineRenderLimit = TimelineRenderPageSize;
|
|
_elementCache.Clear();
|
|
_lastRenderedTimelineKeys.Clear();
|
|
_lastRenderedMessageCount = 0;
|
|
_lastRenderedEventCount = 0;
|
|
InvalidateTimelineCache();
|
|
}
|
|
|
|
EmptyState.Visibility = System.Windows.Visibility.Collapsed;
|
|
var renderPlan = BuildTranscriptRenderPlan(conv, visibleMessages, visibleEvents);
|
|
|
|
// B-3: 스트리밍 전용 빠른 경로 → 일반 인크리멘탈 → 전체 재빌드
|
|
if (!TryApplyStreamingAppendRender(renderPlan)
|
|
&& !TryApplyIncrementalTranscriptRender(renderPlan))
|
|
ApplyFullTranscriptRender(renderPlan);
|
|
PruneTranscriptElementCache(renderPlan.NewKeys);
|
|
|
|
_lastRenderedMessageCount = visibleMessages.Count;
|
|
_lastRenderedEventCount = visibleEvents.Count;
|
|
_lastRenderedShowHistory = renderPlan.ShowHistory;
|
|
renderStopwatch.Stop();
|
|
if (renderStopwatch.ElapsedMilliseconds >= 24 || _isStreaming)
|
|
{
|
|
AgentPerformanceLogService.LogMetric(
|
|
"transcript",
|
|
"render_messages",
|
|
conv.Id,
|
|
_activeTab ?? "",
|
|
renderStopwatch.ElapsedMilliseconds,
|
|
new
|
|
{
|
|
preserveViewport,
|
|
streaming = _isStreaming,
|
|
lightweight = IsLightweightLiveProgressMode(),
|
|
visibleMessages = visibleMessages.Count,
|
|
visibleEvents = visibleEvents.Count,
|
|
renderedItems = renderPlan.NewKeys.Count,
|
|
hiddenCount = renderPlan.HiddenCount,
|
|
transcriptElements = GetTranscriptElementCount(),
|
|
processFeedAppends = _processFeedAppendCount,
|
|
processFeedMerges = _processFeedMergeCount,
|
|
rowKindCounts = _transcriptRowKindCounts.ToDictionary(
|
|
pair => pair.Key.ToString(),
|
|
pair => pair.Value),
|
|
});
|
|
}
|
|
|
|
if (!preserveViewport)
|
|
{
|
|
_ = Dispatcher.InvokeAsync(ScrollTranscriptToEnd, DispatcherPriority.Background);
|
|
return;
|
|
}
|
|
|
|
_ = Dispatcher.InvokeAsync(() =>
|
|
{
|
|
if (_transcriptScrollViewer == null)
|
|
return;
|
|
|
|
var newScrollableHeight = GetTranscriptScrollableHeight();
|
|
var delta = newScrollableHeight - previousScrollableHeight;
|
|
var targetOffset = Math.Max(0, previousVerticalOffset + Math.Max(0, delta));
|
|
ScrollTranscriptToVerticalOffset(targetOffset);
|
|
}, DispatcherPriority.Background);
|
|
}
|
|
}
|