AX Agent 권한 모드 팝업 단순 리스트형으로 정리
Some checks failed
Release Gate / gate (push) Has been cancelled

- 권한 모드 팝업 폭과 padding, 그림자 강도를 줄여 레퍼런스처럼 가벼운 드롭다운 패널 스타일로 정리
- 상세 정보 섹션과 부가 요약 영역을 제거하고 실제 선택 가능한 권한 모드 행만 남기도록 단순화
- 계획 모드는 레거시로 제외하고 권한 요청, 편집 자동 승인, 권한 건너뛰기 중심으로 재배치
- 각 권한 모드 행은 왼쪽 accent bar 대신 라운드 row 리스트형으로 바꾸고 체크 위치와 텍스트 계층 정리
- README 및 DEVELOPMENT 문서에 2026-04-05 23:44 (KST) 기준 변경 이력 반영

검증
- dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\
- 경고 0 / 오류 0 확인
This commit is contained in:
2026-04-05 20:54:54 +09:00
parent 87c05720ce
commit 1c9b13c14f
4 changed files with 24 additions and 288 deletions

View File

@@ -1059,3 +1059,5 @@ MIT License
- AX Agent 빈 상태 상단 심볼과 작업 유형/대화 주제 프리셋 카드 아이콘 비율을 다시 맞췄다. 프리셋 카드 내부 아이콘과 `프리셋 추가` 심볼은 한 단계 줄이고, 상단 중앙 심볼과 제목도 같은 밀도로 재조정해 화면 균형을 정리했다.
- 업데이트: 2026-04-05 23:38 (KST)
- 하단 컨텍스트 토큰 hover 라벨이 마우스를 떼어도 남아 있던 문제를 보정했다. 닫힘 판정을 단순 `IsMouseOver` 대신 실제 마우스 좌표 기준으로 바꾸고, 팝업 내용도 `현재 사용량 + 자동 압축 기준`만 남기도록 단순화했다.
- 업데이트: 2026-04-05 23:44 (KST)
- 권한 모드 팝업에서 불필요한 `상세 정보` 섹션을 제거하고, 선택 가능한 모드 행만 남기는 단순 리스트형 UI로 정리했다. `계획 모드`는 제외하고 실제 사용하는 권한 요청/편집 자동 승인/권한 건너뛰기 중심으로 재정렬했다.

View File

@@ -4810,3 +4810,6 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎.
- 업데이트: 2026-04-05 23:38 (KST)
- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 의 하단 컨텍스트 토큰 hover 팝업을 더 단순하게 정리했다. 모델명/오늘 사용량 줄은 제거하고 `현재 사용량``자동 압축 시작 기준`만 남겼다.
- 같은 파일의 토큰 팝업 닫힘 판정도 `IsMouseOver`만 보던 방식에서 실제 마우스 좌표 기반으로 바꿔, 마우스를 떼었는데도 라벨이 남아 있는 경우를 줄였다.
- 업데이트: 2026-04-05 23:44 (KST)
- [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml) 의 권한 모드 팝업 폭, padding, 그림자를 줄여 더 단순한 드롭다운 패널처럼 보이게 정리했다.
- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 에서 권한 모드 팝업의 `상세 정보` 섹션을 제거하고, `계획 모드`를 제외한 핵심 권한 모드 행만 남기도록 재구성했다. 각 행도 왼쪽 accent bar 방식 대신 라운드 row 리스트형으로 바꿔 레퍼런스처럼 더 가벼운 선택 UI로 맞췄다.

View File

@@ -1352,9 +1352,9 @@
<Border Background="{DynamicResource LauncherBackground}"
BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1" CornerRadius="18"
Padding="10" MinWidth="340">
Padding="12" MinWidth="308" MaxWidth="324">
<Border.Effect>
<DropShadowEffect BlurRadius="22" ShadowDepth="0" Opacity="0.15"/>
<DropShadowEffect BlurRadius="18" ShadowDepth="0" Opacity="0.10"/>
</Border.Effect>
<StackPanel x:Name="PermissionItems" Margin="2"/>
</Border>

View File

@@ -1873,260 +1873,9 @@ public partial class ChatWindow : Window
ChatConversation? currentConversation;
lock (_convLock) currentConversation = _currentConversation;
var summary = _appState.GetPermissionSummary(currentConversation);
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White;
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
Border CreateCollapsibleSection(string sectionKey, string icon, string title, UIElement content, bool expanded, string accentHex = "#334155")
{
var body = new Border
{
Margin = new Thickness(0, 5, 0, 0),
Visibility = expanded ? Visibility.Visible : Visibility.Collapsed,
Child = content,
};
var caret = new TextBlock
{
Text = expanded ? "\uE70D" : "\uE76C",
FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 10.5,
Foreground = secondaryText,
VerticalAlignment = VerticalAlignment.Center,
};
var headerGrid = new Grid();
headerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
headerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
headerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
headerGrid.Children.Add(new TextBlock
{
Text = icon,
FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 10.5,
Foreground = BrushFromHex(accentHex),
Margin = new Thickness(0, 0, 6, 0),
VerticalAlignment = VerticalAlignment.Center,
});
var titleBlock = new TextBlock
{
Text = title,
FontSize = 9.5,
FontWeight = FontWeights.SemiBold,
Foreground = primaryText,
VerticalAlignment = VerticalAlignment.Center,
};
Grid.SetColumn(titleBlock, 1);
headerGrid.Children.Add(titleBlock);
Grid.SetColumn(caret, 2);
headerGrid.Children.Add(caret);
var headerBorder = new Border
{
Background = Brushes.Transparent,
CornerRadius = new CornerRadius(6),
Padding = new Thickness(8, 5, 8, 5),
Cursor = Cursors.Hand,
Focusable = true,
Child = headerGrid,
};
KeyboardNavigation.SetIsTabStop(headerBorder, true);
headerBorder.MouseEnter += (_, _) => headerBorder.Background = BrushFromHex("#F8FAFC");
headerBorder.MouseLeave += (_, _) => headerBorder.Background = Brushes.Transparent;
void ToggleSection()
{
var show = body.Visibility != Visibility.Visible;
body.Visibility = show ? Visibility.Visible : Visibility.Collapsed;
caret.Text = show ? "\uE70D" : "\uE76C";
SetPermissionPopupSectionExpanded(sectionKey, show);
}
headerBorder.MouseLeftButtonUp += (_, _) => ToggleSection();
headerBorder.KeyDown += (_, ke) =>
{
if (ke.Key is Key.Enter or Key.Space)
{
ke.Handled = true;
ToggleSection();
}
};
return new Border
{
Background = Brushes.Transparent,
BorderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray,
BorderThickness = new Thickness(0, 0, 0, 1),
CornerRadius = new CornerRadius(0),
Padding = new Thickness(0, 2, 0, 2),
Margin = new Thickness(0, 0, 0, 2),
Child = new StackPanel
{
Children =
{
headerBorder,
body,
}
}
};
}
var summaryCard = new StackPanel
{
Margin = new Thickness(0, 0, 0, 4),
Children =
{
CreateFlatPopupRow(
"\uE946",
$"현재 모드 · {PermissionModeCatalog.ToDisplayLabel(summary.EffectiveMode)}",
summary.Description,
string.Equals(summary.RiskLevel, "high", StringComparison.OrdinalIgnoreCase) ? "#C2410C" :
string.Equals(summary.RiskLevel, "locked", StringComparison.OrdinalIgnoreCase) ? "#475569" : "#4338CA",
false,
null),
CreateFlatPopupRow(
"\uE8D7",
$"기본값 · {PermissionModeCatalog.ToDisplayLabel(summary.DefaultMode)}",
$"예외 {summary.OverrideCount}개",
"#64748B",
false,
null)
}
};
StackPanel? overrideSection = null;
if (summary.TopOverrides.Count > 0)
{
var overrideWrap = new StackPanel
{
Margin = new Thickness(0, 0, 0, 5),
};
foreach (var overrideEntry in summary.TopOverrides)
{
overrideWrap.Children.Add(CreateFlatPopupRow(
"\uE72E",
overrideEntry.Key,
PermissionModeCatalog.ToDisplayLabel(overrideEntry.Value),
"#2563EB",
false,
null));
}
overrideSection = new StackPanel
{
Margin = new Thickness(0, 0, 0, 8),
Children =
{
new TextBlock
{
Text = "도구별 예외",
FontSize = 10,
FontWeight = FontWeights.SemiBold,
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
Margin = new Thickness(2, 0, 0, 3),
},
overrideWrap
}
};
}
var latestDenied = _appState.GetLatestDeniedPermission();
Border? deniedCard = null;
if (latestDenied != null)
{
var deniedStack = new StackPanel();
deniedStack.Children.Add(new StackPanel
{
Orientation = Orientation.Horizontal,
Margin = new Thickness(0, 0, 0, 2),
Children =
{
new TextBlock
{
Text = "\uEA39",
FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 10.5,
Foreground = BrushFromHex("#991B1B"),
Margin = new Thickness(0, 0, 6, 0),
VerticalAlignment = VerticalAlignment.Center,
},
new TextBlock
{
Text = "최근 권한 거부",
FontSize = 10.5,
FontWeight = FontWeights.SemiBold,
Foreground = BrushFromHex("#991B1B"),
}
}
});
deniedStack.Children.Add(new TextBlock
{
Text = _appState.FormatPermissionEventLine(latestDenied),
FontSize = 10,
Foreground = BrushFromHex("#991B1B"),
Margin = new Thickness(0, 0, 0, 0),
TextWrapping = TextWrapping.Wrap,
LineHeight = 14,
MaxWidth = 250,
});
if (!string.IsNullOrWhiteSpace(latestDenied.ToolName))
{
deniedStack.Children.Add(new TextBlock
{
Text = $"도구 {latestDenied.ToolName}에 바로 적용",
FontSize = 9.5,
Margin = new Thickness(0, 4, 0, 0),
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
});
var actionRow = new StackPanel
{
Margin = new Thickness(0, 6, 0, 0),
};
actionRow.Children.Add(CreateFlatPopupRow(
"\uE711",
"읽기 전용",
"이 도구의 쓰기 작업을 차단합니다",
"#991B1B",
true,
() => { SetToolPermissionOverride(latestDenied.ToolName!, PermissionModeCatalog.Deny); RefreshPermissionPopup(); }));
actionRow.Children.Add(CreateFlatPopupRow(
"\uE8D7",
"권한 요청",
"실행 전 항상 확인받도록 되돌립니다",
"#1D4ED8",
true,
() => { SetToolPermissionOverride(latestDenied.ToolName!, PermissionModeCatalog.Default); RefreshPermissionPopup(); }));
actionRow.Children.Add(CreateFlatPopupRow(
"\uE73E",
"편집 자동 승인",
"이 도구의 편집 작업을 자동 허용합니다",
"#166534",
true,
() => { SetToolPermissionOverride(latestDenied.ToolName!, PermissionModeCatalog.AcceptEdits); RefreshPermissionPopup(); }));
actionRow.Children.Add(CreateFlatPopupRow(
"\uE74D",
"예외 해제",
"도구별 예외를 제거하고 기본값을 사용합니다",
"#374151",
true,
() => { SetToolPermissionOverride(latestDenied.ToolName!, null); RefreshPermissionPopup(); }));
deniedStack.Children.Add(actionRow);
}
deniedCard = new Border
{
Background = Brushes.Transparent,
BorderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray,
BorderThickness = new Thickness(0, 0, 0, 1),
CornerRadius = new CornerRadius(0),
Padding = new Thickness(8, 8, 8, 8),
Margin = new Thickness(0, 0, 0, 4),
Child = deniedStack,
};
}
var coreLevels = PermissionModePresentationCatalog.Ordered.ToList();
var coreLevels = PermissionModePresentationCatalog.Ordered
.Where(item => !string.Equals(item.Mode, PermissionModeCatalog.Plan, StringComparison.OrdinalIgnoreCase))
.ToList();
var current = PermissionModeCatalog.NormalizeGlobalMode(_settings.Settings.Llm.FilePermission);
void AddPermissionRows(Panel container, IEnumerable<PermissionModePresentation> levels)
{
@@ -2137,11 +1886,11 @@ public partial class ChatWindow : Window
var rowBorder = new Border
{
Background = isActive ? BrushFromHex("#F8FAFC") : Brushes.Transparent,
BorderBrush = isActive ? BrushFromHex("#C7D2FE") : BrushFromHex("#E5E7EB"),
BorderThickness = new Thickness(isActive ? 2 : 0, 0, 0, 1),
CornerRadius = new CornerRadius(0),
Padding = new Thickness(8, 8, 8, 8),
Margin = new Thickness(0, 0, 0, 0),
BorderBrush = Brushes.Transparent,
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(12),
Padding = new Thickness(10, 10, 10, 10),
Margin = new Thickness(0, 0, 0, 4),
Cursor = Cursors.Hand,
Focusable = true,
};
@@ -2156,9 +1905,9 @@ public partial class ChatWindow : Window
{
Text = item.Icon,
FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 12,
FontSize = 15,
Foreground = BrushFromHex(item.ColorHex),
Margin = new Thickness(1, 0, 8, 0),
Margin = new Thickness(0, 0, 10, 0),
VerticalAlignment = VerticalAlignment.Center,
});
@@ -2166,19 +1915,19 @@ public partial class ChatWindow : Window
textStack.Children.Add(new TextBlock
{
Text = item.Title,
FontSize = 11.5,
FontSize = 13.5,
FontWeight = FontWeights.SemiBold,
Foreground = TryFindResource("PrimaryText") as Brush ?? Brushes.White,
});
textStack.Children.Add(new TextBlock
{
Text = item.Description,
FontSize = 10.5,
Margin = new Thickness(0, 1, 0, 0),
FontSize = 11.5,
Margin = new Thickness(0, 2, 0, 0),
Foreground = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray,
TextWrapping = TextWrapping.Wrap,
LineHeight = 15,
MaxWidth = 240,
LineHeight = 16,
MaxWidth = 220,
});
Grid.SetColumn(textStack, 1);
row.Children.Add(textStack);
@@ -2187,11 +1936,11 @@ public partial class ChatWindow : Window
{
Text = isActive ? "\uE73E" : "",
FontFamily = new FontFamily("Segoe MDL2 Assets"),
FontSize = 11,
FontSize = 12,
FontWeight = FontWeights.Bold,
Foreground = BrushFromHex("#2563EB"),
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(8, 0, 1, 0),
Margin = new Thickness(12, 0, 0, 0),
};
Grid.SetColumn(check, 2);
row.Children.Add(check);
@@ -2200,12 +1949,10 @@ public partial class ChatWindow : Window
rowBorder.MouseEnter += (_, _) =>
{
rowBorder.Background = BrushFromHex("#F8FAFC");
rowBorder.BorderBrush = isActive ? BrushFromHex("#C7D2FE") : BrushFromHex("#E2E8F0");
};
rowBorder.MouseLeave += (_, _) =>
{
rowBorder.Background = isActive ? BrushFromHex("#F8FAFC") : Brushes.Transparent;
rowBorder.BorderBrush = isActive ? BrushFromHex("#C7D2FE") : BrushFromHex("#E5E7EB");
};
var capturedLevel = level;
@@ -2234,24 +1981,8 @@ public partial class ChatWindow : Window
}
}
PermissionItems.Children.Add(CreatePopupSectionLabel("핵심 권한 모드"));
AddPermissionRows(PermissionItems, coreLevels);
// claw-code 기준 UX 정렬: 기본 화면은 핵심 모드 중심, 부가 정보는 단일 상세 섹션으로 제공.
var detailsPanel = new StackPanel();
detailsPanel.Children.Add(summaryCard);
if (overrideSection != null)
detailsPanel.Children.Add(overrideSection);
if (deniedCard != null)
detailsPanel.Children.Add(deniedCard);
PermissionItems.Children.Add(CreateCollapsibleSection(
"permission_details",
"\uE946",
"상세 정보",
detailsPanel,
expanded: GetPermissionPopupSectionExpanded("permission_details", false)));
PermissionPopup.IsOpen = true;
Dispatcher.BeginInvoke(() =>
{