AX Agent surface visual language 공통화 및 preview/file browser 정리
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- ChatWindow에 공통 popup container/menu item/separator/file tree header helper를 추가 - Preview와 FileBrowser presentation이 같은 surface 스타일을 사용하도록 정리 - README와 DEVELOPMENT 문서에 2026-04-06 11:20 (KST) 기준 visual polish 1차 반영 - dotnet build 검증 경고 0, 오류 0 확인
This commit is contained in:
@@ -1197,3 +1197,6 @@ MIT License
|
||||
- 업데이트: 2026-04-06 11:11 (KST)
|
||||
- 좌측 대화 목록의 필터/정렬 interaction을 [ChatWindow.ConversationFilterPresentation.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.ConversationFilterPresentation.cs) 로 분리했다. 실행 중 보기, 최근/활동 정렬, 대화 목록 선호 저장/복원, 관련 버튼 UI 상태 갱신이 메인 [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 밖으로 이동해 sidebar 상태 표현 책임이 더 응집도 있게 정리됐다.
|
||||
- 이 단계까지 누적 완료된 구조 개선은 상태선/권한/도구 결과 카탈로그화, inline ask/plan 분리, footer/Git/preset/list/message/timeline/conversation management/sidebar interaction/filter 분리까지다. 이제 남는 건 큰 분리가 아니라 실제 시나리오 기반 polish와 공통 시각 언어 고도화다.
|
||||
- 업데이트: 2026-04-06 11:20 (KST)
|
||||
- preview/file browser의 popup과 row 스타일을 공통 surface helper로 통일했다. [ChatWindow.SurfaceVisualPresentation.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.SurfaceVisualPresentation.cs)를 추가해 popup container, popup menu item, separator, file tree header를 공통 helper로 만들고, [ChatWindow.PreviewPresentation.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.PreviewPresentation.cs) 와 [ChatWindow.FileBrowserPresentation.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.FileBrowserPresentation.cs) 가 같은 surface 언어를 쓰도록 맞췄다.
|
||||
- 이 단계는 큰 구조 분리 이후의 visual language polish 1차로, preview와 file browser가 서로 다른 위젯처럼 보이던 차이를 줄이고 이후 공통 popup/surface 확장을 쉽게 하는 기반을 마련했다.
|
||||
|
||||
@@ -4935,3 +4935,5 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎.
|
||||
- Document update: 2026-04-06 11:03 (KST) - This leaves the main chat window even more orchestration-focused and effectively closes the last obvious sidebar interaction block from the large structure-improvement plan. Remaining work is now follow-up UX polish rather than major decomposition.
|
||||
- Document update: 2026-04-06 11:11 (KST) - Split conversation list filter/sort interactions and preference persistence out of `ChatWindow.xaml.cs` into `ChatWindow.ConversationFilterPresentation.cs`. Running-only toggles, recent/activity sort toggles, UI-state refresh, and conversation-list preference apply/persist logic now live in a dedicated presentation partial.
|
||||
- Document update: 2026-04-06 11:11 (KST) - This keeps sidebar state presentation more cohesive and further narrows the main chat window file toward orchestration-only responsibilities. Remaining work is now post-plan polish rather than another major structural split.
|
||||
- Document update: 2026-04-06 11:20 (KST) - Added `ChatWindow.SurfaceVisualPresentation.cs` and introduced shared surface helpers for popup containers, popup menu rows, separators, and file-tree headers. `ChatWindow.PreviewPresentation.cs` and `ChatWindow.FileBrowserPresentation.cs` now use the same popup/surface language instead of duplicating slightly different visual styles.
|
||||
- Document update: 2026-04-06 11:20 (KST) - This is the first visual-language polish pass after the large structure split. It reduces the feeling that preview and file browser are separate widgets and makes future popup/surface refinements reusable.
|
||||
|
||||
@@ -94,7 +94,7 @@ public partial class ChatWindow
|
||||
count++;
|
||||
var dirItem = new TreeViewItem
|
||||
{
|
||||
Header = CreateFileTreeHeader("\uED25", subDir.Name, null),
|
||||
Header = CreateSurfaceFileTreeHeader("\uED25", subDir.Name, null),
|
||||
Tag = subDir.FullName,
|
||||
IsExpanded = depth < 1,
|
||||
};
|
||||
@@ -140,7 +140,7 @@ public partial class ChatWindow
|
||||
|
||||
var fileItem = new TreeViewItem
|
||||
{
|
||||
Header = CreateFileTreeHeader(icon, file.Name, size),
|
||||
Header = CreateSurfaceFileTreeHeader(icon, file.Name, size),
|
||||
Tag = file.FullName,
|
||||
};
|
||||
|
||||
@@ -167,38 +167,6 @@ public partial class ChatWindow
|
||||
}
|
||||
}
|
||||
|
||||
private static StackPanel CreateFileTreeHeader(string icon, string name, string? sizeText)
|
||||
{
|
||||
var sp = new StackPanel { Orientation = Orientation.Horizontal };
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = icon,
|
||||
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
||||
FontSize = 11,
|
||||
Foreground = new SolidColorBrush(Color.FromRgb(0x9C, 0xA3, 0xAF)),
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = new Thickness(0, 0, 5, 0),
|
||||
});
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = name,
|
||||
FontSize = 11.5,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
});
|
||||
if (sizeText != null)
|
||||
{
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = $" {sizeText}",
|
||||
FontSize = 10,
|
||||
Foreground = new SolidColorBrush(Color.FromRgb(0x6B, 0x72, 0x80)),
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
});
|
||||
}
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
private static string GetFileIcon(string ext) => ext switch
|
||||
{
|
||||
".html" or ".htm" => "\uEB41",
|
||||
@@ -224,11 +192,8 @@ public partial class ChatWindow
|
||||
|
||||
private void ShowFileTreeContextMenu(string filePath)
|
||||
{
|
||||
var bg = TryFindResource("LauncherBackground") as Brush ?? new SolidColorBrush(Color.FromRgb(0x1E, 0x1E, 0x2E));
|
||||
var borderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
||||
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White;
|
||||
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
||||
var hoverBg = TryFindResource("HintBackground") as Brush ?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
|
||||
var dangerBrush = new SolidColorBrush(Color.FromRgb(0xE7, 0x4C, 0x3C));
|
||||
|
||||
var popup = new Popup
|
||||
@@ -239,73 +204,22 @@ public partial class ChatWindow
|
||||
Placement = PlacementMode.MousePoint,
|
||||
};
|
||||
var panel = new StackPanel { Margin = new Thickness(2) };
|
||||
var container = new Border
|
||||
{
|
||||
Background = bg,
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(10),
|
||||
Padding = new Thickness(6),
|
||||
MinWidth = 200,
|
||||
Effect = new System.Windows.Media.Effects.DropShadowEffect
|
||||
{
|
||||
BlurRadius = 16,
|
||||
ShadowDepth = 4,
|
||||
Opacity = 0.3,
|
||||
Color = Colors.Black,
|
||||
Direction = 270,
|
||||
},
|
||||
Child = panel,
|
||||
};
|
||||
var container = CreateSurfacePopupContainer(panel, 200, new Thickness(6));
|
||||
popup.Child = container;
|
||||
|
||||
void AddItem(string icon, string label, Action action, Brush? labelColor = null, Brush? iconColor = null)
|
||||
{
|
||||
var sp = new StackPanel { Orientation = Orientation.Horizontal };
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = icon,
|
||||
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
||||
FontSize = 13,
|
||||
Foreground = iconColor ?? secondaryText,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = new Thickness(0, 0, 10, 0),
|
||||
});
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = label,
|
||||
FontSize = 12.5,
|
||||
Foreground = labelColor ?? primaryText,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
});
|
||||
var item = new Border
|
||||
{
|
||||
Child = sp,
|
||||
Background = Brushes.Transparent,
|
||||
CornerRadius = new CornerRadius(7),
|
||||
Cursor = Cursors.Hand,
|
||||
Padding = new Thickness(10, 8, 14, 8),
|
||||
Margin = new Thickness(0, 1, 0, 1),
|
||||
};
|
||||
item.MouseEnter += (s, _) => { if (s is Border b) b.Background = hoverBg; };
|
||||
item.MouseLeave += (s, _) => { if (s is Border b) b.Background = Brushes.Transparent; };
|
||||
item.MouseLeftButtonUp += (_, _) =>
|
||||
var item = CreateSurfacePopupMenuItem(icon, iconColor ?? secondaryText, label, () =>
|
||||
{
|
||||
popup.IsOpen = false;
|
||||
action();
|
||||
};
|
||||
}, labelColor ?? primaryText);
|
||||
panel.Children.Add(item);
|
||||
}
|
||||
|
||||
void AddSep()
|
||||
{
|
||||
panel.Children.Add(new Border
|
||||
{
|
||||
Height = 1,
|
||||
Margin = new Thickness(10, 4, 10, 4),
|
||||
Background = borderBrush,
|
||||
Opacity = 0.3,
|
||||
});
|
||||
panel.Children.Add(CreateSurfacePopupSeparator());
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(filePath).ToLowerInvariant();
|
||||
|
||||
@@ -514,61 +514,27 @@ public partial class ChatWindow
|
||||
if (_previewTabPopup != null)
|
||||
_previewTabPopup.IsOpen = false;
|
||||
|
||||
var bg = TryFindResource("LauncherBackground") as Brush ?? new SolidColorBrush(Color.FromRgb(0x1E, 0x1E, 0x2E));
|
||||
var borderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
||||
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White;
|
||||
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
||||
var hoverBg = TryFindResource("ItemHoverBackground") as Brush ?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
|
||||
|
||||
var stack = new StackPanel();
|
||||
|
||||
void AddItem(string icon, string iconColor, string label, Action action)
|
||||
{
|
||||
var itemBorder = new Border
|
||||
{
|
||||
Background = Brushes.Transparent,
|
||||
CornerRadius = new CornerRadius(6),
|
||||
Padding = new Thickness(10, 7, 16, 7),
|
||||
Cursor = Cursors.Hand,
|
||||
};
|
||||
var sp = new StackPanel { Orientation = Orientation.Horizontal };
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = icon,
|
||||
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
||||
FontSize = 12,
|
||||
Foreground = string.IsNullOrEmpty(iconColor)
|
||||
? secondaryText
|
||||
: new SolidColorBrush((Color)ColorConverter.ConvertFromString(iconColor)),
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = new Thickness(0, 0, 8, 0),
|
||||
});
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = label,
|
||||
FontSize = 13,
|
||||
Foreground = primaryText,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
});
|
||||
itemBorder.Child = sp;
|
||||
itemBorder.MouseEnter += (s, _) => { if (s is Border b) b.Background = hoverBg; };
|
||||
itemBorder.MouseLeave += (s, _) => { if (s is Border b) b.Background = Brushes.Transparent; };
|
||||
itemBorder.MouseLeftButtonUp += (_, _) =>
|
||||
var iconBrush = string.IsNullOrEmpty(iconColor)
|
||||
? secondaryText
|
||||
: new SolidColorBrush((Color)ColorConverter.ConvertFromString(iconColor));
|
||||
var itemBorder = CreateSurfacePopupMenuItem(icon, iconBrush, label, () =>
|
||||
{
|
||||
_previewTabPopup!.IsOpen = false;
|
||||
action();
|
||||
};
|
||||
}, primaryText, 13);
|
||||
stack.Children.Add(itemBorder);
|
||||
}
|
||||
|
||||
void AddSeparator()
|
||||
{
|
||||
stack.Children.Add(new Border
|
||||
{
|
||||
Height = 1,
|
||||
Background = borderBrush,
|
||||
Margin = new Thickness(8, 3, 8, 3),
|
||||
});
|
||||
stack.Children.Add(CreateSurfacePopupSeparator());
|
||||
}
|
||||
|
||||
AddItem("\uE8A7", "#64B5F6", "외부 프로그램으로 열기", () =>
|
||||
@@ -628,23 +594,7 @@ public partial class ChatWindow
|
||||
});
|
||||
}
|
||||
|
||||
var popupBorder = new Border
|
||||
{
|
||||
Background = bg,
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(12),
|
||||
Padding = new Thickness(4, 6, 4, 6),
|
||||
MinWidth = 180,
|
||||
Effect = new System.Windows.Media.Effects.DropShadowEffect
|
||||
{
|
||||
BlurRadius = 16,
|
||||
Opacity = 0.4,
|
||||
ShadowDepth = 4,
|
||||
Color = Colors.Black,
|
||||
},
|
||||
Child = stack,
|
||||
};
|
||||
var popupBorder = CreateSurfacePopupContainer(stack, 180, new Thickness(4, 6, 4, 6));
|
||||
|
||||
_previewTabPopup = new Popup
|
||||
{
|
||||
|
||||
121
src/AxCopilot/Views/ChatWindow.SurfaceVisualPresentation.cs
Normal file
121
src/AxCopilot/Views/ChatWindow.SurfaceVisualPresentation.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace AxCopilot.Views;
|
||||
|
||||
public partial class ChatWindow
|
||||
{
|
||||
private Border CreateSurfacePopupContainer(UIElement content, double minWidth = 180, Thickness? padding = null)
|
||||
{
|
||||
var bg = TryFindResource("LauncherBackground") as Brush ?? new SolidColorBrush(Color.FromRgb(0x1E, 0x1E, 0x2E));
|
||||
var borderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
||||
|
||||
return new Border
|
||||
{
|
||||
Background = bg,
|
||||
BorderBrush = borderBrush,
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(12),
|
||||
Padding = padding ?? new Thickness(6),
|
||||
MinWidth = minWidth,
|
||||
Effect = new System.Windows.Media.Effects.DropShadowEffect
|
||||
{
|
||||
BlurRadius = 16,
|
||||
ShadowDepth = 4,
|
||||
Opacity = 0.34,
|
||||
Color = Colors.Black,
|
||||
},
|
||||
Child = content,
|
||||
};
|
||||
}
|
||||
|
||||
private Border CreateSurfacePopupMenuItem(string icon, Brush iconBrush, string label, Action onClick, Brush? labelBrush = null, double fontSize = 12.5)
|
||||
{
|
||||
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White;
|
||||
var hoverBg = TryFindResource("ItemHoverBackground") as Brush ?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
|
||||
|
||||
var item = new Border
|
||||
{
|
||||
Background = Brushes.Transparent,
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Padding = new Thickness(10, 8, 14, 8),
|
||||
Margin = new Thickness(0, 1, 0, 1),
|
||||
Cursor = Cursors.Hand,
|
||||
};
|
||||
|
||||
var row = new StackPanel { Orientation = Orientation.Horizontal };
|
||||
row.Children.Add(new TextBlock
|
||||
{
|
||||
Text = icon,
|
||||
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
||||
FontSize = 13,
|
||||
Foreground = iconBrush,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = new Thickness(0, 0, 10, 0),
|
||||
});
|
||||
row.Children.Add(new TextBlock
|
||||
{
|
||||
Text = label,
|
||||
FontSize = fontSize,
|
||||
Foreground = labelBrush ?? primaryText,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
});
|
||||
item.Child = row;
|
||||
|
||||
item.MouseEnter += (s, _) => { if (s is Border b) b.Background = hoverBg; };
|
||||
item.MouseLeave += (s, _) => { if (s is Border b) b.Background = Brushes.Transparent; };
|
||||
item.MouseLeftButtonUp += (_, _) => onClick();
|
||||
return item;
|
||||
}
|
||||
|
||||
private Border CreateSurfacePopupSeparator()
|
||||
{
|
||||
var borderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
||||
return new Border
|
||||
{
|
||||
Height = 1,
|
||||
Margin = new Thickness(10, 4, 10, 4),
|
||||
Background = borderBrush,
|
||||
Opacity = 0.3,
|
||||
};
|
||||
}
|
||||
|
||||
private StackPanel CreateSurfaceFileTreeHeader(string icon, string name, string? sizeText)
|
||||
{
|
||||
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
||||
|
||||
var sp = new StackPanel { Orientation = Orientation.Horizontal };
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = icon,
|
||||
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
||||
FontSize = 11,
|
||||
Foreground = secondaryText,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = new Thickness(0, 0, 5, 0),
|
||||
});
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = name,
|
||||
FontSize = 11.5,
|
||||
Foreground = TryFindResource("PrimaryText") as Brush ?? Brushes.White,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
});
|
||||
if (!string.IsNullOrEmpty(sizeText))
|
||||
{
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = $" {sizeText}",
|
||||
FontSize = 10,
|
||||
Foreground = secondaryText,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
});
|
||||
}
|
||||
|
||||
return sp;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user