- 권한 모드 팝업 폭과 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:
@@ -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(() =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user