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 ImageExts = new(StringComparer.OrdinalIgnoreCase) { ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp", ".ico", ".tiff", ".tif" }; private static readonly HashSet 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" }; }