권한 표시 카탈로그를 분리해 UI 표면 기준을 단일화
Some checks failed
Release Gate / gate (push) Has been cancelled

- PermissionModePresentationCatalog 신규 추가: 모드 라벨/설명/아이콘/색을 단일 소스로 관리

- ChatWindow 권한 팝업에서 하드코딩 튜플을 제거하고 카탈로그 기반 렌더링으로 전환

- 유지보수 관점에서 권한 표면 기준 변경 포인트를 1곳으로 축소

- README.md 업데이트 시각(2026-04-04 13:25 KST) 및 변경 이력 항목 갱신

- docs/DEVELOPMENT.md 연속 실행 27차 기록 추가

- 검증: dotnet build 경고 0/오류 0, slash+operation mode 필터 테스트 43건 통과
This commit is contained in:
2026-04-04 13:26:34 +09:00
parent 14534af2e9
commit 15b675d9c4
4 changed files with 90 additions and 16 deletions

View File

@@ -222,7 +222,7 @@ public class MyHandler : IActionHandler
### v0.7.3 — AX Agent 권한 코어 재구성 + 입력 계층 정리
업데이트: 2026-04-04 13:24 (KST)
업데이트: 2026-04-04 13:25 (KST)
| 분류 | 내용 |
|------|------|
@@ -277,6 +277,7 @@ public class MyHandler : IActionHandler
| 권한 요청창 한국어/인코딩 복구 | `PermissionRequestWindow`의 깨진 문자열을 복구하고 권한 선택/위험도/미리보기 문구를 한국어 기준으로 정리 |
| slash 명령 카탈로그 분리 | `ChatWindow` 내부 대형 slash 사전을 `SlashCommandCatalog`로 분리해 입력 계층 결합도를 낮추고 유지보수 범위를 축소 |
| slash 조회 API 전환 | 내장 slash 매칭/조회 경로를 `SlashCommandCatalog.MatchBuiltinCommands`/`TryGetEntry`로 통일 |
| 권한 표시 카탈로그 분리 | 권한 모드 라벨/설명/아이콘/색을 `PermissionModePresentationCatalog`로 분리해 팝업 표면 기준을 단일화 |
| Slash palette 상태 분리 시작 | `ChatWindow`에 몰려 있던 slash 상태를 `SlashPaletteState`로 분리해 이후 Codex/Claude형 composer 개편 기반 마련 |
| 런처 이미지 미리보기 추가 | `#` 클립보드 이미지 항목에서 `Shift+Enter`로 전용 미리보기 창을 열고, 줌·원본 해상도 확인·PNG/JPEG/BMP 저장·클립보드 복사를 지원 |
| 검증 | `dotnet build` 경고 0 / 오류 0, `dotnet test` 436 passed / 0 failed |

View File

@@ -3322,3 +3322,24 @@ else:
### 3) 참고
- 테스트 재빌드 시점에 AxCopilot.dll 파일 잠금(CS2012)이 간헐 발생하여,
빌드 산출물 기준 --no-build 실행으로 테스트를 검증함.
## 2026-04-04 추가 진행 기록 (연속 실행 27차: 권한 표시 카탈로그 단일화)
업데이트: 2026-04-04 13:25 (KST)
### 1) 권한 표시 카탈로그 분리
- 신규 파일 PermissionModePresentationCatalog를 추가.
- 권한 모드별 라벨/설명/아이콘/색상 정보를 단일 소스로 관리:
- 활용하지 않음
- 소극 활용
- 적극 활용
- 계획 중심
- 완전 자동
- 질문 없이 진행
### 2) ChatWindow 연동
- 권한 팝업의 핵심 모드 리스트 생성 시 하드코딩 튜플을 제거하고 카탈로그를 사용하도록 전환.
- 결과: 권한 표면 기준(순서/문구/아이콘/색상)의 유지보수 포인트를 1곳으로 축소.
### 3) 품질 게이트
- dotnet build src/AxCopilot/AxCopilot.csproj 통과 (경고 0, 오류 0).
- dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj --no-build --filter "FullyQualifiedName~ChatWindowSlashPolicyTests|FullyQualifiedName~OperationModeReadinessTests" 통과 (43 passed, 0 failed).

View File

@@ -0,0 +1,59 @@
namespace AxCopilot.Services.Agent;
internal sealed record PermissionModePresentation(
string Mode,
string Icon,
string Title,
string Description,
string ColorHex);
internal static class PermissionModePresentationCatalog
{
public static readonly IReadOnlyList<PermissionModePresentation> Ordered = new[]
{
new PermissionModePresentation(
PermissionModeCatalog.Deny,
"\uE711",
"활용하지 않음",
"파일 읽기만 허용하고 생성/수정/삭제를 차단합니다",
"#107C10"),
new PermissionModePresentation(
PermissionModeCatalog.Default,
"\uE8D7",
"소극 활용",
"변경 전 확인하며 필요할 때만 파일 접근을 진행합니다",
"#2563EB"),
new PermissionModePresentation(
PermissionModeCatalog.AcceptEdits,
"\uE73E",
"적극 활용",
"파일 편집 도구를 자동 승인하고 명령 실행은 계속 확인합니다",
"#107C10"),
new PermissionModePresentation(
PermissionModeCatalog.Plan,
"\uE7C3",
"계획 중심",
"쓰기 전 계획/승인 흐름을 우선합니다",
"#4338CA"),
new PermissionModePresentation(
PermissionModeCatalog.BypassPermissions,
"\uE814",
"완전 자동",
"권한 확인을 대부분 생략합니다. 민감 작업에 주의하세요",
"#B45309"),
new PermissionModePresentation(
PermissionModeCatalog.DontAsk,
"\uE8A5",
"질문 없이 진행",
"권한 질문 없이 진행합니다. 자동 실행 범위를 점검하세요",
"#B91C1C"),
};
public static PermissionModePresentation Resolve(string? mode)
{
var normalized = PermissionModeCatalog.NormalizeGlobalMode(mode);
return Ordered.FirstOrDefault(item =>
string.Equals(item.Mode, normalized, StringComparison.OrdinalIgnoreCase))
?? Ordered[1];
}
}

View File

@@ -1901,20 +1901,13 @@ public partial class ChatWindow : Window
};
}
var coreLevels = new (string Level, string Sym, string Title, string Desc, string Color)[]
{
(PermissionModeCatalog.Deny, "\uE711", "활용하지 않음", "파일 읽기만 허용하고 생성/수정/삭제를 차단합니다", "#107C10"),
(PermissionModeCatalog.Default, "\uE8D7", "소극 활용", "변경 전 확인하며 필요할 때만 파일 접근을 진행합니다", "#2563EB"),
(PermissionModeCatalog.AcceptEdits, "\uE73E", "적극 활용", "파일 편집 도구를 자동 승인하고 명령 실행은 계속 확인합니다", "#107C10"),
(PermissionModeCatalog.Plan, "\uE7C3", "계획 중심", "쓰기 전 계획/승인 흐름을 우선합니다", "#4338CA"),
(PermissionModeCatalog.BypassPermissions, "\uE814", "완전 자동", "권한 확인을 대부분 생략합니다. 민감 작업에 주의하세요", "#B45309"),
(PermissionModeCatalog.DontAsk, "\uE8A5", "질문 없이 진행", "권한 질문 없이 진행합니다. 자동 실행 범위를 점검하세요", "#B91C1C"),
};
var coreLevels = PermissionModePresentationCatalog.Ordered;
var current = PermissionModeCatalog.NormalizeGlobalMode(_settings.Settings.Llm.FilePermission);
void AddPermissionRows(Panel container, IEnumerable<(string Level, string Sym, string Title, string Desc, string Color)> levels)
void AddPermissionRows(Panel container, IEnumerable<PermissionModePresentation> levels)
{
foreach (var (level, sym, title, desc, color) in levels)
foreach (var item in levels)
{
var level = item.Mode;
var isActive = level.Equals(current, StringComparison.OrdinalIgnoreCase);
var rowBorder = new Border
{
@@ -1936,10 +1929,10 @@ public partial class ChatWindow : Window
row.Children.Add(new TextBlock
{
Text = sym,
Text = item.Icon,
FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 13.5,
Foreground = BrushFromHex(color),
Foreground = BrushFromHex(item.ColorHex),
Margin = new Thickness(2, 0, 9, 0),
VerticalAlignment = VerticalAlignment.Center,
});
@@ -1947,14 +1940,14 @@ public partial class ChatWindow : Window
var textStack = new StackPanel();
textStack.Children.Add(new TextBlock
{
Text = title,
Text = item.Title,
FontSize = 11.5,
FontWeight = FontWeights.SemiBold,
Foreground = TryFindResource("PrimaryText") as Brush ?? Brushes.White,
});
textStack.Children.Add(new TextBlock
{
Text = desc,
Text = item.Description,
FontSize = 10,
Margin = new Thickness(0, 2, 0, 0),
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,