[Phase L2-6] 검색 결과 그룹핑 (앱/폴더/파일/단축키)

AxCopilot.SDK/IActionHandler.cs:
- LauncherItem 레코드에 Group? 선택 매개변수 추가 (기존 호출 코드 무변경)

Core/CommandResolver.cs:
- Fuzzy 검색 결과에 Group 태깅: 앱/폴더/단축키/파일

ViewModels/LauncherViewModel.cs:
- ICollectionView GroupedResults 프로퍼티 추가
- CollectionViewSource + PropertyGroupDescription(nameof(LauncherItem.Group)) 초기화

Views/LauncherWindow.xaml:
- ItemsSource: Results → GroupedResults 변경
- ListView.GroupStyle 추가: 그룹 이름 헤더(10px, SemiBold, 흐린 보조 텍스트)
- GroupItem ContainerStyle로 헤더+아이템 수직 배치
- Group=null 항목은 NullToCollapsedConverter로 헤더 숨김

빌드: 경고 0, 오류 0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 10:36:26 +09:00
parent d6d5f518d0
commit 679de30f68
4 changed files with 57 additions and 2 deletions

View File

@@ -40,7 +40,8 @@ public record LauncherItem(
string? IconPath, // null이면 기본 아이콘 사용
object? Data, // ExecuteAsync에 전달되는 임의 데이터
string? ActionUrl = null, // Enter 시 열릴 URL (선택)
string? Symbol = null // Segoe MDL2 Assets 유니코드 심볼 (null이면 타입 기반 자동 결정)
string? Symbol = null, // Segoe MDL2 Assets 유니코드 심볼 (null이면 타입 기반 자동 결정)
string? Group = null // 검색 결과 그룹 레이블 (null이면 그룹 없음)
);
/// <summary>

View File

@@ -104,6 +104,13 @@ public class CommandResolver
_ => Symbols.Plugin
},
_ => Symbols.File
},
Group: r.Entry.Type switch
{
IndexEntryType.App => "앱",
IndexEntryType.Folder => "폴더",
IndexEntryType.Alias => "단축키",
_ => "파일"
}
)),
item => (item.Data as IndexEntry)?.Path

View File

@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using AxCopilot.Core;
using AxCopilot.Models;
@@ -41,6 +42,12 @@ public partial class LauncherViewModel : INotifyPropertyChanged
/// </summary>
public BulkObservableCollection<LauncherItem> Results { get; } = new();
/// <summary>
/// 그룹핑이 적용된 결과 뷰. prefix 없는 일반 검색에서 앱/폴더/파일/단축키 섹션 구분 표시.
/// Group == null 항목은 별도 그룹 헤더 없이 표시됩니다.
/// </summary>
public ICollectionView GroupedResults { get; }
// ─── 기본 프로퍼티 ────────────────────────────────────────────────────────
public string InputText
@@ -243,6 +250,11 @@ public partial class LauncherViewModel : INotifyPropertyChanged
{
_resolver = resolver;
_settings = settings;
// 그룹핑 뷰 초기화 (UI 스레드에서 생성 보장)
GroupedResults = CollectionViewSource.GetDefaultView(Results);
GroupedResults.GroupDescriptions.Add(
new PropertyGroupDescription(nameof(LauncherItem.Group)));
}
// ─── 런처 표시 시 초기화 ──────────────────────────────────────────────────

View File

@@ -18,6 +18,7 @@
WindowStartupLocation="Manual"
Loaded="Window_Loaded"
Deactivated="Window_Deactivated"
IsVisibleChanged="Window_IsVisibleChanged"
PreviewKeyDown="Window_PreviewKeyDown"
KeyDown="Window_KeyDown">
@@ -488,7 +489,7 @@
<!-- ─── 결과 리스트 ─── -->
<ListView x:Name="ResultList"
Grid.Row="4"
ItemsSource="{Binding Results}"
ItemsSource="{Binding GroupedResults}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
Background="Transparent"
BorderThickness="0"
@@ -507,6 +508,40 @@
<Style TargetType="ScrollBar" BasedOn="{StaticResource SlimScrollBar}"/>
</ListView.Resources>
<!-- 그룹 헤더: Group이 null/empty이면 헤더 숨김 -->
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border Padding="8,4,8,2"
Margin="0,4,0,0">
<TextBlock Text="{Binding Name}"
FontFamily="Segoe UI, Malgun Gothic"
FontSize="10"
FontWeight="SemiBold"
Foreground="{DynamicResource SecondaryText}"
Opacity="0.7"
Visibility="{Binding Name, Converter={StaticResource NullToCollapsedConverter}}"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<StackPanel>
<ContentPresenter/>
<ItemsPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>