using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;
namespace AxCopilot.Handlers;
///
/// 클립보드 히스토리 핸들러. "#" 프리픽스로 사용합니다.
/// 예: # (빈 쿼리) → 최근 클립보드 목록
/// # hello → "hello"가 포함된 항목 필터
///
public class ClipboardHistoryHandler : IActionHandler
{
private readonly ClipboardHistoryService _historyService;
public string? Prefix => "#";
public PluginMetadata Metadata => new(
"ClipboardHistory",
"클립보드 히스토리 — # 뒤에 검색어 (또는 빈 입력으로 전체 보기)",
"1.0",
"AX");
public ClipboardHistoryHandler(ClipboardHistoryService historyService)
{
_historyService = historyService;
}
private static readonly Dictionary CategoryFilters = new(StringComparer.OrdinalIgnoreCase)
{
{ "url", "URL" }, { "코드", "코드" }, { "code", "코드" },
{ "경로", "경로" }, { "path", "경로" }, { "핀", "핀" }, { "pin", "핀" },
};
public Task> GetItemsAsync(string query, CancellationToken ct)
{
var history = _historyService.History;
if (history.Count == 0)
{
return Task.FromResult>(
[
new LauncherItem(
"클립보드 히스토리가 없습니다",
"텍스트를 복사하면 이 곳에 기록됩니다",
null, null, Symbol: Symbols.History)
]);
}
var q = query.Trim().ToLowerInvariant();
string? catFilter = null;
foreach (var (prefix, cat) in CategoryFilters)
{
if (q == prefix || q.StartsWith(prefix + " "))
{
catFilter = cat;
q = q.Length > prefix.Length ? q[(prefix.Length + 1)..].Trim() : "";
break;
}
}
var filtered = history.AsEnumerable();
if (catFilter == "핀")
filtered = filtered.Where(e => e.IsPinned);
else if (catFilter != null)
filtered = filtered.Where(e => e.Category == catFilter);
if (!string.IsNullOrEmpty(q))
filtered = filtered.Where(e => e.Preview.ToLowerInvariant().Contains(q));
var sorted = filtered
.OrderByDescending(e => e.IsPinned)
.ThenByDescending(e => e.CopiedAt);
var items = sorted
.Select(e =>
{
var pinMark = e.IsPinned ? "\uD83D\uDCCC " : "";
var catMark = e.Category != "일반" ? $"[{e.Category}] " : "";
return new LauncherItem(
$"{pinMark}{e.Preview}",
$"{catMark}{e.RelativeTime} · {e.CopiedAt:MM/dd HH:mm}",
null,
e,
Symbol: e.IsPinned ? Symbols.Favorite : (e.IsText ? Symbols.History : Symbols.Picture));
})
.ToList();
if (items.Count == 0)
{
items.Add(new LauncherItem(
$"'{query}'에 해당하는 항목 없음",
"#pin #url #코드 #경로 로 필터링 가능",
null, null, Symbol: Symbols.History));
}
return Task.FromResult>(items);
}
public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
{
if (item.Data is not ClipboardEntry entry) return;
try
{
_historyService.SuppressNextCapture();
_historyService.PromoteEntry(entry);
if (!entry.IsText && entry.Image != null)
{
var originalImg = ClipboardHistoryService.LoadOriginalImage(entry.OriginalImagePath);
Clipboard.SetImage(originalImg ?? entry.Image);
return;
}
if (string.IsNullOrEmpty(entry.Text)) return;
Clipboard.SetText(entry.Text);
var prevWindow = WindowTracker.PreviousWindow;
if (prevWindow == IntPtr.Zero) return;
await ForegroundPasteHelper.PasteClipboardAsync(prevWindow, ct, initialDelayMs: 260);
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
LogService.Warn($"클립보드 히스토리 붙여넣기 실패: {ex.Message}");
}
}
}