Files
AX-Copilot-Codex/.decompiledproj/AxCopilot/Handlers/ClipboardHistoryHandler.cs

193 lines
5.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using AxCopilot.SDK;
using AxCopilot.Services;
namespace AxCopilot.Handlers;
public class ClipboardHistoryHandler : IActionHandler
{
[StructLayout(LayoutKind.Explicit, Size = 40)]
private struct INPUT
{
[FieldOffset(0)]
public uint Type;
[FieldOffset(8)]
public KEYBDINPUT ki;
}
private struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public nint dwExtraInfo;
}
private readonly ClipboardHistoryService _historyService;
private static readonly Dictionary<string, string> CategoryFilters = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "url", "URL" },
{ "코드", "코드" },
{ "code", "코드" },
{ "경로", "경로" },
{ "path", "경로" },
{ "핀", "핀" },
{ "pin", "핀" }
};
public string? Prefix => "#";
public PluginMetadata Metadata => new PluginMetadata("ClipboardHistory", "클립보드 히스토리 — # 뒤에 검색어 (또는 빈 입력으로 전체 보기)", "1.0", "AX");
public ClipboardHistoryHandler(ClipboardHistoryService historyService)
{
_historyService = historyService;
}
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
{
IReadOnlyList<ClipboardEntry> history = _historyService.History;
if (history.Count == 0)
{
return Task.FromResult((IEnumerable<LauncherItem>)new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(new LauncherItem("클립보드 히스토리가 없습니다", "텍스트를 복사하면 이 곳에 기록됩니다", null, null, null, "\ue81c")));
}
string q = query.Trim().ToLowerInvariant();
string catFilter = null;
foreach (var (text3, text4) in CategoryFilters)
{
if (q == text3 || q.StartsWith(text3 + " "))
{
catFilter = text4;
object obj;
if (q.Length <= text3.Length)
{
obj = "";
}
else
{
string text5 = q;
int num = text3.Length + 1;
obj = text5.Substring(num, text5.Length - num).Trim();
}
q = (string)obj;
break;
}
}
IEnumerable<ClipboardEntry> source = history.AsEnumerable();
if (catFilter == "핀")
{
source = source.Where((ClipboardEntry e) => e.IsPinned);
}
else if (catFilter != null)
{
source = source.Where((ClipboardEntry e) => e.Category == catFilter);
}
if (!string.IsNullOrEmpty(q))
{
source = source.Where((ClipboardEntry e) => e.Preview.ToLowerInvariant().Contains(q));
}
IOrderedEnumerable<ClipboardEntry> source2 = from e in source
orderby e.IsPinned descending, e.CopiedAt descending
select e;
List<LauncherItem> list = source2.Select(delegate(ClipboardEntry e)
{
string text6 = (e.IsPinned ? "\ud83d\udccc " : "");
string value = ((e.Category != "일반") ? ("[" + e.Category + "] ") : "");
return new LauncherItem(text6 + e.Preview, $"{value}{e.RelativeTime} · {e.CopiedAt:MM/dd HH:mm}", null, e, null, e.IsPinned ? "\ue728" : (e.IsText ? "\ue81c" : "\ueb9f"));
}).ToList();
if (list.Count == 0)
{
list.Add(new LauncherItem("'" + query + "'에 해당하는 항목 없음", "#pin #url #코드 #경로 로 필터링 가능", null, null, null, "\ue81c"));
}
return Task.FromResult((IEnumerable<LauncherItem>)list);
}
public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
{
object data = item.Data;
if (!(data is ClipboardEntry entry))
{
return;
}
try
{
_historyService.SuppressNextCapture();
_historyService.PromoteEntry(entry);
if (!entry.IsText && entry.Image != null)
{
BitmapSource originalImg = ClipboardHistoryService.LoadOriginalImage(entry.OriginalImagePath);
Clipboard.SetImage(originalImg ?? entry.Image);
}
else if (!string.IsNullOrEmpty(entry.Text))
{
Clipboard.SetText(entry.Text);
nint prevWindow = WindowTracker.PreviousWindow;
if (prevWindow != IntPtr.Zero)
{
await Task.Delay(300, ct);
uint lpdwProcessId;
uint targetThread = GetWindowThreadProcessId(prevWindow, out lpdwProcessId);
uint currentThread = GetCurrentThreadId();
AttachThreadInput(currentThread, targetThread, fAttach: true);
SetForegroundWindow(prevWindow);
AttachThreadInput(currentThread, targetThread, fAttach: false);
await Task.Delay(100, ct);
SendCtrlV();
}
}
}
catch (OperationCanceledException)
{
}
catch (Exception ex2)
{
LogService.Warn("클립보드 히스토리 붙여넣기 실패: " + ex2.Message);
}
}
private static void SendCtrlV()
{
INPUT[] array = new INPUT[4];
array[0].Type = 1u;
array[0].ki.wVk = 17;
array[1].Type = 1u;
array[1].ki.wVk = 86;
array[2].Type = 1u;
array[2].ki.wVk = 86;
array[2].ki.dwFlags = 2u;
array[3].Type = 1u;
array[3].ki.wVk = 17;
array[3].ki.dwFlags = 2u;
SendInput((uint)array.Length, array, Marshal.SizeOf<INPUT>());
}
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(nint hWnd);
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(nint hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, [MarshalAs(UnmanagedType.Bool)] bool fAttach);
[DllImport("kernel32.dll")]
private static extern uint GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern uint SendInput(uint nInputs, [MarshalAs(UnmanagedType.LPArray)] INPUT[] pInputs, int cbSize);
}