142 lines
4.8 KiB
C#
142 lines
4.8 KiB
C#
using System.IO;
|
|
using AxCopilot.SDK;
|
|
using AxCopilot.Services;
|
|
using AxCopilot.Themes;
|
|
|
|
namespace AxCopilot.Handlers;
|
|
|
|
/// <summary>
|
|
/// 최근 파일 핸들러. "recent" 프리픽스로 사용합니다.
|
|
/// Windows Recent 폴더(%APPDATA%\Microsoft\Windows\Recent)의 .lnk 파일을
|
|
/// 최근 수정 순으로 나열합니다.
|
|
/// 예: recent → 최근 20개 파일 목록
|
|
/// recent 보고서 → 이름에 "보고서" 포함 파일 필터
|
|
/// </summary>
|
|
public class RecentFilesHandler : IActionHandler
|
|
{
|
|
public string? Prefix => "recent";
|
|
|
|
public PluginMetadata Metadata => new(
|
|
"RecentFiles",
|
|
"최근 파일 — recent 뒤에 검색어 입력",
|
|
"1.0",
|
|
"AX");
|
|
|
|
private static readonly string RecentFolder = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
@"Microsoft\Windows\Recent");
|
|
|
|
// 간단한 캐시: 10초간 유효
|
|
private static (DateTime At, List<(string Name, string LinkPath, DateTime Modified)> Files)? _cache;
|
|
private static readonly TimeSpan CacheTtl = TimeSpan.FromSeconds(10);
|
|
|
|
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
|
{
|
|
var q = query.Trim();
|
|
|
|
if (string.IsNullOrWhiteSpace(q))
|
|
{
|
|
// 힌트만 표시
|
|
}
|
|
|
|
var files = GetRecentFiles();
|
|
IEnumerable<(string Name, string LinkPath, DateTime Modified)> filtered = files;
|
|
|
|
if (!string.IsNullOrWhiteSpace(q))
|
|
{
|
|
filtered = files.Where(f =>
|
|
f.Name.Contains(q, StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
|
|
var items = filtered.Take(20).Select(f => new LauncherItem(
|
|
f.Name,
|
|
$"{f.Modified:yyyy-MM-dd HH:mm} · Enter로 열기",
|
|
null,
|
|
f.LinkPath,
|
|
Symbol: GetSymbol(f.Name))).ToList();
|
|
|
|
if (!items.Any())
|
|
{
|
|
items.Add(new LauncherItem(
|
|
string.IsNullOrWhiteSpace(q) ? "최근 파일 없음" : "검색 결과 없음",
|
|
string.IsNullOrWhiteSpace(q)
|
|
? "Windows Recent 폴더가 비어 있습니다"
|
|
: $"'{q}' 파일을 최근 목록에서 찾을 수 없습니다",
|
|
null, null, Symbol: Symbols.Info));
|
|
}
|
|
|
|
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
|
}
|
|
|
|
public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
|
|
{
|
|
if (item.Data is string linkPath && File.Exists(linkPath))
|
|
{
|
|
try
|
|
{
|
|
System.Diagnostics.Process.Start(
|
|
new System.Diagnostics.ProcessStartInfo(linkPath)
|
|
{ UseShellExecute = true });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogService.Warn($"최근 파일 열기 실패: {ex.Message}");
|
|
}
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
// ─── 내부 ─────────────────────────────────────────────────────────────────
|
|
|
|
private static List<(string Name, string LinkPath, DateTime Modified)> GetRecentFiles()
|
|
{
|
|
// 캐시 유효 확인
|
|
if (_cache.HasValue && (DateTime.Now - _cache.Value.At) < CacheTtl)
|
|
return _cache.Value.Files;
|
|
|
|
var result = new List<(string, string, DateTime)>();
|
|
|
|
try
|
|
{
|
|
if (!Directory.Exists(RecentFolder))
|
|
return result;
|
|
|
|
var lnkFiles = Directory
|
|
.GetFiles(RecentFolder, "*.lnk")
|
|
.Select(p => (Path: p, Info: new FileInfo(p)))
|
|
.OrderByDescending(f => f.Info.LastWriteTime)
|
|
.Take(100)
|
|
.ToList();
|
|
|
|
foreach (var (path, info) in lnkFiles)
|
|
{
|
|
var name = Path.GetFileNameWithoutExtension(info.Name);
|
|
result.Add((name, path, info.LastWriteTime));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogService.Warn($"최근 파일 목록 읽기 실패: {ex.Message}");
|
|
}
|
|
|
|
_cache = (DateTime.Now, result);
|
|
return result;
|
|
}
|
|
|
|
private static string GetSymbol(string name)
|
|
{
|
|
var ext = Path.GetExtension(name).ToLowerInvariant();
|
|
return ext switch
|
|
{
|
|
".exe" or ".msi" => Symbols.App,
|
|
".xlsx" or ".xls" or ".csv" => Symbols.File,
|
|
".docx" or ".doc" => Symbols.File,
|
|
".pptx" or ".ppt" => Symbols.File,
|
|
".pdf" => Symbols.File,
|
|
".txt" or ".md" or ".log" => Symbols.Text,
|
|
".jpg" or ".jpeg" or ".png" or ".gif" or ".webp" or ".bmp" => Symbols.Picture,
|
|
_ => Symbols.File
|
|
};
|
|
}
|
|
}
|