using AxCopilot.Models; using AxCopilot.SDK; using AxCopilot.Services; using AxCopilot.Views; namespace AxCopilot.Handlers; /// /// "!" 프리픽스 핸들러. AX Agent (AI 어시스턴트) 기능. /// ★ DEPLOY_STUB = true → 배포용 "개발 중" 표시 /// ★ DEPLOY_STUB = false → 실제 ChatWindow 동작 /// public class ChatHandler : IActionHandler { // ┌──────────────────────────────────────────────────────────────┐ // │ 배포 시 true, 개발 활성화 시 false 로 전환 │ // └──────────────────────────────────────────────────────────────┘ private const bool DEPLOY_STUB = false; private readonly SettingsService _settings; private readonly object _windowLock = new(); private ChatWindow? _chatWindow; public string? Prefix => "!"; public PluginMetadata Metadata => new("ax.agent", "AX Agent", "1.0", "AX Agent — AI 어시스턴트"); public ChatHandler(SettingsService settings) { _settings = settings; } public Task> GetItemsAsync(string query, CancellationToken ct) { // ── 배포용 스텁 ───────────────────────────────────────────── #pragma warning disable CS0162 // DEPLOY_STUB 플래그에 의한 의도된 비활성 코드 if (DEPLOY_STUB) { var stub = new List { new LauncherItem( "AX Agent (개발 중)", "이 기능은 다음 버전에서 제공될 예정입니다. 기대해 주세요!", null, null, Symbol: "\uE8BD") }; return Task.FromResult>(stub); } #pragma warning restore CS0162 // ── AI 비활성화 체크 ───────────────────────────────────────── var appSettings = (System.Windows.Application.Current as App)?.SettingsService?.Settings; if (appSettings?.AiEnabled == false) return Task.FromResult>(Array.Empty()); // ── 실제 구현 ─────────────────────────────────────────────── var items = new List(); var q = query.Trim(); if (string.IsNullOrEmpty(q)) { items.Add(new LauncherItem( "AX Agent 대화하기", "AI 비서와 대화를 시작합니다", null, "open_chat", Symbol: "\uE8BD")); try { var storage = ServiceLocator.Get(); var metas = storage.LoadAllMeta(); foreach (var conv in metas.Take(5)) { var ago = FormatTimeAgo(conv.UpdatedAt); var symbol = ChatCategory.GetSymbol(conv.Category); items.Add(new LauncherItem( conv.Title, $"{ago} · 메시지 {conv.Messages.Count}개", null, $"resume:{conv.Id}", Symbol: symbol)); } if (metas.Any()) items.Add(new LauncherItem( "새 대화 시작", "이전 대화와 별개의 새 대화를 시작합니다", null, "new_chat", Symbol: "\uE710")); } catch { } } else { items.Add(new LauncherItem( $"AI에게 물어보기: {(q.Length > 40 ? q[..40] + "…" : q)}", "Enter를 누르면 AX Agent이 열리고 질문이 전송됩니다", null, $"ask:{q}", Symbol: "\uE8BD")); items.Add(new LauncherItem( "AX Agent 대화하기", "질문 없이 Agent 창만 엽니다", null, "open_chat", Symbol: "\uE8BD")); } return Task.FromResult>(items); } public Task ExecuteAsync(LauncherItem item, CancellationToken ct) { #pragma warning disable CS0162 // DEPLOY_STUB 플래그에 의한 의도된 비활성 코드 if (DEPLOY_STUB) return Task.CompletedTask; #pragma warning restore CS0162 // AI 비활성화 시 실행 차단 var appSettings2 = (System.Windows.Application.Current as App)?.SettingsService?.Settings; if (appSettings2?.AiEnabled == false) return Task.CompletedTask; var data = item.Data as string ?? "open_chat"; System.Windows.Application.Current.Dispatcher.Invoke(() => { EnsureChatWindow(); if (data.StartsWith("ask:")) { var question = data[4..]; _chatWindow!.Show(); _chatWindow.Activate(); _chatWindow.SendInitialMessage(question); } else if (data.StartsWith("resume:")) { var convId = data[7..]; _chatWindow!.Show(); _chatWindow.Activate(); _chatWindow.ResumeConversation(convId); } else if (data == "new_chat") { _chatWindow!.Show(); _chatWindow.Activate(); _chatWindow.StartNewAndFocus(); } else { _chatWindow!.Show(); _chatWindow.Activate(); } }); return Task.CompletedTask; } private void EnsureChatWindow() { lock (_windowLock) { if (_chatWindow == null || !_chatWindow.IsLoaded) { _chatWindow = new ChatWindow(_settings); _chatWindow.Closed += (_, _) => { lock (_windowLock) _chatWindow = null; }; } } } private static string FormatTimeAgo(DateTime dt) { var diff = DateTime.Now - dt; if (diff.TotalMinutes < 1) return "방금 전"; if (diff.TotalHours < 1) return $"{(int)diff.TotalMinutes}분 전"; if (diff.TotalDays < 1) return $"{(int)diff.TotalHours}시간 전"; if (diff.TotalDays < 7) return $"{(int)diff.TotalDays}일 전"; return dt.ToString("yyyy-MM-dd"); } }