diff --git a/README.md b/README.md index 6dfd0f2..8f8ee44 100644 --- a/README.md +++ b/README.md @@ -379,6 +379,11 @@ ow + toggle 시각 언어로 통일했습니다. - DraftQueue 패널 상단에 실행 중 / 다음 / 보류 / 완료 / 실패 요약 pill을 추가하고, composer 상단의 모델/컨텍스트/프리셋 줄도 더 낮고 평평한 밀도로 정리했습니다. - 브랜치/워크트리 패널에는 공통 요약 strip을 추가해 현재 상태를 같은 시각 언어로 보여주도록 맞췄고, 저장소 루트 `.gitignore`에는 빌드 산출물·IDE 파일·OS 잡파일·비밀정보 패턴을 추가했습니다. - 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\` 경고 0 / 오류 0 +- 업데이트: 2026-04-04 23:23 (KST) +- AX Agent는 이제 설정 서비스 변경 이벤트를 직접 구독해 메인 설정, AX Agent 설정, 저장 경로와 관계없이 테마/권한/데이터 활용/모델 라벨/composer/대기열 UI를 즉시 다시 읽어오도록 fan-out 경로를 통합했습니다. +- AX Agent 설정 저장 경로에서 표현 수준을 `rich`로 고정 덮어쓰던 처리도 제거해, 사용자가 선택한 `풍부하게 / 적절하게 / 간단하게` 값이 다른 설정 저장 흐름에서도 유지되도록 보정했습니다. +- DraftQueue 패널은 실행 중 / 다음 작업 / 보류 / 완료 / 실패를 개별 섹션으로 나눠 현재 실행 상태와 최근 결과를 더 빠르게 파악할 수 있도록 재구성했습니다. +- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\` 경고 0 / 오류 0 --- diff --git a/docs/AGENT_ROADMAP.md b/docs/AGENT_ROADMAP.md index 55d27c1..6555038 100644 --- a/docs/AGENT_ROADMAP.md +++ b/docs/AGENT_ROADMAP.md @@ -97,3 +97,7 @@ - 트레이 좌클릭 기본 진입점을 AX Agent로 전환하고, 우클릭 메뉴 상단에 앱 버전 헤더를 추가해 AX Agent 중심 진입 흐름을 강화함. - 메인 설정 저장 완료 후 열린 AX Agent 창이 즉시 테마/모델/권한/하단 상태줄을 다시 읽어오도록 fan-out 경로를 추가해 설정 반영 지연을 줄임. - DraftQueue kind 분류를 message/command/steering/direct/followup 기준으로 재정리해 큐 타입과 실제 입력 성격이 더 잘 맞도록 보강함. +- 업데이트: 2026-04-04 23:23 (KST) +- AX Agent가 `SettingsService.SettingsChanged`를 직접 구독하도록 바꿔 메인 설정/AX Agent 설정 어느 경로에서 저장하더라도 테마, 모델, 권한, 데이터 활용, composer, 대기열 UI가 즉시 동일 상태를 반영하도록 fan-out을 통합함. +- AX Agent 설정 저장 경로에서 표현 수준을 무조건 `rich`로 덮어쓰던 로직을 제거해 `풍부하게 / 적절하게 / 간단하게`가 다른 설정 저장 경로에서도 유지되도록 보정함. +- DraftQueue 패널은 `실행 중 / 다음 작업 / 보류 / 완료 / 실패` 개별 섹션 구조로 다시 나눠 현재 실행 흐름과 재시도 대기, 결과 이력을 더 빠르게 파악할 수 있도록 정리함. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index e5f4305..ef63ede 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -4068,3 +4068,11 @@ ow + toggle 시각 언어로 다시 정렬했다. - 메인 설정 저장 완료 시 열려 있는 AX Agent 창이 즉시 테마, 모델, 권한, 데이터 활용, overlay/inline 빠른 설정, 하단 composer 라벨을 다시 읽어오도록 `RefreshFromSavedSettings()` fan-out 경로를 추가했다. - DraftQueue kind 판정은 일반 입력(message), 슬래시 명령(command), 조정 입력(steering), 직접 실행 요청(direct), 후속 작업(followup)이 실제 입력 형태와 더 일치하도록 보강했고, 전송 버튼은 일반 메시지 전송 경로를 사용하도록 정리했다. - 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\ (경고 0 / 오류 0) + +### 2026-04-04 추가 진행 기록 (연속 실행 76차: 설정 이벤트 fan-out 통합과 큐 상태 섹션 재구성) +- 업데이트: 2026-04-04 23:23 (KST) +- AX Agent 창이 `SettingsService.SettingsChanged`를 직접 구독하도록 연결해 메인 설정, AX Agent 설정, 저장 방식이 달라도 테마/권한/데이터 활용/모델 라벨/하단 composer/대기열 UI가 즉시 같은 상태를 반영하도록 fan-out 경로를 통합했다. +- `RefreshFromSavedSettings()`는 기존 시각 요소 갱신 외에도 대기열 패널과 대화 목록까지 다시 읽어오도록 확장해 저장 후 AX Agent 화면과 실제 실행 상태가 더 빠르게 맞춰지도록 보강했다. +- AX Agent 설정 저장 경로와 구형 Agent 설정창에서 표현 수준을 무조건 `rich`로 덮어쓰던 코드를 제거해, 사용자가 선택한 `풍부하게 / 적절하게 / 간단하게` 값이 다른 설정 흐름에서도 유지되도록 수정했다. +- DraftQueue 패널은 기존 `실행 대기 / 최근 결과` 묶음에서 `실행 중 / 다음 작업 / 보류 / 완료 / 실패` 개별 섹션 구조로 재편해 queue state가 많은 경우에도 현재 진행, 다음 실행, 재시도 대기, 완료/실패 이력을 더 빠르게 구분할 수 있게 했다. +- 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\ (경고 0 / 오류 0) diff --git a/src/AxCopilot/Views/AgentSettingsWindow.xaml.cs b/src/AxCopilot/Views/AgentSettingsWindow.xaml.cs index e7f4f52..7151479 100644 --- a/src/AxCopilot/Views/AgentSettingsWindow.xaml.cs +++ b/src/AxCopilot/Views/AgentSettingsWindow.xaml.cs @@ -383,7 +383,6 @@ public partial class AgentSettingsWindow : Window _llm.PlanMode = _planMode; _llm.AgentDecisionLevel = _reasoningMode; _llm.FolderDataUsage = _folderDataUsage; - _llm.AgentUiExpressionLevel = "rich"; _llm.EnableProactiveContextCompact = ChkEnableProactiveCompact.IsChecked == true; _llm.ContextCompactTriggerPercent = ParseInt(TxtContextCompactTriggerPercent.Text, 80, 10, 95); diff --git a/src/AxCopilot/Views/ChatWindow.xaml.cs b/src/AxCopilot/Views/ChatWindow.xaml.cs index d629665..1d7e687 100644 --- a/src/AxCopilot/Views/ChatWindow.xaml.cs +++ b/src/AxCopilot/Views/ChatWindow.xaml.cs @@ -94,6 +94,7 @@ public partial class ChatWindow : Window private string _gitBranchSearchText = ""; private StackPanel? _selectedMessageActionBar; private Border? _selectedMessageBorder; + private bool _isRefreshingFromSettings; private void ApplyQuickActionVisual(Button button, bool active, string activeBg, string activeFg) { if (button?.Content is not string text) @@ -134,6 +135,7 @@ public partial class ChatWindow : Window { InitializeComponent(); _settings = settings; + _settings.SettingsChanged += Settings_SettingsChanged; _storage = new ChatStorageService(); _llm = new LlmService(settings); _router = new ModelRouterService(settings); @@ -297,6 +299,7 @@ public partial class ChatWindow : Window }; Closed += (_, _) => { + _settings.SettingsChanged -= Settings_SettingsChanged; SubAgentTool.StatusChanged -= OnSubAgentStatusChanged; _streamCts?.Cancel(); _cursorTimer.Stop(); @@ -306,6 +309,28 @@ public partial class ChatWindow : Window }; } + private void Settings_SettingsChanged(object? sender, EventArgs e) + { + if (_forceClose || !IsLoaded || _isRefreshingFromSettings) + return; + + Dispatcher.BeginInvoke(new Action(() => + { + if (_forceClose || !IsLoaded) + return; + + _isRefreshingFromSettings = true; + try + { + RefreshFromSavedSettings(); + } + finally + { + _isRefreshingFromSettings = false; + } + }), DispatcherPriority.Input); + } + private bool IsPermissionAutoApprovedForSession(string toolName, string target) { if (string.IsNullOrWhiteSpace(toolName) || string.IsNullOrWhiteSpace(target)) @@ -13956,6 +13981,8 @@ public partial class ChatWindow : Window RefreshContextUsageVisual(); UpdateTabUI(); BuildBottomBar(); + RefreshDraftQueueUi(); + RefreshConversationList(); }, DispatcherPriority.Input); } @@ -13978,7 +14005,6 @@ public partial class ChatWindow : Window llm.Code.EnableCodeVerification = ChkOverlayEnableCodeVerification?.IsChecked == true; llm.EnableParallelTools = ChkOverlayEnableParallelTools?.IsChecked == true; llm.FolderDataUsage = _folderDataUsage; - llm.AgentUiExpressionLevel = "rich"; CommitOverlayEndpointInput(normalizeOnInvalid: true); CommitOverlayApiKeyInput(); @@ -17228,45 +17254,35 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi } DraftQueuePanel.Visibility = Visibility.Visible; - const int maxVisible = 8; var summary = _appState.GetDraftQueueSummary(_activeTab); - var activeItems = visibleItems - .Where(item => !string.Equals(item.State, "completed", StringComparison.OrdinalIgnoreCase) - && !string.Equals(item.State, "failed", StringComparison.OrdinalIgnoreCase)) - .Take(maxVisible) - .ToList(); - var historyItems = visibleItems - .Where(item => string.Equals(item.State, "completed", StringComparison.OrdinalIgnoreCase) - || string.Equals(item.State, "failed", StringComparison.OrdinalIgnoreCase)) - .Take(Math.Max(0, maxVisible - activeItems.Count)) - .ToList(); - DraftQueuePanel.Children.Add(CreateDraftQueueSummaryStrip(summary)); + const int maxPerSection = 3; + var runningItems = visibleItems + .Where(item => string.Equals(item.State, "running", StringComparison.OrdinalIgnoreCase)) + .Take(maxPerSection) + .ToList(); + var queuedItems = visibleItems + .Where(item => string.Equals(item.State, "queued", StringComparison.OrdinalIgnoreCase) && !IsDraftBlocked(item)) + .Take(maxPerSection) + .ToList(); + var blockedItems = visibleItems + .Where(IsDraftBlocked) + .Take(maxPerSection) + .ToList(); + var completedItems = visibleItems + .Where(item => string.Equals(item.State, "completed", StringComparison.OrdinalIgnoreCase)) + .Take(maxPerSection) + .ToList(); + var failedItems = visibleItems + .Where(item => string.Equals(item.State, "failed", StringComparison.OrdinalIgnoreCase)) + .Take(maxPerSection) + .ToList(); - if (activeItems.Count > 0) - { - DraftQueuePanel.Children.Add(CreateDraftQueueSectionLabel($"실행 대기 · {activeItems.Count}")); - foreach (var item in activeItems) - DraftQueuePanel.Children.Add(CreateDraftQueueCard(item)); - } - - if (historyItems.Count > 0) - { - DraftQueuePanel.Children.Add(CreateDraftQueueSectionLabel($"최근 결과 · {historyItems.Count}")); - foreach (var item in historyItems) - DraftQueuePanel.Children.Add(CreateDraftQueueCard(item)); - } - - if (visibleItems.Count > activeItems.Count + historyItems.Count) - { - DraftQueuePanel.Children.Add(new TextBlock - { - Text = $"추가 항목 {visibleItems.Count - (activeItems.Count + historyItems.Count)}개", - Margin = new Thickness(8, 4, 0, 0), - FontSize = 11, - Foreground = TryFindResource("SecondaryText") as Brush ?? BrushFromHex("#7A7F87"), - }); - } + AddDraftQueueSection("실행 중", runningItems, summary.RunningCount); + AddDraftQueueSection("다음 작업", queuedItems, summary.QueuedCount); + AddDraftQueueSection("보류", blockedItems, summary.BlockedCount); + AddDraftQueueSection("완료", completedItems, summary.CompletedCount); + AddDraftQueueSection("실패", failedItems, summary.FailedCount); if (summary.CompletedCount > 0 || summary.FailedCount > 0) { @@ -17286,6 +17302,27 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi } } + private void AddDraftQueueSection(string label, IReadOnlyList items, int totalCount) + { + if (DraftQueuePanel == null || totalCount <= 0) + return; + + DraftQueuePanel.Children.Add(CreateDraftQueueSectionLabel($"{label} · {totalCount}")); + foreach (var item in items) + DraftQueuePanel.Children.Add(CreateDraftQueueCard(item)); + + if (totalCount > items.Count) + { + DraftQueuePanel.Children.Add(new TextBlock + { + Text = $"추가 항목 {totalCount - items.Count}개", + Margin = new Thickness(8, -2, 0, 8), + FontSize = 10.5, + Foreground = TryFindResource("SecondaryText") as Brush ?? BrushFromHex("#7A7F87"), + }); + } + } + private UIElement CreateDraftQueueSummaryStrip(AppStateService.DraftQueueSummaryState summary) { var wrap = new WrapPanel