- 계획 승인 기본 흐름을 transcript inline 우선 구조로 더 정리하고, 계획 버튼은 저장된 계획을 여는 상세 보기 성격으로 분리했습니다. - OperationalStatusPresentationState를 확장해 runtime badge, compact strip, quick strip의 문구·강조색·노출 여부를 한 번에 계산하도록 통합했습니다. - ChatWindow 상태선/quick strip/status token 로직을 StatusPresentation partial로 분리해 메인 창 코드의 직접 분기와 렌더 책임을 줄였습니다. - 문서 이력(README, DEVELOPMENT)을 2026-04-06 01:37 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:
172
src/AxCopilot/Views/ChatWindow.StatusPresentation.cs
Normal file
172
src/AxCopilot/Views/ChatWindow.StatusPresentation.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using AxCopilot.Services;
|
||||
|
||||
namespace AxCopilot.Views;
|
||||
|
||||
public partial class ChatWindow
|
||||
{
|
||||
private AppStateService.OperationalStatusPresentationState BuildOperationalStatusPresentation()
|
||||
{
|
||||
var hasLiveRuntimeActivity = !string.Equals(_activeTab, "Chat", StringComparison.OrdinalIgnoreCase)
|
||||
&& (_runningConversationCount > 0 || _appState.ActiveTasks.Count > 0);
|
||||
return _appState.GetOperationalStatusPresentation(
|
||||
_activeTab,
|
||||
hasLiveRuntimeActivity,
|
||||
_runningConversationCount,
|
||||
_spotlightConversationCount,
|
||||
_runningOnlyFilter,
|
||||
_sortConversationsByRecent);
|
||||
}
|
||||
|
||||
private void UpdateTaskSummaryIndicators()
|
||||
{
|
||||
var status = BuildOperationalStatusPresentation();
|
||||
|
||||
if (RuntimeActivityBadge != null)
|
||||
RuntimeActivityBadge.Visibility = status.ShowRuntimeBadge
|
||||
? Visibility.Visible
|
||||
: Visibility.Collapsed;
|
||||
|
||||
if (RuntimeActivityLabel != null)
|
||||
RuntimeActivityLabel.Text = status.RuntimeLabel;
|
||||
|
||||
if (LastCompletedLabel != null)
|
||||
{
|
||||
LastCompletedLabel.Text = status.LastCompletedText;
|
||||
LastCompletedLabel.Visibility = status.ShowLastCompleted ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
if (ConversationStatusStrip != null && ConversationStatusStripLabel != null)
|
||||
{
|
||||
ConversationStatusStrip.Visibility = status.ShowCompactStrip
|
||||
? Visibility.Visible
|
||||
: Visibility.Collapsed;
|
||||
ConversationStatusStripLabel.Text = status.ShowCompactStrip ? status.StripText : "";
|
||||
|
||||
if (status.ShowCompactStrip)
|
||||
{
|
||||
ConversationStatusStrip.Background = BrushFromHex(status.StripBackgroundHex);
|
||||
ConversationStatusStrip.BorderBrush = BrushFromHex(status.StripBorderHex);
|
||||
ConversationStatusStripLabel.Foreground = BrushFromHex(status.StripForegroundHex);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateConversationQuickStripUi(status);
|
||||
}
|
||||
|
||||
private void UpdateConversationQuickStripUi()
|
||||
{
|
||||
UpdateConversationQuickStripUi(BuildOperationalStatusPresentation());
|
||||
}
|
||||
|
||||
private void UpdateConversationQuickStripUi(AppStateService.OperationalStatusPresentationState status)
|
||||
{
|
||||
if (ConversationQuickStrip == null || QuickRunningLabel == null || QuickHotLabel == null
|
||||
|| BtnQuickRunningFilter == null || BtnQuickHotSort == null)
|
||||
return;
|
||||
|
||||
ConversationQuickStrip.Visibility = status.ShowQuickStrip
|
||||
? Visibility.Visible
|
||||
: Visibility.Collapsed;
|
||||
|
||||
QuickRunningLabel.Text = status.QuickRunningText;
|
||||
QuickHotLabel.Text = status.QuickHotText;
|
||||
|
||||
BtnQuickRunningFilter.Background = BrushFromHex(status.QuickRunningBackgroundHex);
|
||||
BtnQuickRunningFilter.BorderBrush = BrushFromHex(status.QuickRunningBorderHex);
|
||||
BtnQuickRunningFilter.BorderThickness = new Thickness(1);
|
||||
QuickRunningLabel.Foreground = BrushFromHex(status.QuickRunningForegroundHex);
|
||||
|
||||
BtnQuickHotSort.Background = BrushFromHex(status.QuickHotBackgroundHex);
|
||||
BtnQuickHotSort.BorderBrush = BrushFromHex(status.QuickHotBorderHex);
|
||||
BtnQuickHotSort.BorderThickness = new Thickness(1);
|
||||
QuickHotLabel.Foreground = BrushFromHex(status.QuickHotForegroundHex);
|
||||
}
|
||||
|
||||
private void SetStatus(string text, bool spinning)
|
||||
{
|
||||
if (StatusLabel != null)
|
||||
StatusLabel.Text = text;
|
||||
|
||||
if (spinning)
|
||||
StartStatusAnimation();
|
||||
else
|
||||
StopStatusAnimation();
|
||||
}
|
||||
|
||||
private static bool IsDecisionPending(string? summary)
|
||||
{
|
||||
var text = summary?.Trim() ?? "";
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return true;
|
||||
|
||||
return text.Contains("확인 대기", StringComparison.OrdinalIgnoreCase)
|
||||
|| text.Contains("승인 대기", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static bool IsDecisionApproved(string? summary)
|
||||
{
|
||||
var text = summary?.Trim() ?? "";
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return false;
|
||||
|
||||
return text.Contains("계획 승인", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static bool IsDecisionRejected(string? summary)
|
||||
{
|
||||
var text = summary?.Trim() ?? "";
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return false;
|
||||
|
||||
return text.Contains("계획 반려", StringComparison.OrdinalIgnoreCase)
|
||||
|| text.Contains("수정 요청", StringComparison.OrdinalIgnoreCase)
|
||||
|| text.Contains("취소", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string GetDecisionStatusText(string? summary)
|
||||
{
|
||||
if (IsDecisionPending(summary))
|
||||
return "계획 승인 대기 중";
|
||||
if (IsDecisionApproved(summary))
|
||||
return "계획 승인됨 · 실행 시작";
|
||||
if (IsDecisionRejected(summary))
|
||||
return "계획 반려됨 · 계획 재작성";
|
||||
return string.IsNullOrWhiteSpace(summary) ? "사용자 의사결정 대기 중" : TruncateForStatus(summary);
|
||||
}
|
||||
|
||||
private void SetStatusIdle()
|
||||
{
|
||||
StopStatusAnimation();
|
||||
if (StatusLabel != null)
|
||||
StatusLabel.Text = "대기 중";
|
||||
if (StatusElapsed != null)
|
||||
{
|
||||
StatusElapsed.Text = "";
|
||||
StatusElapsed.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
if (StatusTokens != null)
|
||||
{
|
||||
StatusTokens.Text = "";
|
||||
StatusTokens.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
RefreshContextUsageVisual();
|
||||
ScheduleGitBranchRefresh(250);
|
||||
}
|
||||
|
||||
private void UpdateStatusTokens(int inputTokens, int outputTokens)
|
||||
{
|
||||
if (StatusTokens == null)
|
||||
return;
|
||||
|
||||
var llm = _settings.Settings.Llm;
|
||||
var (inCost, outCost) = Services.TokenEstimator.EstimateCost(
|
||||
inputTokens, outputTokens, llm.Service, llm.Model);
|
||||
var totalCost = inCost + outCost;
|
||||
var costText = totalCost > 0 ? $" · {Services.TokenEstimator.FormatCost(totalCost)}" : "";
|
||||
StatusTokens.Text = $"↑{Services.TokenEstimator.Format(inputTokens)} ↓{Services.TokenEstimator.Format(outputTokens)}{costText}";
|
||||
StatusTokens.Visibility = Visibility.Visible;
|
||||
RefreshContextUsageVisual();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user