diff --git a/docs/LAUNCHER_ROADMAP.md b/docs/LAUNCHER_ROADMAP.md
index a41ccd7..2759a61 100644
--- a/docs/LAUNCHER_ROADMAP.md
+++ b/docs/LAUNCHER_ROADMAP.md
@@ -102,7 +102,7 @@
| ✅ L3-5 | **파일 태그 시스템** | 파일에 사용자 태그 부여, `tag` 프리픽스로 태그 기반 검색. `file_tags.json` 로컬 저장 | 중간 | — |
| L3-6 | **오프라인 AI (로컬 SLM)** | ONNX Runtime + phi-3, 서버 없이 번역/요약 | 낮음 | → Agent 18-5 |
| ✅ L3-7 | **다중 디스플레이** | 마우스 커서 위치 모니터에 런처 표시, 독 바 per-monitor 위치 저장·유효성 검증 | 낮음 | — |
-| L3-8 | **알림 센터 통합** | Windows 알림과 연동 | 낮음 | — |
+| ✅ L3-8 | **알림 센터 통합** | `notif` 프리픽스로 알림 이력 조회·검색·초기화. NotificationCenterService 이력 연동, 클립보드 복사 | 낮음 | — |
| L3-9 | **런처 미니 위젯** | 날씨/일정/할일을 런처 하단에 카드형으로 표시. 로컬 데이터 기반 | 낮음 | — |
---
diff --git a/src/AxCopilot/App.xaml.cs b/src/AxCopilot/App.xaml.cs
index b23618d..51cc911 100644
--- a/src/AxCopilot/App.xaml.cs
+++ b/src/AxCopilot/App.xaml.cs
@@ -168,6 +168,8 @@ public partial class App : System.Windows.Application
commandResolver.RegisterHandler(new WebSearchSummaryHandler(settings, _sharedLlm));
// Phase L3-5: 파일 태그 시스템
commandResolver.RegisterHandler(new TagHandler());
+ // Phase L3-8: 알림 센터
+ commandResolver.RegisterHandler(new NotifHandler());
// ─── 플러그인 로드 ────────────────────────────────────────────────────
var pluginHost = new PluginHost(settings, commandResolver);
diff --git a/src/AxCopilot/Handlers/NotifHandler.cs b/src/AxCopilot/Handlers/NotifHandler.cs
new file mode 100644
index 0000000..ece8852
--- /dev/null
+++ b/src/AxCopilot/Handlers/NotifHandler.cs
@@ -0,0 +1,139 @@
+using System.Windows;
+using AxCopilot.SDK;
+using AxCopilot.Services;
+using AxCopilot.Themes;
+
+namespace AxCopilot.Handlers;
+
+///
+/// Phase L3-8: 알림 센터 핸들러. "notif" 프리픽스로 사용합니다.
+/// AX Copilot이 표시한 최근 알림 이력을 런처에서 조회합니다.
+///
+/// 사용법:
+/// notif → 최근 알림 이력 목록
+/// notif clear → 알림 이력 초기화
+/// notif [검색어] → 제목·내용으로 필터링
+///
+/// Enter → 알림 내용을 클립보드에 복사.
+///
+public class NotifHandler : IActionHandler
+{
+ public string? Prefix => "notif";
+
+ public PluginMetadata Metadata => new(
+ "NotifCenter",
+ "알림 센터 — notif",
+ "1.0",
+ "AX",
+ "AX Copilot 알림 이력을 조회합니다.");
+
+ public Task> GetItemsAsync(string query, CancellationToken ct)
+ {
+ var q = query.Trim();
+
+ // clear 명령
+ if (q.Equals("clear", StringComparison.OrdinalIgnoreCase))
+ {
+ var count = NotificationCenterService.History.Count;
+ return Task.FromResult>(
+ [
+ new LauncherItem(
+ $"알림 이력 초기화 ({count}건)",
+ "Enter를 눌러 전체 삭제",
+ null, "__CLEAR__",
+ Symbol: Symbols.Delete)
+ ]);
+ }
+
+ var history = NotificationCenterService.History;
+
+ // 검색어 필터
+ IEnumerable filtered = history;
+ if (!string.IsNullOrEmpty(q))
+ {
+ filtered = history
+ .Where(e => e.Title.Contains(q, StringComparison.OrdinalIgnoreCase)
+ || e.Message.Contains(q, StringComparison.OrdinalIgnoreCase));
+ }
+
+ var list = filtered.Take(12).ToList();
+
+ if (list.Count == 0)
+ {
+ var emptyMsg = string.IsNullOrEmpty(q)
+ ? "알림 이력이 없습니다"
+ : $"'{q}'에 해당하는 알림 없음";
+ return Task.FromResult>(
+ [
+ new LauncherItem(
+ emptyMsg,
+ "AX Copilot 동작 중 발생한 알림이 여기에 표시됩니다",
+ null, null,
+ Symbol: Symbols.Info)
+ ]);
+ }
+
+ var items = list
+ .Select(e => new LauncherItem(
+ e.Title,
+ $"{e.Message} · {TimeAgo(e.Timestamp)}",
+ null, e,
+ Symbol: GetSymbol(e)))
+ .ToList();
+
+ return Task.FromResult>(items);
+ }
+
+ public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
+ {
+ // 이력 초기화
+ if (item.Data is string s && s == "__CLEAR__")
+ {
+ NotificationCenterService.ClearHistory();
+ NotificationService.LogOnly("AX Copilot", "알림 이력이 초기화되었습니다.");
+ return Task.CompletedTask;
+ }
+
+ // 알림 내용 클립보드 복사
+ if (item.Data is NotificationEntry entry)
+ {
+ try
+ {
+ Application.Current?.Dispatcher.Invoke(() =>
+ Clipboard.SetText($"[{entry.Title}] {entry.Message}"));
+ }
+ catch (Exception ex)
+ {
+ LogService.Warn($"[NotifHandler] 클립보드 복사 실패: {ex.Message}");
+ }
+ }
+
+ return Task.CompletedTask;
+ }
+
+ // ─── 내부 헬퍼 ──────────────────────────────────────────────────────────
+
+ private static string TimeAgo(DateTime timestamp)
+ {
+ var diff = DateTime.Now - timestamp;
+ if (diff.TotalSeconds < 60) return "방금 전";
+ if (diff.TotalMinutes < 60) return $"{(int)diff.TotalMinutes}분 전";
+ if (diff.TotalHours < 24) return $"{(int)diff.TotalHours}시간 전";
+ return $"{(int)diff.TotalDays}일 전";
+ }
+
+ private static string GetSymbol(NotificationEntry entry) => entry.Type switch
+ {
+ NotificationType.Error => Symbols.Error,
+ NotificationType.Warning => Symbols.Warning,
+ NotificationType.Success => Symbols.Favorite,
+ _ => entry.Title switch
+ {
+ var t when t.Contains("태그") => Symbols.Tag,
+ var t when t.Contains("즐겨찾기") => Symbols.Favorite,
+ var t when t.Contains("저장")
+ || t.Contains("내보내기") => Symbols.Save,
+ _ => Symbols.ReminderBell,
+ }
+ };
+}
diff --git a/src/AxCopilot/Services/NotificationService.cs b/src/AxCopilot/Services/NotificationService.cs
index a8592cd..da8005e 100644
--- a/src/AxCopilot/Services/NotificationService.cs
+++ b/src/AxCopilot/Services/NotificationService.cs
@@ -1,7 +1,10 @@
+using System.Collections.Concurrent;
+
namespace AxCopilot.Services;
///
/// 트레이 아이콘 풍선 알림을 핸들러/서비스에서 표시하기 위한 전달 서비스.
+/// Phase L3-8: 알림 이력은 NotificationCenterService가 관리합니다.
/// App.xaml.cs의 SetupTrayIcon 이후 Initialize()로 초기화됩니다.
///
internal static class NotificationService
@@ -12,7 +15,11 @@ internal static class NotificationService
public static void Initialize(Action showBalloon)
=> _showBalloon = showBalloon;
- /// 트레이 풍선 알림을 표시합니다.
+ /// 트레이 풍선 알림을 표시합니다 (이력 기록은 NotificationCenterService.Show 담당).
public static void Notify(string title, string message)
=> _showBalloon?.Invoke(title, message);
+
+ /// 트레이 알림 없이 알림 이력에만 추가합니다.
+ public static void LogOnly(string title, string message)
+ => NotificationCenterService.Show(title, message, NotificationType.Info);
}
diff --git a/src/AxCopilot/ViewModels/LauncherViewModel.cs b/src/AxCopilot/ViewModels/LauncherViewModel.cs
index 05d0021..a4126b8 100644
--- a/src/AxCopilot/ViewModels/LauncherViewModel.cs
+++ b/src/AxCopilot/ViewModels/LauncherViewModel.cs
@@ -149,6 +149,8 @@ public partial class LauncherViewModel : INotifyPropertyChanged
{ "help", ("도움말", Symbols.Info, "#6B7280") },
// ─── Phase L3-5 파일 태그 ──────────────────────────────────────────────
{ "tag", ("태그", Symbols.Tag, "#6366F1") },
+ // ─── Phase L3-8 알림 센터 ─────────────────────────────────────────────
+ { "notif", ("알림", Symbols.ReminderBell, "#F59E0B") },
};
// ─── 설정 기능 토글 (런처 실동작 연결) ──────────────────────────────────