[Phase L2-4/L2-5] 클립보드 OCR 이미지 검색 + Ctrl+Click 다중 선택

Phase L2-4: 클립보드 이미지 OCR 텍스트 추출 및 검색
- AxCopilot.csproj: TFM net8.0-windows → net8.0-windows10.0.17763.0 (Windows OCR API 활성화)
- ClipboardEntry: OcrText 프로퍼티 추가 (set), Preview → OCR 텍스트 우선 표시 (72자 상한)
- SavedClipEntry: OcrText 직렬화 필드 추가, BuildSnapshot/LoadHistory 연동
- ClipboardHistoryService.OnClipboardUpdate: 이미지 저장 후 백그라운드 OCR 트리거
  (EnableOcrSearch 설정 체크, capturedEntry.OcrText 비동기 갱신)
- ClipboardHistoryService.ImageCache.cs: ExtractOcrTextAsync() 추가
  (WinRT BitmapDecoder → SoftwareBitmap → OcrEngine.RecognizeAsync, 5,000자 상한)
  WinRT 별칭(WinBitmapDecoder, WinSoftwareBitmap 등) 으로 WPF 네임스페이스 충돌 방지
- AppSettings.Models.cs: ClipboardHistorySettings.EnableOcrSearch (default=true)
- ClipboardHistoryHandler.GetItemsAsync: OcrText 포함 검색, 'OCR ·' 표시 배지

Phase L2-5: Ctrl+Click 클립보드 항목 다중 선택
- LauncherWindow.Shell.cs: ResultList_PreviewMouseLeftButtonUp에 Ctrl+Click 분기 추가
  (IsClipboardMode + Ctrl 조합 시 ToggleMergeItem 호출, 기존 단일 선택 흐름 유지)
- LauncherWindow.ShortcutHelp.cs: Ctrl+Click / Shift+↑↓ / 병합 단축키 도움말 추가

빌드: 경고 0, 오류 0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 10:18:45 +09:00
parent e78da8eb81
commit d6d5f518d0
8 changed files with 120 additions and 8 deletions

View File

@@ -72,9 +72,11 @@ public class ClipboardHistoryHandler : IActionHandler
else if (catFilter != null)
filtered = filtered.Where(e => e.Category == catFilter);
// 텍스트 검색
// 텍스트 검색 — 텍스트 내용, Preview, OCR 추출 텍스트 모두 포함 (Phase L2-4)
if (!string.IsNullOrEmpty(q))
filtered = filtered.Where(e => e.Preview.ToLowerInvariant().Contains(q));
filtered = filtered.Where(e =>
e.Preview.ToLowerInvariant().Contains(q) ||
(e.OcrText != null && e.OcrText.ToLowerInvariant().Contains(q)));
// 핀 항목을 상단에 배치
var sorted = filtered
@@ -86,9 +88,11 @@ public class ClipboardHistoryHandler : IActionHandler
{
var pinMark = e.IsPinned ? "\uD83D\uDCCC " : "";
var catMark = e.Category != "일반" ? $"[{e.Category}] " : "";
// 이미지 항목에 OCR 인덱스 완료 표시
var ocrMark = !e.IsText && e.OcrText != null ? "OCR · " : "";
return new LauncherItem(
$"{pinMark}{e.Preview}",
$"{catMark}{e.RelativeTime} · {e.CopiedAt:MM/dd HH:mm}",
$"{catMark}{ocrMark}{e.RelativeTime} · {e.CopiedAt:MM/dd HH:mm}",
null,
e,
Symbol: e.IsPinned ? Symbols.Favorite : (e.IsText ? Symbols.History : Symbols.Picture));