AX Agent 대화 목록 선택 카드와 idle 심볼을 정리한다

좌측 대화 목록 선택 상태가 제목 굵기만 바뀌어 체감이 약하던 문제를 수정했다. ConversationItemTemplate가 ItemSelectedBackground 기반의 둥근 카드 배경을 사용하도록 바꾸고 패딩과 아이콘 영역도 함께 다듬어 선택 항목이 테마에 맞게 한눈에 구분되도록 맞췄다.

실행 중이거나 미확인 완료 상태가 아닌 대화에는 점선 링 형태의 idle 심볼이 보이도록 ShowIdleIndicator 계산 속성과 목록 트리거를 추가했다. 관련 회귀를 ConversationItemViewModelTests로 고정하고 README와 DEVELOPMENT 문서에도 2026-04-15 21:00 KST 기준 변경 이력을 남겼다.

검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify_conversation_list_selected_card\ -p:IntermediateOutputPath=obj\verify_conversation_list_selected_card\ / 경고 0 오류 0
검증: dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ConversationItemViewModelTests" -p:OutputPath=bin\verify_conversation_list_selected_card_tests\ -p:IntermediateOutputPath=obj\verify_conversation_list_selected_card_tests\ / 통과 3
This commit is contained in:
2026-04-15 21:04:50 +09:00
parent 96e4f80edf
commit 41228ae82e
5 changed files with 84 additions and 10 deletions

View File

@@ -1,5 +1,13 @@
# AX Commander # AX Commander
- 업데이트: 2026-04-15 21:00 (KST)
- AX Agent 좌측 대화 목록의 선택 표시를 카드형으로 다시 다듬었습니다. `src/AxCopilot/Views/ChatWindow.xaml``ConversationItemTemplate`가 선택된 항목에 `ItemSelectedBackground` 기반의 둥근 직사각형 배경을 채우도록 바뀌어, 제목만 굵어지는 대신 항목 전체가 현재 테마에 맞춰 강조됩니다.
- 같은 템플릿에 idle 심볼도 추가했습니다. 실행 중(`IsRunning`)이나 미확인 완료 점(`HasUnreadCompletion`)이 없는 대화는 좌측에 점선 링 형태의 기본 심볼을 보여주고, 실행 중이면 기존 러닝 링으로, 아직 열어보지 않은 완료 대화면 완료 점으로 자연스럽게 바뀝니다.
- `src/AxCopilot/ViewModels/ChatWindowViewModel.cs``ShowIdleIndicator` 계산 속성을 추가했고, `src/AxCopilot.Tests/ViewModels/ConversationItemViewModelTests.cs`로 idle/running/completion 상태에 따른 표시 규칙 회귀 테스트를 보강했습니다.
- 검증:
- `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_conversation_list_selected_card\\ -p:IntermediateOutputPath=obj\\verify_conversation_list_selected_card\\` 경고 0 / 오류 0
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ConversationItemViewModelTests" -p:OutputPath=bin\\verify_conversation_list_selected_card_tests\\ -p:IntermediateOutputPath=obj\\verify_conversation_list_selected_card_tests\\` 통과 3
- 업데이트: 2026-04-15 20:55 (KST) - 업데이트: 2026-04-15 20:55 (KST)
- Code 탭 리뷰 로그 기준으로 비 Git 작업 폴더 회복 흐름을 보강했습니다. `src/AxCopilot/Services/Agent/AgentLoopService.cs`, `src/AxCopilot/Services/Agent/TaskTypePolicy.cs``git_tool(diff)`만 고집하지 않고 `code_review(file_review)` 또는 직접 파일 검토 경로를 함께 안내하도록 바뀌어, `현재 작업 폴더는 Git 저장소가 아닙니다``Git을 찾을 수 없습니다` 이후 같은 Git 계열 도구를 반복 호출하던 흐름을 줄였습니다. - Code 탭 리뷰 로그 기준으로 비 Git 작업 폴더 회복 흐름을 보강했습니다. `src/AxCopilot/Services/Agent/AgentLoopService.cs`, `src/AxCopilot/Services/Agent/TaskTypePolicy.cs``git_tool(diff)`만 고집하지 않고 `code_review(file_review)` 또는 직접 파일 검토 경로를 함께 안내하도록 바뀌어, `현재 작업 폴더는 Git 저장소가 아닙니다``Git을 찾을 수 없습니다` 이후 같은 Git 계열 도구를 반복 호출하던 흐름을 줄였습니다.
- `src/AxCopilot/Services/Agent/CodeReviewTool.cs``diff_review` 전에 실제 Git 저장소 루트를 확인하고, 저장소가 아니거나 Git 실행이 불가능하면 바로 `file_review` 대안을 반환하도록 보강했습니다. Git 탐지도 `where.exe` 기반으로 맞춰 `git_tool``code_review` 사이 탐지 불일치를 줄였습니다. - `src/AxCopilot/Services/Agent/CodeReviewTool.cs``diff_review` 전에 실제 Git 저장소 루트를 확인하고, 저장소가 아니거나 Git 실행이 불가능하면 바로 `file_review` 대안을 반환하도록 보강했습니다. Git 탐지도 `where.exe` 기반으로 맞춰 `git_tool``code_review` 사이 탐지 불일치를 줄였습니다.

View File

@@ -1563,3 +1563,10 @@ UI ?遺우쁽????域뱀뮆???귐뗫솯?醫딆춦 ???袁る퓮 ?臾믩씜 ??疫
- 테스트: `src/AxCopilot.Tests/Services/AgentLoopCodeQualityTests.cs`, `src/AxCopilot.Tests/Services/OperationModeReadinessTests.cs` - 테스트: `src/AxCopilot.Tests/Services/AgentLoopCodeQualityTests.cs`, `src/AxCopilot.Tests/Services/OperationModeReadinessTests.cs`
- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_review_policy_fix\\ -p:IntermediateOutputPath=obj\\verify_review_policy_fix\\` 경고 0 / 오류 0 - 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_review_policy_fix\\ -p:IntermediateOutputPath=obj\\verify_review_policy_fix\\` 경고 0 / 오류 0
- 검증: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AgentLoopCodeQualityTests|OperationModeReadinessTests" -p:OutputPath=bin\\verify_review_policy_fix_tests\\ -p:IntermediateOutputPath=obj\\verify_review_policy_fix_tests\\` 통과 133 - 검증: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AgentLoopCodeQualityTests|OperationModeReadinessTests" -p:OutputPath=bin\\verify_review_policy_fix_tests\\ -p:IntermediateOutputPath=obj\\verify_review_policy_fix_tests\\` 통과 133
업데이트: 2026-04-15 21:00 (KST)
- AX Agent 좌측 대화 목록의 선택 스타일을 카드형 강조로 조정했습니다. `src/AxCopilot/Views/ChatWindow.xaml``ConversationItemTemplate`가 선택된 항목에 `ItemSelectedBackground`를 채우는 둥근 직사각형 배경을 적용해, 제목만 굵어지는 대신 항목 전체가 현재 테마에서 더 분명하게 선택 상태를 보여줍니다.
- 같은 목록 템플릿에 idle 심볼도 추가했습니다. `src/AxCopilot/ViewModels/ChatWindowViewModel.cs``ConversationItemViewModel.ShowIdleIndicator`가 실행 중/미확인 완료가 아닌 항목을 판정하고, 목록에서는 점선 링 심볼을 기본으로 보여주다가 실행 중이면 러닝 링, 아직 열어보지 않은 완료 대화면 완료 점으로 치환합니다.
- `src/AxCopilot.Tests/ViewModels/ConversationItemViewModelTests.cs`에 idle/running/completion 상태별 아이콘 계산 회귀 테스트를 추가했습니다.
- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_conversation_list_selected_card\\ -p:IntermediateOutputPath=obj\\verify_conversation_list_selected_card\\` 경고 0 / 오류 0
- 검증: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ConversationItemViewModelTests" -p:OutputPath=bin\\verify_conversation_list_selected_card_tests\\ -p:IntermediateOutputPath=obj\\verify_conversation_list_selected_card_tests\\` 통과 3

View File

@@ -0,0 +1,43 @@
using AxCopilot.ViewModels;
using FluentAssertions;
using Xunit;
namespace AxCopilot.Tests.ViewModels;
public class ConversationItemViewModelTests
{
[Fact]
public void ShowIdleIndicator_IsTrue_WhenConversationIsIdle()
{
var viewModel = new ConversationItemViewModel
{
HasUnreadCompletion = false,
};
viewModel.ShowIdleIndicator.Should().BeTrue();
}
[Fact]
public void ShowIdleIndicator_IsFalse_WhenConversationIsRunning()
{
var viewModel = new ConversationItemViewModel
{
HasUnreadCompletion = false,
};
viewModel.IsRunning = true;
viewModel.ShowIdleIndicator.Should().BeFalse();
}
[Fact]
public void ShowIdleIndicator_IsFalse_WhenUnreadCompletionMarkerIsVisible()
{
var viewModel = new ConversationItemViewModel
{
HasUnreadCompletion = true,
};
viewModel.ShowIdleIndicator.Should().BeFalse();
}
}

View File

@@ -247,7 +247,11 @@ public class ConversationItemViewModel : ViewModelBase
public bool IsRunning public bool IsRunning
{ {
get => _isRunning; get => _isRunning;
set => SetProperty(ref _isRunning, value); set
{
if (SetProperty(ref _isRunning, value))
OnPropertyChanged(nameof(ShowIdleIndicator));
}
} }
public string Category { get; init; } = "general"; public string Category { get; init; } = "general";
@@ -274,4 +278,5 @@ public class ConversationItemViewModel : ViewModelBase
: AgentRunCount > 0 ? $"실행 {AgentRunCount}" : ""; : AgentRunCount > 0 ? $"실행 {AgentRunCount}" : "";
public bool HasRunStatus => AgentRunCount > 0 || FailedAgentRunCount > 0; public bool HasRunStatus => AgentRunCount > 0 || FailedAgentRunCount > 0;
public bool HasFailed => FailedAgentRunCount > 0; public bool HasFailed => FailedAgentRunCount > 0;
public bool ShowIdleIndicator => !IsRunning && !HasUnreadCompletion;
} }

View File

@@ -163,8 +163,8 @@
<!-- ── 대화 목록 항목 DataTemplate ── --> <!-- ── 대화 목록 항목 DataTemplate ── -->
<DataTemplate x:Key="ConversationItemTemplate" DataType="{x:Type vm:ConversationItemViewModel}"> <DataTemplate x:Key="ConversationItemTemplate" DataType="{x:Type vm:ConversationItemViewModel}">
<Border x:Name="ConvItemBorder" <Border x:Name="ConvItemBorder"
CornerRadius="10" CornerRadius="11"
Padding="10,7" Padding="12,8"
Cursor="Hand" Cursor="Hand"
Background="Transparent" Background="Transparent"
BorderBrush="Transparent" BorderBrush="Transparent"
@@ -177,8 +177,8 @@
<Setter Property="Margin" Value="10,1,0,1"/> <Setter Property="Margin" Value="10,1,0,1"/>
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True"> <DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" Value="{DynamicResource HintBackground}"/> <Setter Property="Background" Value="{DynamicResource ItemSelectedBackground}"/>
<Setter Property="BorderBrush" Value="{DynamicResource AccentColor}"/> <Setter Property="BorderBrush" Value="{DynamicResource ItemSelectedBackground}"/>
<Setter Property="BorderThickness" Value="1"/> <Setter Property="BorderThickness" Value="1"/>
</DataTrigger> </DataTrigger>
<MultiDataTrigger> <MultiDataTrigger>
@@ -193,20 +193,28 @@
</Border.Style> </Border.Style>
<Grid VerticalAlignment="Center"> <Grid VerticalAlignment="Center">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/> <ColumnDefinition Width="22"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid Grid.Column="0" <Grid Grid.Column="0"
Width="12" Width="14"
Height="12" Height="14"
Margin="0,0,8,0" Margin="0,0,10,0"
VerticalAlignment="Center"> VerticalAlignment="Center">
<Ellipse x:Name="ConvIdleRing"
Visibility="Collapsed"
Stroke="{DynamicResource SecondaryText}"
StrokeThickness="1.3"
StrokeDashArray="1.35,1.55"
StrokeDashCap="Round"
Fill="Transparent"
Opacity="0.55"/>
<Ellipse x:Name="ConvRunningRing" <Ellipse x:Name="ConvRunningRing"
Visibility="Collapsed" Visibility="Collapsed"
Stroke="{DynamicResource SecondaryText}" Stroke="{DynamicResource SecondaryText}"
StrokeThickness="1.4" StrokeThickness="1.6"
Fill="Transparent"/> Fill="Transparent"/>
<Grid x:Name="ConvUnreadBadge" <Grid x:Name="ConvUnreadBadge"
Visibility="Collapsed"> Visibility="Collapsed">
@@ -242,6 +250,9 @@
<DataTrigger Binding="{Binding IsSelected}" Value="True"> <DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="ConvTitleBlock" Property="FontWeight" Value="SemiBold"/> <Setter TargetName="ConvTitleBlock" Property="FontWeight" Value="SemiBold"/>
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding ShowIdleIndicator}" Value="True">
<Setter TargetName="ConvIdleRing" Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsRunning}" Value="True"> <DataTrigger Binding="{Binding IsRunning}" Value="True">
<Setter TargetName="ConvRunningRing" Property="Visibility" Value="Visible"/> <Setter TargetName="ConvRunningRing" Property="Visibility" Value="Visible"/>
<Setter TargetName="ConvRunningRing" Property="Stroke" Value="{DynamicResource AccentColor}"/> <Setter TargetName="ConvRunningRing" Property="Stroke" Value="{DynamicResource AccentColor}"/>