권한 팝업 밀도 압축 2단계: 카드/행 타이포 컴팩트 정렬
Some checks failed
Release Gate / gate (push) Has been cancelled

- ChatWindow 권한 팝업 섹션 헤더/본문 간격과 폰트를 축소해 카드 밀도를 정돈

- 권한 요약/예외/최근 거부 카드 및 빠른 액션 버튼의 패딩·폰트·마진을 압축

- 핵심 권한 모드 행(아이콘/제목/설명/체크) 크기를 줄여 slash 팝업과 시각 리듬 통일

- README.md, docs/DEVELOPMENT.md에 2026-04-04 14:16(KST) 기준 이력 반영

- 검증: dotnet build 0경고/0오류, 관련 필터 테스트 82건 통과
This commit is contained in:
2026-04-04 14:17:22 +09:00
parent d3e1f947ba
commit db920d2256
3 changed files with 62 additions and 41 deletions

View File

@@ -222,7 +222,7 @@ public class MyHandler : IActionHandler
### v0.7.3 — AX Agent 권한 코어 재구성 + 입력 계층 정리 ### v0.7.3 — AX Agent 권한 코어 재구성 + 입력 계층 정리
업데이트: 2026-04-04 14:09 (KST) 업데이트: 2026-04-04 14:16 (KST)
| 분류 | 내용 | | 분류 | 내용 |
|------|------| |------|------|
@@ -283,6 +283,7 @@ public class MyHandler : IActionHandler
| 권한 팝업 핵심 4모드 정렬 | 권한 팝업을 `소극 활용/적극 활용/계획 중심/완전 자동` 중심으로 단순화하고 `활용하지 않음/질문 없이 진행``고급 모드` 접힘 섹션으로 분리 | | 권한 팝업 핵심 4모드 정렬 | 권한 팝업을 `소극 활용/적극 활용/계획 중심/완전 자동` 중심으로 단순화하고 `활용하지 않음/질문 없이 진행``고급 모드` 접힘 섹션으로 분리 |
| slash 스크롤 체감 개선 | 휠/방향키 이동 시 전체 재렌더링을 제거하고 선택 하이라이트만 갱신하도록 바꿔 `/` 팝업 스크롤 반응성과 안정성을 개선 | | slash 스크롤 체감 개선 | 휠/방향키 이동 시 전체 재렌더링을 제거하고 선택 하이라이트만 갱신하도록 바꿔 `/` 팝업 스크롤 반응성과 안정성을 개선 |
| slash 팝업 밀도 압축 2단계 | `/` 팝업 폭/높이/패딩과 항목 폰트/행 높이를 축소해 Codex형 컴팩트 밀도와 스캔 속도를 강화 | | slash 팝업 밀도 압축 2단계 | `/` 팝업 폭/높이/패딩과 항목 폰트/행 높이를 축소해 Codex형 컴팩트 밀도와 스캔 속도를 강화 |
| 권한 팝업 밀도 압축 2단계 | 권한 팝업의 섹션/요약/예외/거부 카드와 권한 행 타이포를 압축해 슬래시 팝업과 동일한 컴팩트 리듬으로 정렬 |
| 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

@@ -3452,3 +3452,23 @@ else:
### 3) 품질 게이트 ### 3) 품질 게이트
- `dotnet build src/AxCopilot/AxCopilot.csproj -c Debug -p:UseSharedCompilation=false -nodeReuse:false` 통과 (경고 0, 오류 0). - `dotnet build src/AxCopilot/AxCopilot.csproj -c Debug -p:UseSharedCompilation=false -nodeReuse:false` 통과 (경고 0, 오류 0).
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj --no-build --filter "FullyQualifiedName~ChatWindowSlashPolicyTests|FullyQualifiedName~SlashCommandCatalogTests"` 통과 (41 passed, 0 failed). - `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj --no-build --filter "FullyQualifiedName~ChatWindowSlashPolicyTests|FullyQualifiedName~SlashCommandCatalogTests"` 통과 (41 passed, 0 failed).
## 2026-04-04 추가 진행 기록 (연속 실행 33차: 권한 팝업 밀도 압축 2단계)
업데이트: 2026-04-04 14:16 (KST)
### 1) 권한 팝업 카드/섹션 밀도 조정
- `CreateCollapsibleSection`의 헤더/본문 간격과 폰트를 축소.
- 섹션 컨테이너 마진을 줄여 카드 간 여백을 압축.
### 2) 권한 요약/예외/거부 카드 타이포 압축
- 요약 카드와 예외 칩, 최근 거부 카드의 패딩·마진·폰트·라인높이를 축소.
- 최근 거부 빠른 액션 버튼 크기(패딩/폰트/최소너비)를 낮춰 한 행 밀도를 개선.
### 3) 핵심 권한 모드 리스트 밀도 조정
- 권한 행(아이콘/제목/설명/체크)의 패딩·폰트·행간을 축소.
- 목적: 슬래시 팝업과 유사한 컴팩트 스캔 리듬으로 통일.
### 4) 품질 게이트
- `dotnet build src/AxCopilot/AxCopilot.csproj -c Debug -p:UseSharedCompilation=false -nodeReuse:false` 통과 (경고 0, 오류 0).
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj --no-build --filter "FullyQualifiedName~OperationModePolicyTests|FullyQualifiedName~PermissionModeCatalogTests|FullyQualifiedName~PermissionModePresentationCatalogTests|FullyQualifiedName~ChatWindowSlashPolicyTests"` 통과 (82 passed, 0 failed).

View File

@@ -1610,7 +1610,7 @@ public partial class ChatWindow : Window
{ {
var body = new Border var body = new Border
{ {
Margin = new Thickness(0, 6, 0, 0), Margin = new Thickness(0, 5, 0, 0),
Visibility = expanded ? Visibility.Visible : Visibility.Collapsed, Visibility = expanded ? Visibility.Visible : Visibility.Collapsed,
Child = content, Child = content,
}; };
@@ -1618,7 +1618,7 @@ public partial class ChatWindow : Window
{ {
Text = expanded ? "\uE70D" : "\uE76C", Text = expanded ? "\uE70D" : "\uE76C",
FontFamily = new FontFamily("Segoe MDL2 Assets"), FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 11, FontSize = 10.5,
Foreground = secondaryText, Foreground = secondaryText,
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
}; };
@@ -1631,7 +1631,7 @@ public partial class ChatWindow : Window
{ {
Text = icon, Text = icon,
FontFamily = new FontFamily("Segoe MDL2 Assets"), FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 11, FontSize = 10.5,
Foreground = BrushFromHex(accentHex), Foreground = BrushFromHex(accentHex),
Margin = new Thickness(0, 0, 6, 0), Margin = new Thickness(0, 0, 6, 0),
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
@@ -1639,7 +1639,7 @@ public partial class ChatWindow : Window
var titleBlock = new TextBlock var titleBlock = new TextBlock
{ {
Text = title, Text = title,
FontSize = 10, FontSize = 9.5,
FontWeight = FontWeights.SemiBold, FontWeight = FontWeights.SemiBold,
Foreground = primaryText, Foreground = primaryText,
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
@@ -1653,7 +1653,7 @@ public partial class ChatWindow : Window
{ {
Background = Brushes.Transparent, Background = Brushes.Transparent,
CornerRadius = new CornerRadius(6), CornerRadius = new CornerRadius(6),
Padding = new Thickness(8, 5, 8, 5), Padding = new Thickness(7, 4, 7, 4),
Cursor = Cursors.Hand, Cursor = Cursors.Hand,
Focusable = true, Focusable = true,
Child = headerGrid, Child = headerGrid,
@@ -1685,7 +1685,7 @@ public partial class ChatWindow : Window
BorderThickness = new Thickness(1), BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(8), CornerRadius = new CornerRadius(8),
Padding = new Thickness(2), Padding = new Thickness(2),
Margin = new Thickness(0, 0, 0, 6), Margin = new Thickness(0, 0, 0, 5),
Child = new StackPanel Child = new StackPanel
{ {
Children = Children =
@@ -1711,8 +1711,8 @@ public partial class ChatWindow : Window
: BrushFromHex("#C7D2FE"), : BrushFromHex("#C7D2FE"),
BorderThickness = new Thickness(1), BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(8), CornerRadius = new CornerRadius(8),
Padding = new Thickness(11, 9, 11, 9), Padding = new Thickness(9, 7, 9, 7),
Margin = new Thickness(0, 0, 0, 6), Margin = new Thickness(0, 0, 0, 5),
Child = new StackPanel Child = new StackPanel
{ {
Children = Children =
@@ -1720,7 +1720,7 @@ public partial class ChatWindow : Window
new TextBlock new TextBlock
{ {
Text = $"현재 권한 모드 · {PermissionModeCatalog.ToDisplayLabel(summary.EffectiveMode)}", Text = $"현재 권한 모드 · {PermissionModeCatalog.ToDisplayLabel(summary.EffectiveMode)}",
FontSize = 12, FontSize = 11.5,
FontWeight = FontWeights.SemiBold, FontWeight = FontWeights.SemiBold,
Foreground = string.Equals(summary.RiskLevel, "high", StringComparison.OrdinalIgnoreCase) Foreground = string.Equals(summary.RiskLevel, "high", StringComparison.OrdinalIgnoreCase)
? BrushFromHex("#C2410C") ? BrushFromHex("#C2410C")
@@ -1731,11 +1731,11 @@ public partial class ChatWindow : Window
new TextBlock new TextBlock
{ {
Text = $"{summary.Description} 기본값 {PermissionModeCatalog.ToDisplayLabel(summary.DefaultMode)} · 예외 {summary.OverrideCount}개", Text = $"{summary.Description} 기본값 {PermissionModeCatalog.ToDisplayLabel(summary.DefaultMode)} · 예외 {summary.OverrideCount}개",
FontSize = 10, FontSize = 9.5,
Margin = new Thickness(0, 4, 0, 0), Margin = new Thickness(0, 3, 0, 0),
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray, Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
TextWrapping = TextWrapping.Wrap, TextWrapping = TextWrapping.Wrap,
LineHeight = 15, LineHeight = 14,
MaxWidth = 250, MaxWidth = 250,
} }
} }
@@ -1747,7 +1747,7 @@ public partial class ChatWindow : Window
{ {
var overrideWrap = new WrapPanel var overrideWrap = new WrapPanel
{ {
Margin = new Thickness(0, 0, 0, 6), Margin = new Thickness(0, 0, 0, 5),
}; };
foreach (var overrideEntry in summary.TopOverrides) foreach (var overrideEntry in summary.TopOverrides)
@@ -1758,8 +1758,8 @@ public partial class ChatWindow : Window
BorderBrush = BrushFromHex("#E2E8F0"), BorderBrush = BrushFromHex("#E2E8F0"),
BorderThickness = new Thickness(1), BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(12), CornerRadius = new CornerRadius(12),
Padding = new Thickness(8, 3, 8, 3), Padding = new Thickness(7, 2.5, 7, 2.5),
Margin = new Thickness(0, 0, 5, 5), Margin = new Thickness(0, 0, 4, 4),
Child = new StackPanel Child = new StackPanel
{ {
Orientation = Orientation.Horizontal, Orientation = Orientation.Horizontal,
@@ -1769,7 +1769,7 @@ public partial class ChatWindow : Window
{ {
Text = "\uE72E", Text = "\uE72E",
FontFamily = new FontFamily("Segoe MDL2 Assets"), FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 10, FontSize = 9.5,
Foreground = BrushFromHex("#2563EB"), Foreground = BrushFromHex("#2563EB"),
Margin = new Thickness(0, 0, 4, 0), Margin = new Thickness(0, 0, 4, 0),
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
@@ -1777,7 +1777,7 @@ public partial class ChatWindow : Window
new TextBlock new TextBlock
{ {
Text = $"{overrideEntry.Key} · {PermissionModeCatalog.ToDisplayLabel(overrideEntry.Value)}", Text = $"{overrideEntry.Key} · {PermissionModeCatalog.ToDisplayLabel(overrideEntry.Value)}",
FontSize = 10, FontSize = 9.5,
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.DimGray, Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.DimGray,
} }
} }
@@ -1793,7 +1793,7 @@ public partial class ChatWindow : Window
new TextBlock new TextBlock
{ {
Text = "도구별 예외", Text = "도구별 예외",
FontSize = 10.5, FontSize = 10,
FontWeight = FontWeights.SemiBold, FontWeight = FontWeights.SemiBold,
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray, Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
Margin = new Thickness(2, 0, 0, 3), Margin = new Thickness(2, 0, 0, 3),
@@ -1818,7 +1818,7 @@ public partial class ChatWindow : Window
{ {
Text = "\uEA39", Text = "\uEA39",
FontFamily = new FontFamily("Segoe MDL2 Assets"), FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 11, FontSize = 10.5,
Foreground = BrushFromHex("#991B1B"), Foreground = BrushFromHex("#991B1B"),
Margin = new Thickness(0, 0, 6, 0), Margin = new Thickness(0, 0, 6, 0),
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
@@ -1826,7 +1826,7 @@ public partial class ChatWindow : Window
new TextBlock new TextBlock
{ {
Text = "최근 권한 거부", Text = "최근 권한 거부",
FontSize = 11, FontSize = 10.5,
FontWeight = FontWeights.SemiBold, FontWeight = FontWeights.SemiBold,
Foreground = BrushFromHex("#991B1B"), Foreground = BrushFromHex("#991B1B"),
} }
@@ -1835,11 +1835,11 @@ public partial class ChatWindow : Window
deniedStack.Children.Add(new TextBlock deniedStack.Children.Add(new TextBlock
{ {
Text = _appState.FormatPermissionEventLine(latestDenied), Text = _appState.FormatPermissionEventLine(latestDenied),
FontSize = 10.5, FontSize = 10,
Foreground = BrushFromHex("#991B1B"), Foreground = BrushFromHex("#991B1B"),
Margin = new Thickness(0, 0, 0, 0), Margin = new Thickness(0, 0, 0, 0),
TextWrapping = TextWrapping.Wrap, TextWrapping = TextWrapping.Wrap,
LineHeight = 14.5, LineHeight = 14,
MaxWidth = 250, MaxWidth = 250,
}); });
@@ -1848,14 +1848,14 @@ public partial class ChatWindow : Window
deniedStack.Children.Add(new TextBlock deniedStack.Children.Add(new TextBlock
{ {
Text = $"도구 {latestDenied.ToolName}에 바로 적용", Text = $"도구 {latestDenied.ToolName}에 바로 적용",
FontSize = 10, FontSize = 9.5,
Margin = new Thickness(0, 5, 0, 0), Margin = new Thickness(0, 4, 0, 0),
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray, Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
}); });
var actionRow = new WrapPanel var actionRow = new WrapPanel
{ {
Margin = new Thickness(0, 8, 0, 0), Margin = new Thickness(0, 6, 0, 0),
}; };
Button CreateActionButton(string text, string backgroundHex, string foregroundHex, Action onClick) Button CreateActionButton(string text, string backgroundHex, string foregroundHex, Action onClick)
@@ -1867,11 +1867,11 @@ public partial class ChatWindow : Window
Foreground = BrushFromHex(foregroundHex), Foreground = BrushFromHex(foregroundHex),
BorderBrush = BrushFromHex(backgroundHex), BorderBrush = BrushFromHex(backgroundHex),
BorderThickness = new Thickness(1), BorderThickness = new Thickness(1),
Padding = new Thickness(9, 4, 9, 4), Padding = new Thickness(8, 3, 8, 3),
Margin = new Thickness(0, 0, 5, 5), Margin = new Thickness(0, 0, 4, 4),
FontSize = 10, FontSize = 9.5,
Cursor = Cursors.Hand, Cursor = Cursors.Hand,
MinWidth = 58, MinWidth = 54,
}; };
ApplyHoverScaleAnimation(button, 1.02); ApplyHoverScaleAnimation(button, 1.02);
button.Click += (_, _) => button.Click += (_, _) =>
@@ -1895,8 +1895,8 @@ public partial class ChatWindow : Window
BorderBrush = BrushFromHex("#FECACA"), BorderBrush = BrushFromHex("#FECACA"),
BorderThickness = new Thickness(1), BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(8), CornerRadius = new CornerRadius(8),
Padding = new Thickness(11, 8, 11, 8), Padding = new Thickness(9, 7, 9, 7),
Margin = new Thickness(0, 0, 0, 7), Margin = new Thickness(0, 0, 0, 5),
Child = deniedStack, Child = deniedStack,
}; };
} }
@@ -1926,8 +1926,8 @@ public partial class ChatWindow : Window
BorderBrush = isActive ? BrushFromHex("#C7D2FE") : Brushes.Transparent, BorderBrush = isActive ? BrushFromHex("#C7D2FE") : Brushes.Transparent,
BorderThickness = new Thickness(1), BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(8), CornerRadius = new CornerRadius(8),
Padding = new Thickness(10, 6, 10, 6), Padding = new Thickness(9, 5, 9, 5),
Margin = new Thickness(0, 2, 0, 2), Margin = new Thickness(0, 1.5, 0, 1.5),
Cursor = Cursors.Hand, Cursor = Cursors.Hand,
Focusable = true, Focusable = true,
}; };
@@ -1942,7 +1942,7 @@ public partial class ChatWindow : Window
{ {
Text = item.Icon, Text = item.Icon,
FontFamily = new FontFamily("Segoe MDL2 Assets"), FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 13.5, FontSize = 13,
Foreground = BrushFromHex(item.ColorHex), Foreground = BrushFromHex(item.ColorHex),
Margin = new Thickness(2, 0, 9, 0), Margin = new Thickness(2, 0, 9, 0),
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
@@ -1952,18 +1952,18 @@ public partial class ChatWindow : Window
textStack.Children.Add(new TextBlock textStack.Children.Add(new TextBlock
{ {
Text = item.Title, Text = item.Title,
FontSize = 11.5, FontSize = 11,
FontWeight = FontWeights.SemiBold, FontWeight = FontWeights.SemiBold,
Foreground = TryFindResource("PrimaryText") as Brush ?? Brushes.White, Foreground = TryFindResource("PrimaryText") as Brush ?? Brushes.White,
}); });
textStack.Children.Add(new TextBlock textStack.Children.Add(new TextBlock
{ {
Text = item.Description, Text = item.Description,
FontSize = 10, FontSize = 9.5,
Margin = new Thickness(0, 2, 0, 0), Margin = new Thickness(0, 1.5, 0, 0),
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray, Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
TextWrapping = TextWrapping.Wrap, TextWrapping = TextWrapping.Wrap,
LineHeight = 14, LineHeight = 13.5,
MaxWidth = 230, MaxWidth = 230,
}); });
Grid.SetColumn(textStack, 1); Grid.SetColumn(textStack, 1);
@@ -1973,7 +1973,7 @@ public partial class ChatWindow : Window
{ {
Text = isActive ? "\uE73E" : "", Text = isActive ? "\uE73E" : "",
FontFamily = new FontFamily("Segoe MDL2 Assets"), FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 12, FontSize = 11.5,
FontWeight = FontWeights.Bold, FontWeight = FontWeights.Bold,
Foreground = BrushFromHex("#2563EB"), Foreground = BrushFromHex("#2563EB"),
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
@@ -2022,7 +2022,7 @@ public partial class ChatWindow : Window
PermissionItems.Children.Add(new TextBlock PermissionItems.Children.Add(new TextBlock
{ {
Text = "핵심 권한 모드", Text = "핵심 권한 모드",
FontSize = 10.5, FontSize = 10,
FontWeight = FontWeights.SemiBold, FontWeight = FontWeights.SemiBold,
Foreground = secondaryText, Foreground = secondaryText,
Margin = new Thickness(2, 0, 0, 4), Margin = new Thickness(2, 0, 0, 4),