런처 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:
@@ -14,6 +14,8 @@ namespace AxCopilot.Views;
|
||||
|
||||
public partial class LauncherWindow : Window
|
||||
{
|
||||
private static App? CurrentApp => System.Windows.Application.Current as App;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
@@ -34,6 +36,7 @@ public partial class LauncherWindow : Window
|
||||
|
||||
private readonly LauncherViewModel _vm;
|
||||
private System.Windows.Threading.DispatcherTimer? _indexStatusTimer;
|
||||
private System.Windows.Threading.DispatcherTimer? _toastTimer;
|
||||
|
||||
/// <summary>Ctrl+, 단축키로 설정 창을 여는 콜백 (App.xaml.cs에서 주입)</summary>
|
||||
public Action? OpenSettingsAction { get; set; }
|
||||
@@ -71,16 +74,9 @@ public partial class LauncherWindow : Window
|
||||
Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
var svc = app.IndexService;
|
||||
IndexStatusText.Text = $"✓ {svc.LastIndexCount:N0}개 항목 색인됨 ({svc.LastIndexDuration.TotalSeconds:F1}초)";
|
||||
IndexStatusText.Visibility = Visibility.Visible;
|
||||
// 기존 타이머 정리 후 5초 후 자동 숨기기
|
||||
_indexStatusTimer?.Stop();
|
||||
_indexStatusTimer = new System.Windows.Threading.DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromSeconds(5)
|
||||
};
|
||||
_indexStatusTimer.Tick += (_, _) => { IndexStatusText.Visibility = Visibility.Collapsed; _indexStatusTimer.Stop(); };
|
||||
_indexStatusTimer.Start();
|
||||
ShowIndexStatus(
|
||||
$"✓ {svc.LastIndexCount:N0}개 항목 색인됨 ({svc.LastIndexDuration.TotalSeconds:F1}초)",
|
||||
TimeSpan.FromSeconds(5));
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -88,7 +84,7 @@ public partial class LauncherWindow : Window
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CenterOnScreen();
|
||||
ApplyInitialPlacement();
|
||||
ApplyTheme();
|
||||
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input, () =>
|
||||
{
|
||||
@@ -151,7 +147,7 @@ public partial class LauncherWindow : Window
|
||||
_vm.OnShown();
|
||||
_vm.InputText = "";
|
||||
base.Show();
|
||||
CenterOnScreen();
|
||||
ApplyInitialPlacement();
|
||||
AnimateIn();
|
||||
|
||||
// 포그라운드 강제 + 포커스를 3단계로 보장
|
||||
@@ -695,6 +691,64 @@ public partial class LauncherWindow : Window
|
||||
};
|
||||
}
|
||||
|
||||
private void ApplyInitialPlacement()
|
||||
{
|
||||
if (!TryRestoreRememberedPosition())
|
||||
CenterOnScreen();
|
||||
|
||||
UpdateRememberedPositionCache();
|
||||
}
|
||||
|
||||
private bool TryRestoreRememberedPosition()
|
||||
{
|
||||
var launcher = CurrentApp?.SettingsService?.Settings?.Launcher;
|
||||
if (launcher == null || !launcher.RememberPosition) return false;
|
||||
if (launcher.LastLeft < 0 || launcher.LastTop < 0) return false;
|
||||
|
||||
var rememberPoint = new Point(launcher.LastLeft, launcher.LastTop);
|
||||
if (!IsVisibleOnAnyScreen(rememberPoint)) return false;
|
||||
|
||||
Left = launcher.LastLeft;
|
||||
Top = launcher.LastTop;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsVisibleOnAnyScreen(Point point)
|
||||
{
|
||||
foreach (var screen in FormsScreen.AllScreens)
|
||||
{
|
||||
var bounds = screen.WorkingArea;
|
||||
if (point.X >= bounds.Left && point.X <= bounds.Right - 40 &&
|
||||
point.Y >= bounds.Top && point.Y <= bounds.Bottom - 40)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UpdateRememberedPositionCache()
|
||||
{
|
||||
var launcher = CurrentApp?.SettingsService?.Settings?.Launcher;
|
||||
if (launcher == null || !launcher.RememberPosition || !IsLoaded) return;
|
||||
|
||||
launcher.LastLeft = Left;
|
||||
launcher.LastTop = Top;
|
||||
}
|
||||
|
||||
private void SaveRememberedPosition()
|
||||
{
|
||||
var app = CurrentApp;
|
||||
var settingsService = app?.SettingsService;
|
||||
if (settingsService == null) return;
|
||||
var launcher = settingsService.Settings.Launcher;
|
||||
if (launcher == null || !launcher.RememberPosition || !IsLoaded) return;
|
||||
|
||||
UpdateRememberedPositionCache();
|
||||
settingsService.Save();
|
||||
}
|
||||
|
||||
// 지원 테마 이름 목록
|
||||
private static readonly HashSet<string> KnownThemes =
|
||||
new(StringComparer.OrdinalIgnoreCase)
|
||||
@@ -1031,8 +1085,7 @@ public partial class LauncherWindow : Window
|
||||
{
|
||||
var app = (App)System.Windows.Application.Current;
|
||||
_ = app.IndexService?.BuildAsync(CancellationToken.None);
|
||||
IndexStatusText.Text = "⟳ 인덱스 재구축 중…";
|
||||
IndexStatusText.Visibility = Visibility.Visible;
|
||||
ShowIndexStatus("⟳ 인덱스 재구축 중…", TimeSpan.FromSeconds(8));
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
@@ -1256,6 +1309,36 @@ public partial class LauncherWindow : Window
|
||||
return;
|
||||
}
|
||||
|
||||
// ─── F3 → 파일 빠른 미리보기 (QuickLook 토글) ───────────────────────
|
||||
if (e.Key == Key.F3)
|
||||
{
|
||||
ToggleQuickLook();
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// ─── F4 → 화면 영역 OCR 즉시 실행 ─────────────────────────────────
|
||||
if (e.Key == Key.F4)
|
||||
{
|
||||
Hide();
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var handler = new Handlers.OcrHandler();
|
||||
var item = new SDK.LauncherItem(
|
||||
"화면 영역 텍스트 추출", "", null, "__ocr_region__");
|
||||
await handler.ExecuteAsync(item, CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Services.LogService.Error($"F4 OCR 실행 오류: {ex.Message}");
|
||||
}
|
||||
});
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// ─── Ctrl+1~9 → n번째 결과 즉시 실행 ───────────────────────────────
|
||||
if (mod == ModifierKeys.Control)
|
||||
{
|
||||
@@ -1295,6 +1378,8 @@ public partial class LauncherWindow : Window
|
||||
"[ 기능 ]",
|
||||
"F1 도움말",
|
||||
"F2 파일 이름 바꾸기",
|
||||
"F3 파일 빠른 미리보기",
|
||||
"F4 화면 OCR",
|
||||
"F5 인덱스 새로 고침",
|
||||
"Delete 항목 제거",
|
||||
"Ctrl+, 설정",
|
||||
@@ -1331,14 +1416,14 @@ public partial class LauncherWindow : Window
|
||||
var fadeIn = (System.Windows.Media.Animation.Storyboard)FindResource("ToastFadeIn");
|
||||
fadeIn.Begin(this);
|
||||
|
||||
_indexStatusTimer?.Stop();
|
||||
_indexStatusTimer = new System.Windows.Threading.DispatcherTimer
|
||||
_toastTimer?.Stop();
|
||||
_toastTimer = new System.Windows.Threading.DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromSeconds(2)
|
||||
};
|
||||
_indexStatusTimer.Tick += (_, _) =>
|
||||
_toastTimer.Tick += (_, _) =>
|
||||
{
|
||||
_indexStatusTimer.Stop();
|
||||
_toastTimer.Stop();
|
||||
// 페이드아웃 후 Collapsed
|
||||
var fadeOut = (System.Windows.Media.Animation.Storyboard)FindResource("ToastFadeOut");
|
||||
EventHandler? onCompleted = null;
|
||||
@@ -1350,6 +1435,24 @@ public partial class LauncherWindow : Window
|
||||
fadeOut.Completed += onCompleted;
|
||||
fadeOut.Begin(this);
|
||||
};
|
||||
_toastTimer.Start();
|
||||
}
|
||||
|
||||
private void ShowIndexStatus(string message, TimeSpan duration)
|
||||
{
|
||||
IndexStatusText.Text = message;
|
||||
IndexStatusText.Visibility = Visibility.Visible;
|
||||
|
||||
_indexStatusTimer?.Stop();
|
||||
_indexStatusTimer = new System.Windows.Threading.DispatcherTimer
|
||||
{
|
||||
Interval = duration
|
||||
};
|
||||
_indexStatusTimer.Tick += (_, _) =>
|
||||
{
|
||||
_indexStatusTimer.Stop();
|
||||
IndexStatusText.Visibility = Visibility.Collapsed;
|
||||
};
|
||||
_indexStatusTimer.Start();
|
||||
}
|
||||
|
||||
@@ -1560,6 +1663,28 @@ public partial class LauncherWindow : Window
|
||||
if (_vm.CloseOnFocusLost) Hide();
|
||||
}
|
||||
|
||||
private void Window_LocationChanged(object sender, EventArgs e)
|
||||
{
|
||||
UpdateRememberedPositionCache();
|
||||
}
|
||||
|
||||
private void Window_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue is not bool isVisible)
|
||||
return;
|
||||
|
||||
if (isVisible)
|
||||
{
|
||||
StartWidgetUpdates();
|
||||
return;
|
||||
}
|
||||
|
||||
_quickLookWindow?.Close();
|
||||
_quickLookWindow = null;
|
||||
StopWidgetUpdates();
|
||||
SaveRememberedPosition();
|
||||
}
|
||||
|
||||
private void ScrollToSelected()
|
||||
{
|
||||
if (_vm.SelectedItem != null)
|
||||
|
||||
Reference in New Issue
Block a user