diff --git a/src/AxCopilot/Views/ClipboardImagePreviewWindow.xaml b/src/AxCopilot/Views/ClipboardImagePreviewWindow.xaml
new file mode 100644
index 0000000..8a0dd13
--- /dev/null
+++ b/src/AxCopilot/Views/ClipboardImagePreviewWindow.xaml
@@ -0,0 +1,225 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AxCopilot/Views/ClipboardImagePreviewWindow.xaml.cs b/src/AxCopilot/Views/ClipboardImagePreviewWindow.xaml.cs
new file mode 100644
index 0000000..bc15721
--- /dev/null
+++ b/src/AxCopilot/Views/ClipboardImagePreviewWindow.xaml.cs
@@ -0,0 +1,231 @@
+using System.IO;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media.Imaging;
+using AxCopilot.Services;
+using Microsoft.Win32;
+
+namespace AxCopilot.Views;
+
+///
+/// Phase L2-3: 클립보드 이미지 미리보기 창.
+/// 원본 해상도 이미지 표시 + 확대/축소 + 클립보드 복사 + 파일 저장.
+/// Shift+Enter (이미지 항목 선택 시) 또는 런처 내 미리보기 액션으로 열립니다.
+///
+public partial class ClipboardImagePreviewWindow : Window
+{
+ private readonly ClipboardEntry _entry;
+ private BitmapSource? _fullImage;
+ private double _zoomFactor = 1.0;
+
+ private const double ZoomStep = 0.25;
+ private const double MinZoom = 0.05;
+ private const double MaxZoom = 8.0;
+
+ public ClipboardImagePreviewWindow(ClipboardEntry entry)
+ {
+ InitializeComponent();
+ _entry = entry;
+ KeyDown += OnKeyDown;
+ Loaded += OnLoaded;
+ }
+
+ // ─── 초기화 ──────────────────────────────────────────────────────────
+
+ private void OnLoaded(object sender, RoutedEventArgs e)
+ {
+ LoadImage();
+ }
+
+ private void LoadImage()
+ {
+ // 원본 해상도 이미지 우선, 없으면 썸네일 폴백
+ _fullImage = ClipboardHistoryService.LoadOriginalImage(_entry.OriginalImagePath)
+ ?? _entry.Image;
+
+ if (_fullImage == null)
+ {
+ SizeLabel.Text = "(이미지 없음)";
+ return;
+ }
+
+ PreviewImage.Source = _fullImage;
+ SizeLabel.Text = $"{_fullImage.PixelWidth} × {_fullImage.PixelHeight} px";
+
+ // 창 레이아웃이 확정된 후 Fit 적용
+ Dispatcher.InvokeAsync(FitToWindow, System.Windows.Threading.DispatcherPriority.Loaded);
+ }
+
+ // ─── 줌 제어 ─────────────────────────────────────────────────────────
+
+ private void FitToWindow()
+ {
+ if (_fullImage == null) return;
+ var availW = ImageScroll.ActualWidth - 24;
+ var availH = ImageScroll.ActualHeight - 24;
+ if (availW <= 0 || availH <= 0) return;
+
+ var scaleX = availW / _fullImage.PixelWidth;
+ var scaleY = availH / _fullImage.PixelHeight;
+ _zoomFactor = Math.Min(1.0, Math.Min(scaleX, scaleY));
+ ApplyZoom();
+ }
+
+ private void ApplyZoom()
+ {
+ ZoomTransform.ScaleX = _zoomFactor;
+ ZoomTransform.ScaleY = _zoomFactor;
+ ZoomLabel.Text = $"{_zoomFactor * 100:F0}%";
+ }
+
+ // ─── 타이틀바 ────────────────────────────────────────────────────────
+
+ 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 BtnZoomIn_Click(object sender, MouseButtonEventArgs e)
+ {
+ _zoomFactor = Math.Min(MaxZoom, Math.Round(_zoomFactor + ZoomStep, 2));
+ ApplyZoom();
+ }
+
+ private void BtnZoomOut_Click(object sender, MouseButtonEventArgs e)
+ {
+ _zoomFactor = Math.Max(MinZoom, Math.Round(_zoomFactor - ZoomStep, 2));
+ ApplyZoom();
+ }
+
+ private void BtnActualSize_Click(object sender, MouseButtonEventArgs e)
+ {
+ _zoomFactor = 1.0;
+ ApplyZoom();
+ }
+
+ private void BtnFitToWindow_Click(object sender, MouseButtonEventArgs e)
+ => FitToWindow();
+
+ private void BtnCopy_Click(object sender, MouseButtonEventArgs e)
+ => CopyToClipboard();
+
+ private void BtnSave_Click(object sender, MouseButtonEventArgs e)
+ => SaveToFile();
+
+ // ─── 스크롤 휠 줌 ────────────────────────────────────────────────────
+
+ private void ImageScroll_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
+ {
+ if ((Keyboard.Modifiers & ModifierKeys.Control) == 0) return;
+ e.Handled = true;
+
+ var delta = e.Delta > 0 ? ZoomStep : -ZoomStep;
+ _zoomFactor = Math.Clamp(Math.Round(_zoomFactor + delta, 2), MinZoom, MaxZoom);
+ ApplyZoom();
+ }
+
+ // ─── 키보드 단축키 ───────────────────────────────────────────────────
+
+ private void OnKeyDown(object sender, KeyEventArgs e)
+ {
+ switch (e.Key)
+ {
+ case Key.Escape:
+ Close();
+ e.Handled = true;
+ break;
+
+ case Key.Add:
+ case Key.OemPlus:
+ _zoomFactor = Math.Min(MaxZoom, Math.Round(_zoomFactor + ZoomStep, 2));
+ ApplyZoom();
+ e.Handled = true;
+ break;
+
+ case Key.Subtract:
+ case Key.OemMinus:
+ _zoomFactor = Math.Max(MinZoom, Math.Round(_zoomFactor - ZoomStep, 2));
+ ApplyZoom();
+ e.Handled = true;
+ break;
+
+ case Key.D0:
+ case Key.NumPad0:
+ _zoomFactor = 1.0;
+ ApplyZoom();
+ e.Handled = true;
+ break;
+
+ case Key.F:
+ FitToWindow();
+ e.Handled = true;
+ break;
+
+ case Key.C when (Keyboard.Modifiers & ModifierKeys.Control) != 0:
+ CopyToClipboard();
+ e.Handled = true;
+ break;
+
+ case Key.S when (Keyboard.Modifiers & ModifierKeys.Control) != 0:
+ SaveToFile();
+ e.Handled = true;
+ break;
+ }
+ }
+
+ // ─── 내부 동작 ───────────────────────────────────────────────────────
+
+ private void CopyToClipboard()
+ {
+ if (_fullImage == null) return;
+ try
+ {
+ Clipboard.SetImage(_fullImage);
+ NotificationCenterService.Show("클립보드", "이미지를 클립보드에 복사했습니다.",
+ NotificationType.Success);
+ }
+ catch (Exception ex)
+ {
+ NotificationCenterService.Show("복사 실패", ex.Message, NotificationType.Error);
+ }
+ }
+
+ private void SaveToFile()
+ {
+ if (_fullImage == null) return;
+
+ var dlg = new SaveFileDialog
+ {
+ Title = "이미지 저장",
+ Filter = "PNG 이미지 (*.png)|*.png|JPEG 이미지 (*.jpg)|*.jpg|BMP 이미지 (*.bmp)|*.bmp",
+ FileName = $"clipboard_{DateTime.Now:yyyyMMdd_HHmmss}",
+ DefaultExt = "png",
+ };
+ if (dlg.ShowDialog(this) != true) return;
+
+ try
+ {
+ BitmapEncoder encoder = Path.GetExtension(dlg.FileName).ToLowerInvariant() switch
+ {
+ ".jpg" or ".jpeg" => new JpegBitmapEncoder { QualityLevel = 95 },
+ ".bmp" => new BmpBitmapEncoder(),
+ _ => new PngBitmapEncoder(),
+ };
+ encoder.Frames.Add(BitmapFrame.Create(_fullImage));
+ using var fs = File.OpenWrite(dlg.FileName);
+ encoder.Save(fs);
+
+ NotificationCenterService.Show("저장 완료", Path.GetFileName(dlg.FileName),
+ NotificationType.Success);
+ }
+ catch (Exception ex)
+ {
+ NotificationCenterService.Show("저장 실패", ex.Message, NotificationType.Error);
+ }
+ }
+}
diff --git a/src/AxCopilot/Views/LauncherWindow.Keyboard.cs b/src/AxCopilot/Views/LauncherWindow.Keyboard.cs
index 749c00b..dc6c006 100644
--- a/src/AxCopilot/Views/LauncherWindow.Keyboard.cs
+++ b/src/AxCopilot/Views/LauncherWindow.Keyboard.cs
@@ -50,6 +50,16 @@ public partial class LauncherWindow
if (shift)
{
+ // Phase L2-3: 클립보드 이미지 항목 → 이미지 미리보기 창 열기
+ if (_vm.SelectedItem?.Data is AxCopilot.Services.ClipboardEntry imgEntry && !imgEntry.IsText)
+ {
+ e.Handled = true;
+ Hide();
+ var previewWin = new ClipboardImagePreviewWindow(imgEntry);
+ previewWin.Show();
+ break;
+ }
+
// 퍼지 파일 검색 결과: Shift+Enter → 파일이 있는 폴더 열기
if (_vm.SelectedItem?.Data is AxCopilot.Services.IndexEntry shiftEntry)
{
diff --git a/src/AxCopilot/Views/LauncherWindow.ShortcutHelp.cs b/src/AxCopilot/Views/LauncherWindow.ShortcutHelp.cs
index 582d4a4..de11d9d 100644
--- a/src/AxCopilot/Views/LauncherWindow.ShortcutHelp.cs
+++ b/src/AxCopilot/Views/LauncherWindow.ShortcutHelp.cs
@@ -41,7 +41,7 @@ public partial class LauncherWindow
"Ctrl+Shift+E 탐색기에서 열기",
"Ctrl+Enter 관리자 실행",
"Alt+Enter 속성 보기",
- "Shift+Enter 대형 텍스트",
+ "Shift+Enter 대형 텍스트 / 이미지 미리보기 (#)",
};
CustomMessageBox.Show(