연속 개선: 권한 상태 간소화·설정창 외부 진입 안정화·컴포저 반응형 보강
Some checks failed
Release Gate / gate (push) Has been cancelled

- 권한 상태 텍스트(/permissions,/allowed-tools)를 운영 모드 포함 축약형으로 재정리
- 하단 권한 버튼 툴팁에 운영 모드/기본값/예외 개수 정보를 일관 반영
- 탭 전환 시 좌측 메뉴 Visibility 재할당을 최소화해 UI 흔들림 완화
- 상단 모델 라벨에 MaxWidth+말줄임 적용으로 긴 모델명 레이아웃 깨짐 방지
- AX Agent 설정창 오픈 시 리소스 병합 예외를 방어하고 외부 진입 경로를 Dispatcher 기반으로 안정화
- UI 체크리스트/개발문서/README에 2026-04-04 12:41 기준 점검 이력 업데이트
- 검증: build 경고0/오류0, 운영모드 필터 18건 통과, 전체 테스트 436건 통과
This commit is contained in:
2026-04-04 12:42:49 +09:00
parent e8e701e4a1
commit 6e65cf6026
5 changed files with 82 additions and 17 deletions

View File

@@ -222,7 +222,7 @@ public class MyHandler : IActionHandler
### v0.7.3 — AX Agent 권한 코어 재구성 + 입력 계층 정리 ### v0.7.3 — AX Agent 권한 코어 재구성 + 입력 계층 정리
업데이트: 2026-04-04 12:33 (KST) 업데이트: 2026-04-04 12:41 (KST)
| 분류 | 내용 | | 분류 | 내용 |
|------|------| |------|------|
@@ -270,6 +270,9 @@ public class MyHandler : IActionHandler
| 권한 팝업 밀도 재정돈 | 권한 섹션 헤더/카드/행의 패딩·폰트·줄간격을 조정해 과밀 영역을 완화하고 Codex/Claude형 스캔 속도에 맞춤 | | 권한 팝업 밀도 재정돈 | 권한 섹션 헤더/카드/행의 패딩·폰트·줄간격을 조정해 과밀 영역을 완화하고 Codex/Claude형 스캔 속도에 맞춤 |
| 좌측/컴포저 라벨 정리 | 좌측 기본 카테고리 라벨을 `주제 선택/작업 선택`으로 통일하고, 입력 상단 바 패딩·간격을 미세 조정해 단일 라인 정돈 강화 | | 좌측/컴포저 라벨 정리 | 좌측 기본 카테고리 라벨을 `주제 선택/작업 선택`으로 통일하고, 입력 상단 바 패딩·간격을 미세 조정해 단일 라인 정돈 강화 |
| 체크리스트 실행 결과 기록 | `docs/UI_UX_CHECKLIST.md`에 2026-04-04 12:22 기준 점검 결과(운영모드 필터 18건 + 전체 436건 통과)를 기록 | | 체크리스트 실행 결과 기록 | `docs/UI_UX_CHECKLIST.md`에 2026-04-04 12:22 기준 점검 결과(운영모드 필터 18건 + 전체 436건 통과)를 기록 |
| 권한 상태 표시 간소화 | 권한 상태 텍스트(`/permissions`, `/allowed-tools`)를 운영 모드 포함 축약형으로 정리하고 권한 버튼 툴팁에 동일 정보를 반영 |
| 설정창 외부 진입 안정화 | AX Agent 설정창 오픈 시 리소스 병합 실패를 방어하고, 외부 진입 경로를 Dispatcher 기반으로 안정화 |
| 모델 라벨 반응형 보강 | 컴포저 상단 모델 라벨에 말줄임(`MaxWidth` + `CharacterEllipsis`)을 적용해 좁은 폭에서 레이아웃 깨짐을 방지 |
| Slash palette 상태 분리 시작 | `ChatWindow`에 몰려 있던 slash 상태를 `SlashPaletteState`로 분리해 이후 Codex/Claude형 composer 개편 기반 마련 | | Slash palette 상태 분리 시작 | `ChatWindow`에 몰려 있던 slash 상태를 `SlashPaletteState`로 분리해 이후 Codex/Claude형 composer 개편 기반 마련 |
| 런처 이미지 미리보기 추가 | `#` 클립보드 이미지 항목에서 `Shift+Enter`로 전용 미리보기 창을 열고, 줌·원본 해상도 확인·PNG/JPEG/BMP 저장·클립보드 복사를 지원 | | 런처 이미지 미리보기 추가 | `#` 클립보드 이미지 항목에서 `Shift+Enter`로 전용 미리보기 창을 열고, 줌·원본 해상도 확인·PNG/JPEG/BMP 저장·클립보드 복사를 지원 |
| 검증 | `dotnet build` 경고 0 / 오류 0, `dotnet test` 436 passed / 0 failed | | 검증 | `dotnet build` 경고 0 / 오류 0, `dotnet test` 436 passed / 0 failed |

View File

@@ -3239,6 +3239,37 @@ else:
- `dotnet build src/AxCopilot/AxCopilot.csproj` 통과 (경고 0, 오류 0). - `dotnet build src/AxCopilot/AxCopilot.csproj` 통과 (경고 0, 오류 0).
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj` 통과 (436 passed, 0 failed). - `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj` 통과 (436 passed, 0 failed).
## 2026-04-04 추가 진행 기록 (연속 실행 24차: 권한/설정 안정화 보강 + 시나리오 점검)
업데이트: 2026-04-04 12:41 (KST)
### 1) 권한 상태 표시 간소화
- `/permissions`, `/allowed-tools` 상태 텍스트를 축약형으로 재정리:
- 현재 권한 모드
- 운영 모드
- 기본값/예외 개수
- 예외 미리보기(최대 3개)
- 하단 권한 버튼 툴팁에도 동일한 운영 모드/기본값/예외 정보를 반영.
### 2) 좌측 패널 전환 안정화
- `UpdateSidebarModeMenu()`에서 탭별 메뉴 표시를 동일 값 재할당하지 않도록 보강해 전환 시 불필요한 렌더 흔들림을 완화.
### 3) 컴포저 반응형 보강
- 상단 모델 라벨에 `MaxWidth=280`, `TextTrimming=CharacterEllipsis` 적용.
- 긴 모델명 선택 시 컴포저 레이아웃 깨짐을 방지.
### 4) AX Agent 설정창 오픈 안정성 보강
- 설정창 리소스 병합 실패 시에도 창 오픈이 유지되도록 예외 방어 추가.
- `OpenAgentSettingsFromExternal()`를 Dispatcher 경유로 변경해 트레이/외부 진입 시 타이밍 이슈를 완화.
### 5) 시나리오/회귀 점검
- 운영 모드 필터 테스트(18건) 재통과:
- `OperationModePolicyTests`
- `OperationModeReadinessTests`
- `LlmOperationModeTests`
- 전체 테스트 436건 재통과.
- `dotnet build` 경고 0 / 오류 0 유지.

View File

@@ -54,3 +54,12 @@
- [x] AX Agent 설정 권한 순환 순서와 `/sandbox-toggle` 순환 순서 일치 확인 - [x] AX Agent 설정 권한 순환 순서와 `/sandbox-toggle` 순환 순서 일치 확인
- [x] 운영 모드 회귀 필터 테스트 18건 통과 (`OperationModePolicy/Readiness/LlmOperationMode`) - [x] 운영 모드 회귀 필터 테스트 18건 통과 (`OperationModePolicy/Readiness/LlmOperationMode`)
- [x] 전체 테스트 436건 통과 - [x] 전체 테스트 436건 통과
점검 시각: 2026-04-04 12:41 (KST)
- [x] 권한 버튼 툴팁에 운영 모드 + 기본 권한 + 예외 개수 표시 확인
- [x] 권한 상태 텍스트(`/permissions`, `/allowed-tools`)를 축약형으로 정리 확인
- [x] 모델 라벨 말줄임 처리(`MaxWidth`, `CharacterEllipsis`) 적용 확인
- [x] 설정창 외부 진입(`OpenAgentSettingsFromExternal`) Dispatcher 경유 오픈 경로 확인
- [x] 운영 모드 필터 테스트 18건 재통과
- [x] 전체 테스트 436건 재통과

View File

@@ -1115,6 +1115,8 @@
VerticalAlignment="Center" Margin="0,0,5,0"/> VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBlock x:Name="ModelLabel" FontSize="12.5" <TextBlock x:Name="ModelLabel" FontSize="12.5"
Foreground="{DynamicResource SecondaryText}" Foreground="{DynamicResource SecondaryText}"
MaxWidth="280"
TextTrimming="CharacterEllipsis"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
<TextBlock Text="&#xE70D;" FontFamily="Segoe MDL2 Assets" FontSize="9" <TextBlock Text="&#xE70D;" FontFamily="Segoe MDL2 Assets" FontSize="9"
Foreground="{DynamicResource SecondaryText}" Foreground="{DynamicResource SecondaryText}"

View File

@@ -717,9 +717,12 @@ public partial class ChatWindow : Window
if (SidebarChatMenu == null || SidebarCoworkMenu == null || SidebarCodeMenu == null) if (SidebarChatMenu == null || SidebarCoworkMenu == null || SidebarCodeMenu == null)
return; return;
SidebarChatMenu.Visibility = _activeTab == "Chat" ? Visibility.Visible : Visibility.Collapsed; var chatVisible = _activeTab == "Chat" ? Visibility.Visible : Visibility.Collapsed;
SidebarCoworkMenu.Visibility = _activeTab == "Cowork" ? Visibility.Visible : Visibility.Collapsed; var coworkVisible = _activeTab == "Cowork" ? Visibility.Visible : Visibility.Collapsed;
SidebarCodeMenu.Visibility = _activeTab == "Code" ? Visibility.Visible : Visibility.Collapsed; var codeVisible = _activeTab == "Code" ? Visibility.Visible : Visibility.Collapsed;
if (SidebarChatMenu.Visibility != chatVisible) SidebarChatMenu.Visibility = chatVisible;
if (SidebarCoworkMenu.Visibility != coworkVisible) SidebarCoworkMenu.Visibility = coworkVisible;
if (SidebarCodeMenu.Visibility != codeVisible) SidebarCodeMenu.Visibility = codeVisible;
if (SidebarModeBadgeTitle != null && SidebarModeBadgeIcon != null) if (SidebarModeBadgeTitle != null && SidebarModeBadgeIcon != null)
{ {
@@ -2139,7 +2142,10 @@ public partial class ChatWindow : Window
_ => "\uE8D7", _ => "\uE8D7",
}; };
if (BtnPermission != null) if (BtnPermission != null)
BtnPermission.ToolTip = $"{summary.Description}\n기본값 {PermissionModeCatalog.ToDisplayLabel(summary.DefaultMode)} · override {summary.OverrideCount}개"; {
var operationMode = OperationModePolicy.Normalize(_settings.Settings.OperationMode);
BtnPermission.ToolTip = $"{summary.Description}\n운영 모드: {operationMode}\n기본값 {PermissionModeCatalog.ToDisplayLabel(summary.DefaultMode)} · 예외 {summary.OverrideCount}개";
}
if (!string.Equals(_lastPermissionBannerMode, perm, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(_lastPermissionBannerMode, perm, StringComparison.OrdinalIgnoreCase))
{ {
@@ -2275,10 +2281,11 @@ public partial class ChatWindow : Window
lock (_convLock) currentConversation = _currentConversation; lock (_convLock) currentConversation = _currentConversation;
var summary = _appState.GetPermissionSummary(currentConversation); var summary = _appState.GetPermissionSummary(currentConversation);
var mode = PermissionModeCatalog.NormalizeGlobalMode(summary.EffectiveMode); var mode = PermissionModeCatalog.NormalizeGlobalMode(summary.EffectiveMode);
var operationMode = OperationModePolicy.Normalize(_settings.Settings.OperationMode);
var overrides = summary.TopOverrides.Count > 0 var overrides = summary.TopOverrides.Count > 0
? string.Join(", ", summary.TopOverrides.Select(x => $"{x.Key}:{x.Value}")) ? string.Join(", ", summary.TopOverrides.Take(3).Select(x => $"{x.Key}:{PermissionModeCatalog.ToDisplayLabel(x.Value)}"))
: "없음"; : "없음";
return $"현재 권한 모드: {PermissionModeCatalog.ToDisplayLabel(mode)} ({mode})\n설명: {summary.Description}\n기본값: {PermissionModeCatalog.ToDisplayLabel(summary.DefaultMode)} · override: {summary.OverrideCount}개\n상위 override: {overrides}"; return $"현재 권한 모드: {PermissionModeCatalog.ToDisplayLabel(mode)}\n운영 모드: {operationMode}\n기본값: {PermissionModeCatalog.ToDisplayLabel(summary.DefaultMode)} · 예외 {summary.OverrideCount}개\n예외 미리보기: {overrides}";
} }
private void OpenPermissionPanelFromSlash(string command, string usageText) private void OpenPermissionPanelFromSlash(string command, string usageText)
@@ -12972,13 +12979,19 @@ public partial class ChatWindow : Window
_agentSettingsWindow = null; _agentSettingsWindow = null;
} }
var win = new AgentSettingsWindow(_settings) var win = new AgentSettingsWindow(_settings);
{ if (IsLoaded && IsVisible)
Owner = IsLoaded && IsVisible ? this : null, win.Owner = this;
};
_agentSettingsWindow = win; _agentSettingsWindow = win;
win.Closed += (_, _) => _agentSettingsWindow = null; win.Closed += (_, _) => _agentSettingsWindow = null;
win.Resources.MergedDictionaries.Add(Resources); try
{
win.Resources.MergedDictionaries.Add(Resources);
}
catch
{
// 리소스 병합 실패 시에도 설정창 자체는 열리도록 유지
}
bool changed; bool changed;
try try
@@ -12988,8 +13001,12 @@ public partial class ChatWindow : Window
catch catch
{ {
// 모달 창 오픈에 실패하면 일반 창으로라도 설정 접근을 보장 // 모달 창 오픈에 실패하면 일반 창으로라도 설정 접근을 보장
win.Show(); try
win.Activate(); {
win.Show();
win.Activate();
}
catch { }
return; return;
} }
if (!changed) if (!changed)
@@ -13006,9 +13023,12 @@ public partial class ChatWindow : Window
public void OpenAgentSettingsFromExternal() public void OpenAgentSettingsFromExternal()
{ {
Show(); Dispatcher.BeginInvoke(() =>
Activate(); {
OpenAgentSettingsWindow(); Show();
Activate();
OpenAgentSettingsWindow();
}, DispatcherPriority.Input);
} }
private void BtnInlineSettingsClose_Click(object sender, RoutedEventArgs e) private void BtnInlineSettingsClose_Click(object sender, RoutedEventArgs e)