- Agent Compare 기준으로 런처 빠른 실행 칩, 검색 히스토리 탐색, 선택 항목 미리보기 패널을 현재 런처에 이식 - 하단 위젯 바, QuickLook(F3), 화면 OCR(F4), 관련 서비스/partial 파일을 현재 LauncherWindow/LauncherViewModel 구조에 연결 - UsageRankingService 상위 항목 조회와 SearchHistoryService를 추가해 실행 상위 경로/검색 기록이 실제 런처 동작에 반영되도록 정리 - README.md, docs/DEVELOPMENT.md에 이식 범위와 검증 결과를 2026-04-05 11:58 (KST) 기준으로 기록 검증 결과 - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ 경고 0 / 오류 0
144 lines
4.5 KiB
C#
144 lines
4.5 KiB
C#
using System.IO;
|
|
using System.Text;
|
|
using System.Windows;
|
|
using System.Windows.Input;
|
|
using System.Windows.Media.Imaging;
|
|
using UglyToad.PdfPig;
|
|
|
|
namespace AxCopilot.Views;
|
|
|
|
public partial class QuickLookWindow : Window
|
|
{
|
|
private static readonly HashSet<string> ImageExts = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp", ".ico", ".tiff", ".tif"
|
|
};
|
|
|
|
private static readonly HashSet<string> TextExts = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
".txt", ".md", ".cs", ".vb", ".fs", ".py", ".js", ".ts", ".jsx", ".tsx",
|
|
".json", ".xml", ".xaml", ".yaml", ".yml", ".toml", ".ini", ".conf",
|
|
".log", ".csv", ".html", ".htm", ".css", ".scss", ".less",
|
|
".sql", ".sh", ".bash", ".bat", ".cmd", ".ps1",
|
|
".config", ".env", ".gitignore", ".editorconfig",
|
|
".java", ".cpp", ".c", ".h", ".hpp", ".rs", ".go", ".rb", ".php", ".swift",
|
|
".vue", ".svelte", ".dockerfile"
|
|
};
|
|
|
|
public QuickLookWindow(string path, Window owner)
|
|
{
|
|
InitializeComponent();
|
|
Owner = owner;
|
|
KeyDown += OnKeyDown;
|
|
Loaded += (_, _) => LoadPreview(path);
|
|
}
|
|
|
|
private void OnKeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.Key is Key.Escape or Key.F3)
|
|
{
|
|
Close();
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
|
|
private void TitleBar_MouseDown(object sender, MouseButtonEventArgs e)
|
|
{
|
|
if (e.LeftButton == MouseButtonState.Pressed)
|
|
DragMove();
|
|
}
|
|
|
|
private void BtnClose_Click(object sender, MouseButtonEventArgs e) => Close();
|
|
|
|
private void LoadPreview(string path)
|
|
{
|
|
try
|
|
{
|
|
FileNameText.Text = Path.GetFileName(path);
|
|
|
|
if (Directory.Exists(path))
|
|
{
|
|
ShowInfo("\uE838", "폴더", path);
|
|
return;
|
|
}
|
|
|
|
if (!File.Exists(path))
|
|
{
|
|
ShowInfo("\uE783", "파일을 찾을 수 없습니다.", path);
|
|
return;
|
|
}
|
|
|
|
var info = new FileInfo(path);
|
|
var ext = Path.GetExtension(path);
|
|
FooterPath.Text = path;
|
|
FooterMeta.Text = $"{FormatSize(info.Length)} · {info.LastWriteTime:yyyy-MM-dd HH:mm}";
|
|
|
|
if (ImageExts.Contains(ext))
|
|
{
|
|
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
|
var image = new BitmapImage();
|
|
image.BeginInit();
|
|
image.CacheOption = BitmapCacheOption.OnLoad;
|
|
image.StreamSource = fs;
|
|
image.EndInit();
|
|
image.Freeze();
|
|
|
|
PreviewImage.Source = image;
|
|
ImageScrollViewer.Visibility = Visibility.Visible;
|
|
FileTypeIcon.Text = "\uE91B";
|
|
return;
|
|
}
|
|
|
|
if (string.Equals(ext, ".pdf", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
using var doc = PdfDocument.Open(path);
|
|
var page = doc.NumberOfPages > 0 ? doc.GetPage(1).Text : "";
|
|
PdfPreviewText.Text = page.Length > 1200 ? page[..1200] + "…" : page;
|
|
PdfScrollViewer.Visibility = Visibility.Visible;
|
|
FileTypeIcon.Text = "\uEA90";
|
|
return;
|
|
}
|
|
|
|
if (TextExts.Contains(ext))
|
|
{
|
|
string text;
|
|
try
|
|
{
|
|
text = File.ReadAllText(path, Encoding.UTF8);
|
|
}
|
|
catch
|
|
{
|
|
text = File.ReadAllText(path);
|
|
}
|
|
|
|
PreviewText.Text = text.Length > 4000 ? text[..4000] + "\n…" : text;
|
|
TextScrollViewer.Visibility = Visibility.Visible;
|
|
FileTypeIcon.Text = "\uE8A5";
|
|
return;
|
|
}
|
|
|
|
ShowInfo("\uE7C3", $"파일 · {ext.TrimStart('.').ToUpperInvariant()}", path);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ShowInfo("\uEA39", "미리보기를 열지 못했습니다.", ex.Message);
|
|
}
|
|
}
|
|
|
|
private void ShowInfo(string icon, string title, string detail)
|
|
{
|
|
InfoTypeIcon.Text = icon;
|
|
InfoTypeName.Text = title;
|
|
InfoSubText.Text = detail;
|
|
InfoPanel.Visibility = Visibility.Visible;
|
|
}
|
|
|
|
private static string FormatSize(long bytes) => bytes switch
|
|
{
|
|
< 1024 => $"{bytes} B",
|
|
< 1048576 => $"{bytes / 1024.0:F1} KB",
|
|
< 1073741824 => $"{bytes / 1048576.0:F1} MB",
|
|
_ => $"{bytes / 1073741824.0:F2} GB"
|
|
};
|
|
}
|