139 lines
5.4 KiB
C#
139 lines
5.4 KiB
C#
using System.Windows;
|
|
using AxCopilot.Models;
|
|
using AxCopilot.SDK;
|
|
using AxCopilot.Services;
|
|
using AxCopilot.Themes;
|
|
|
|
namespace AxCopilot.Handlers;
|
|
|
|
/// <summary>
|
|
/// 텍스트 스니펫 핸들러. ";" 프리픽스로 사용합니다.
|
|
/// 예: ;addr → 미리 저장된 주소 텍스트를 클립보드에 복사 후 붙여넣기
|
|
/// ;sig → 서명 텍스트 붙여넣기
|
|
/// </summary>
|
|
public class SnippetHandler : IActionHandler
|
|
{
|
|
private readonly SettingsService _settings;
|
|
|
|
public string? Prefix => ";";
|
|
|
|
public PluginMetadata Metadata => new(
|
|
"Snippets",
|
|
"텍스트 스니펫 — ; 뒤에 키워드 입력",
|
|
"1.0",
|
|
"AX");
|
|
|
|
public SnippetHandler(SettingsService settings)
|
|
{
|
|
_settings = settings;
|
|
}
|
|
|
|
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
|
{
|
|
var snippets = _settings.Settings.Snippets;
|
|
|
|
if (!snippets.Any())
|
|
{
|
|
return Task.FromResult<IEnumerable<LauncherItem>>(
|
|
[
|
|
new LauncherItem(
|
|
"등록된 스니펫이 없습니다",
|
|
"설정 → 스니펫 탭에서 추가하세요",
|
|
null, null, Symbol: Symbols.Snippet)
|
|
]);
|
|
}
|
|
|
|
var q = query.Trim().ToLowerInvariant();
|
|
var results = snippets
|
|
.Where(s => string.IsNullOrEmpty(q) ||
|
|
s.Key.ToLowerInvariant().Contains(q) ||
|
|
s.Name.ToLowerInvariant().Contains(q))
|
|
.Select(s => new LauncherItem(
|
|
s.Name.Length > 0 ? s.Name : s.Key,
|
|
TruncatePreview(s.Content),
|
|
null,
|
|
s,
|
|
Symbol: Symbols.Snippet))
|
|
.ToList();
|
|
|
|
if (results.Count == 0)
|
|
{
|
|
results.Add(new LauncherItem(
|
|
$"'{query}'에 해당하는 스니펫 없음",
|
|
"설정에서 새 스니펫을 추가하세요",
|
|
null, null, Symbol: Symbols.Snippet));
|
|
}
|
|
|
|
return Task.FromResult<IEnumerable<LauncherItem>>(results);
|
|
}
|
|
|
|
public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
|
|
{
|
|
if (item.Data is not SnippetEntry snippet) return Task.CompletedTask;
|
|
|
|
try
|
|
{
|
|
// 변수 치환: {date}, {time}, {datetime}
|
|
var expanded = ExpandVariables(snippet.Content);
|
|
|
|
Clipboard.SetText(expanded);
|
|
|
|
// 100ms 후 Ctrl+V 시뮬레이션
|
|
Task.Delay(100, ct).ContinueWith(_ =>
|
|
{
|
|
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
|
{
|
|
var inputs = new[]
|
|
{
|
|
new INPUT { type = 1, u = new InputUnion { ki = new KEYBDINPUT { wVk = 0x11 } } }, // Ctrl down
|
|
new INPUT { type = 1, u = new InputUnion { ki = new KEYBDINPUT { wVk = 0x56 } } }, // V down
|
|
new INPUT { type = 1, u = new InputUnion { ki = new KEYBDINPUT { wVk = 0x56, dwFlags = 0x0002 } } }, // V up
|
|
new INPUT { type = 1, u = new InputUnion { ki = new KEYBDINPUT { wVk = 0x11, dwFlags = 0x0002 } } }, // Ctrl up
|
|
};
|
|
SendInput((uint)inputs.Length, inputs, System.Runtime.InteropServices.Marshal.SizeOf<INPUT>());
|
|
});
|
|
}, ct, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogService.Warn($"스니펫 실행 실패: {ex.Message}");
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
// ─── 변수 치환 ─────────────────────────────────────────────────────────
|
|
|
|
private static string ExpandVariables(string content)
|
|
{
|
|
var now = DateTime.Now;
|
|
return content
|
|
.Replace("{date}", now.ToString("yyyy-MM-dd"))
|
|
.Replace("{time}", now.ToString("HH:mm:ss"))
|
|
.Replace("{datetime}", now.ToString("yyyy-MM-dd HH:mm:ss"))
|
|
.Replace("{year}", now.Year.ToString())
|
|
.Replace("{month}", now.Month.ToString("D2"))
|
|
.Replace("{day}", now.Day.ToString("D2"));
|
|
}
|
|
|
|
private static string TruncatePreview(string content)
|
|
{
|
|
var oneLine = content.Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ");
|
|
return oneLine.Length > 60 ? oneLine[..57] + "…" : oneLine;
|
|
}
|
|
|
|
// ─── P/Invoke (SendInput) ──────────────────────────────────────────────
|
|
|
|
[System.Runtime.InteropServices.DllImport("user32.dll")]
|
|
private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
|
|
|
|
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
|
private struct INPUT { public uint type; public InputUnion u; }
|
|
|
|
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)]
|
|
private struct InputUnion { [System.Runtime.InteropServices.FieldOffset(0)] public KEYBDINPUT ki; }
|
|
|
|
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
|
private struct KEYBDINPUT { public ushort wVk; public ushort wScan; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; }
|
|
}
|