using System; using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; using AxCopilot.Services; using AxCopilot.Services.Agent; namespace AxCopilot.Views; public partial class ChatWindow { private void ShowWorktreeMenu(UIElement placementTarget) { var (popup, panel) = CreateThemedPopupMenu(placementTarget, PlacementMode.Top, 320); var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White; var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; var accentBrush = TryFindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue; var currentFolder = GetCurrentWorkFolder(); var root = string.IsNullOrWhiteSpace(currentFolder) ? "" : WorktreeStateStore.ResolveRoot(currentFolder); var active = string.IsNullOrWhiteSpace(root) ? currentFolder : WorktreeStateStore.Load(root).Active; var variants = GetAvailableWorkspaceVariants(root, active); panel.Children.Add(CreatePopupSummaryStrip(new[] { ("모드", string.Equals(active, root, StringComparison.OrdinalIgnoreCase) ? "로컬" : "워크트리", "#F8FAFC", "#E2E8F0", "#475569"), ("변형", variants.Count.ToString(), "#EFF6FF", "#BFDBFE", "#1D4ED8"), })); panel.Children.Add(CreatePopupSectionLabel("현재 작업 위치", new Thickness(8, 6, 8, 4))); panel.Children.Add(CreatePopupMenuRow( "\uED25", "로컬", string.IsNullOrWhiteSpace(root) ? "현재 워크스페이스" : root, !string.IsNullOrWhiteSpace(root) && string.Equals(active, root, StringComparison.OrdinalIgnoreCase), accentBrush, secondaryText, primaryText, () => { popup.IsOpen = false; if (!string.IsNullOrWhiteSpace(root)) SwitchToWorkspace(root, root); })); if (!string.IsNullOrWhiteSpace(active) && !string.Equals(active, root, StringComparison.OrdinalIgnoreCase)) { panel.Children.Add(CreatePopupMenuRow( "\uE7BA", Path.GetFileName(active), active, true, accentBrush, secondaryText, primaryText, () => { popup.IsOpen = false; SwitchToWorkspace(active, root); })); } if (variants.Count > 0) { panel.Children.Add(CreatePopupSectionLabel($"워크트리 / 복사본 · {variants.Count}", new Thickness(8, 10, 8, 4))); foreach (var variant in variants) { var isActive = !string.IsNullOrWhiteSpace(active) && string.Equals(Path.GetFullPath(variant), Path.GetFullPath(active), StringComparison.OrdinalIgnoreCase); panel.Children.Add(CreatePopupMenuRow( "\uE8B7", Path.GetFileName(variant), isActive ? $"현재 선택 · {variant}" : variant, isActive, accentBrush, secondaryText, primaryText, () => { popup.IsOpen = false; SwitchToWorkspace(variant, root); })); } } panel.Children.Add(CreatePopupSectionLabel("새 작업 위치", new Thickness(8, 10, 8, 4))); panel.Children.Add(CreatePopupMenuRow( "\uE943", "현재 브랜치로 워크트리 생성", "Git 저장소면 분리된 작업 복사본을 만들고 전환합니다", false, accentBrush, secondaryText, primaryText, () => { popup.IsOpen = false; _ = CreateCurrentBranchWorktreeAsync(); })); popup.IsOpen = true; } private Border CreatePopupMenuRow( string icon, string title, string description, bool selected, Brush accentBrush, Brush secondaryText, Brush primaryText, Action? onClick) { var hintBackground = TryFindResource("HintBackground") as Brush ?? BrushFromHex("#F8FAFC"); var hoverBackground = TryFindResource("ItemHoverBackground") as Brush ?? BrushFromHex("#F8FAFC"); var row = new Border { Background = selected ? hintBackground : Brushes.Transparent, BorderBrush = selected ? BrushFromHex("#D6E4FF") : Brushes.Transparent, BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(12), Padding = new Thickness(12, 10, 12, 10), Cursor = Cursors.Hand, Margin = new Thickness(0, 0, 0, 6), }; var grid = new Grid(); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); var iconBlock = new TextBlock { Text = icon, FontFamily = new FontFamily("Segoe MDL2 Assets"), FontSize = 14, Foreground = selected ? accentBrush : secondaryText, Margin = new Thickness(0, 1, 10, 0), VerticalAlignment = VerticalAlignment.Center, }; grid.Children.Add(iconBlock); var textStack = new StackPanel(); textStack.Children.Add(new TextBlock { Text = title, FontSize = 13.5, FontWeight = selected ? FontWeights.SemiBold : FontWeights.Medium, Foreground = primaryText, }); if (!string.IsNullOrWhiteSpace(description)) { textStack.Children.Add(new TextBlock { Text = description, FontSize = 11.5, Foreground = secondaryText, Margin = new Thickness(0, 3, 0, 0), TextWrapping = TextWrapping.Wrap, }); } Grid.SetColumn(textStack, 1); grid.Children.Add(textStack); if (selected) { var check = CreateSimpleCheck(accentBrush, 14); Grid.SetColumn(check, 2); check.Margin = new Thickness(10, 0, 0, 0); if (check is FrameworkElement element) element.VerticalAlignment = VerticalAlignment.Center; grid.Children.Add(check); } row.Child = grid; row.MouseEnter += (_, _) => row.Background = selected ? hintBackground : hoverBackground; row.MouseLeave += (_, _) => row.Background = selected ? hintBackground : Brushes.Transparent; row.MouseLeftButtonUp += (_, _) => onClick?.Invoke(); return row; } }