런처 Agent Compare 기능 1차 이식 및 현재 런처 구조 연결
- Agent Compare 기준으로 런처 빠른 실행 칩, 검색 히스토리 탐색, 선택 항목 미리보기 패널을 현재 런처에 이식 - 하단 위젯 바, QuickLook(F3), 화면 OCR(F4), 관련 서비스/partial 파일을 현재 LauncherWindow/LauncherViewModel 구조에 연결 - UsageRankingService 상위 항목 조회와 SearchHistoryService를 추가해 실행 상위 경로/검색 기록이 실제 런처 동작에 반영되도록 정리 - README.md, docs/DEVELOPMENT.md에 이식 범위와 검증 결과를 2026-04-05 11:58 (KST) 기준으로 기록 검증 결과 - 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:
209
src/AxCopilot/Views/LauncherWindow.Widgets.cs
Normal file
209
src/AxCopilot/Views/LauncherWindow.Widgets.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using AxCopilot.Services;
|
||||
|
||||
namespace AxCopilot.Views;
|
||||
|
||||
public partial class LauncherWindow
|
||||
{
|
||||
private DispatcherTimer? _widgetTimer;
|
||||
private static readonly SolidColorBrush DotOnline = new(Color.FromRgb(0x10, 0xB9, 0x81));
|
||||
private static readonly SolidColorBrush DotOffline = new(Color.FromRgb(0x9E, 0x9E, 0x9E));
|
||||
private int _widgetBatteryTick;
|
||||
private int _widgetWeatherTick;
|
||||
|
||||
internal void StartWidgetUpdates()
|
||||
{
|
||||
var settings = CurrentApp?.SettingsService?.Settings;
|
||||
|
||||
PerformanceMonitorService.Instance.StartPolling();
|
||||
ServerStatusService.Instance.Start(settings);
|
||||
PomodoroService.Instance.StateChanged -= OnPomoStateChanged;
|
||||
PomodoroService.Instance.StateChanged += OnPomoStateChanged;
|
||||
ServerStatusService.Instance.StatusChanged -= OnServerStatusChanged;
|
||||
ServerStatusService.Instance.StatusChanged += OnServerStatusChanged;
|
||||
|
||||
_vm.UpdateWidgets();
|
||||
UpdateServerDots();
|
||||
UpdateBatteryWidget();
|
||||
_ = RefreshWeatherAsync();
|
||||
|
||||
if (_widgetTimer == null)
|
||||
{
|
||||
_widgetTimer = new DispatcherTimer(DispatcherPriority.Background)
|
||||
{
|
||||
Interval = TimeSpan.FromSeconds(1)
|
||||
};
|
||||
_widgetTimer.Tick += (_, _) =>
|
||||
{
|
||||
_vm.UpdateWidgets();
|
||||
UpdateServerDots();
|
||||
if (_vm.Widget_PerfText.Length > 0 && _widgetBatteryTick++ % 30 == 0)
|
||||
UpdateBatteryWidget();
|
||||
if (_widgetWeatherTick++ % 120 == 0)
|
||||
_ = RefreshWeatherAsync();
|
||||
};
|
||||
}
|
||||
|
||||
_widgetTimer.Start();
|
||||
UpdatePomoWidgetStyle();
|
||||
}
|
||||
|
||||
internal void StopWidgetUpdates()
|
||||
{
|
||||
_widgetTimer?.Stop();
|
||||
PerformanceMonitorService.Instance.StopPolling();
|
||||
PomodoroService.Instance.StateChanged -= OnPomoStateChanged;
|
||||
ServerStatusService.Instance.StatusChanged -= OnServerStatusChanged;
|
||||
}
|
||||
|
||||
private void OnPomoStateChanged(object? sender, EventArgs e)
|
||||
{
|
||||
Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
_vm.UpdateWidgets();
|
||||
UpdatePomoWidgetStyle();
|
||||
});
|
||||
}
|
||||
|
||||
private void OnServerStatusChanged(object? sender, EventArgs e)
|
||||
=> Dispatcher.InvokeAsync(UpdateServerDots);
|
||||
|
||||
private void UpdateServerDots()
|
||||
{
|
||||
var server = ServerStatusService.Instance;
|
||||
if (OllamaStatusDot != null)
|
||||
OllamaStatusDot.Fill = server.OllamaOnline ? DotOnline : DotOffline;
|
||||
if (LlmStatusDot != null)
|
||||
LlmStatusDot.Fill = server.LlmOnline ? DotOnline : DotOffline;
|
||||
if (McpStatusDot != null)
|
||||
McpStatusDot.Fill = server.McpOnline ? DotOnline : DotOffline;
|
||||
}
|
||||
|
||||
private void UpdatePomoWidgetStyle()
|
||||
{
|
||||
if (WgtPomo == null)
|
||||
return;
|
||||
|
||||
var running = PomodoroService.Instance.IsRunning;
|
||||
WgtPomo.Background = running
|
||||
? new SolidColorBrush(Color.FromArgb(0x1E, 0xF5, 0x9E, 0x0B))
|
||||
: new SolidColorBrush(Color.FromArgb(0x0D, 0xF5, 0x9E, 0x0B));
|
||||
}
|
||||
|
||||
private void WgtPerf_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
_vm.InputText = "info ";
|
||||
InputBox?.Focus();
|
||||
}
|
||||
|
||||
private void WgtPomo_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
_vm.InputText = "pomo ";
|
||||
InputBox?.Focus();
|
||||
}
|
||||
|
||||
private void WgtNote_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
_vm.InputText = "note ";
|
||||
InputBox?.Focus();
|
||||
}
|
||||
|
||||
private void WgtServer_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
var settings = CurrentApp?.SettingsService?.Settings;
|
||||
ServerStatusService.Instance.Refresh(settings);
|
||||
_vm.InputText = "port";
|
||||
InputBox?.Focus();
|
||||
}
|
||||
|
||||
private void WgtWeather_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
var internalMode = CurrentApp?.SettingsService?.Settings.InternalModeEnabled ?? true;
|
||||
if (!internalMode)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo("https://wttr.in") { UseShellExecute = true });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogService.Warn($"날씨 페이지 열기 실패: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WeatherWidgetService.Invalidate();
|
||||
_ = RefreshWeatherAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void WgtCal_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo("ms-clock:") { UseShellExecute = true });
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo("outlookcal:") { UseShellExecute = true });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogService.Warn($"달력 열기 실패: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WgtBattery_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo("ms-settings:powersleep") { UseShellExecute = true });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogService.Warn($"전원 설정 열기 실패: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBatteryWidget()
|
||||
{
|
||||
try
|
||||
{
|
||||
var power = System.Windows.Forms.SystemInformation.PowerStatus;
|
||||
var pct = power.BatteryLifePercent;
|
||||
if (pct > 1.0f || pct < 0f)
|
||||
{
|
||||
_vm.Widget_BatteryVisible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_vm.Widget_BatteryVisible = true;
|
||||
var pctInt = (int)(pct * 100);
|
||||
var charging = power.PowerLineStatus == System.Windows.Forms.PowerLineStatus.Online;
|
||||
_vm.Widget_BatteryText = charging ? $"{pctInt}% 충전" : $"{pctInt}%";
|
||||
_vm.Widget_BatteryIcon = charging ? "\uE83E"
|
||||
: pctInt >= 85 ? "\uEBA7"
|
||||
: pctInt >= 70 ? "\uEBA5"
|
||||
: pctInt >= 50 ? "\uEBA3"
|
||||
: pctInt >= 25 ? "\uEBA1"
|
||||
: "\uEBA0";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogService.Warn($"배터리 위젯 갱신 실패: {ex.Message}");
|
||||
_vm.Widget_BatteryVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshWeatherAsync()
|
||||
{
|
||||
var internalMode = CurrentApp?.SettingsService?.Settings.InternalModeEnabled ?? true;
|
||||
await WeatherWidgetService.RefreshAsync(internalMode);
|
||||
await Dispatcher.InvokeAsync(() => { _vm.Widget_WeatherText = WeatherWidgetService.CachedText; });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user