AX Commander 비교본 런처 기능 대량 이식

변경 목적: Agent Compare 아래 비교본의 개발 문서와 런처 소스를 기준으로 현재 AX Commander에 빠져 있던 신규 런처 기능을 동일한 흐름으로 옮겨, 비교본 수준의 기능 폭을 현재 제품에 반영했습니다.

핵심 수정사항: 비교본의 신규 런처 핸들러 다수를 src/AxCopilot/Handlers로 이식하고 App.xaml.cs 등록 흐름에 연결했습니다. 빠른 링크, 파일 태그, 알림 센터, 포모도로, 파일 브라우저, 핫키 관리, OCR, 세션/스케줄/매크로, Git/정규식/네트워크/압축/해시/UUID/JWT/QR 등 AX Commander 기능을 추가했습니다.

핵심 수정사항: 신규 기능이 실제 동작하도록 AppSettings 확장, SchedulerService/FileTagService/NotificationCenterService/IconCacheService/UrlTemplateEngine/PomodoroService 추가, 배치 이름변경/세션/스케줄/매크로 편집 창 추가, NotificationService와 Symbols 보강, QR/OCR용 csproj 의존성과 Windows 타겟 프레임워크를 반영했습니다.

문서 반영: README.md와 docs/DEVELOPMENT.md에 비교본 기반 런처 기능 이식 이력과 검증 결과를 업데이트했습니다.

검증 결과: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ 실행 기준 경고 0개, 오류 0개를 확인했습니다.
This commit is contained in:
2026-04-05 00:59:45 +09:00
parent 0929778ca7
commit 0336904258
115 changed files with 30749 additions and 1 deletions

View File

@@ -0,0 +1,86 @@
using System.Collections.Concurrent;
namespace AxCopilot.Services;
/// <summary>알림 타입.</summary>
public enum NotificationType { Info, Success, Warning, Error }
/// <summary>알림 항목.</summary>
public record NotificationEntry(
string Title,
string Message,
NotificationType Type,
DateTime Timestamp);
/// <summary>
/// Phase L3-7: 알림 센터 — 앱 전체에서 알림을 발행하고 이력을 관리합니다.
/// 트레이 BalloonTip + 인앱 알림 큐를 결합합니다.
/// </summary>
public static class NotificationCenterService
{
private const int MaxHistory = 50;
private static readonly ConcurrentQueue<NotificationEntry> _history = new();
/// <summary>새 알림 발행 이벤트. UI 계층에서 구독하여 인앱 알림 표시에 활용합니다.</summary>
public static event EventHandler<NotificationEntry>? NotificationRaised;
/// <summary>최근 알림 이력 (최대 50건, 최신 순).</summary>
public static IReadOnlyList<NotificationEntry> History
{
get
{
var list = _history.ToArray();
Array.Reverse(list);
return list;
}
}
/// <summary>알림을 발행합니다. 트레이 BalloonTip + 이벤트를 동시에 발화합니다.</summary>
public static void Show(string title, string message, NotificationType type = NotificationType.Info)
{
var entry = Record(title, message, type);
NotificationService.NotifyBalloonOnly(title, message);
TryRaise(entry);
}
/// <summary>성공 알림.</summary>
public static void ShowSuccess(string title, string message)
=> Show(title, message, NotificationType.Success);
/// <summary>경고 알림.</summary>
public static void ShowWarning(string title, string message)
=> Show(title, message, NotificationType.Warning);
/// <summary>오류 알림.</summary>
public static void ShowError(string title, string message)
=> Show(title, message, NotificationType.Error);
/// <summary>에이전트 작업 완료 알림 (BackgroundAgentService 전용 편의 메서드).</summary>
public static void NotifyAgentCompleted(string agentType, string? result)
{
var preview = result?.Length > 80 ? result[..77] + "…" : result ?? "완료";
Show($"{agentType} 에이전트 완료", preview, NotificationType.Success);
}
/// <summary>이력을 초기화합니다.</summary>
public static void ClearHistory()
{
while (_history.TryDequeue(out _)) { }
}
internal static NotificationEntry Record(string title, string message, NotificationType type)
{
var entry = new NotificationEntry(title, message, type, DateTime.Now);
_history.Enqueue(entry);
while (_history.Count > MaxHistory && _history.TryDequeue(out _)) { }
TryRaise(entry);
return entry;
}
private static void TryRaise(NotificationEntry entry)
{
try { NotificationRaised?.Invoke(null, entry); }
catch (Exception) { }
}
}