using System; using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using Microsoft.Web.WebView2.Core; namespace AxCopilot.Views; public partial class GuideViewerWindow : Window { private string? _pendingHtml; private Uri? _pendingUri; public GuideViewerWindow() { InitializeComponent(); Loaded += OnLoaded; KeyDown += (_, e) => { if (e.Key == Key.Escape) Close(); }; StateChanged += (_, _) => { MaxBtnIcon.Text = WindowState == WindowState.Maximized ? "\uE923" : "\uE922"; }; } private async void OnLoaded(object sender, RoutedEventArgs e) { var app = Application.Current as App; var devMode = app?.SettingsService?.Settings?.Llm?.DevMode ?? false; PrepareGuide(devMode); try { // WebView2 초기화 var env = await CoreWebView2Environment.CreateAsync( userDataFolder: Path.Combine(Path.GetTempPath(), "AxCopilot_GuideViewer")); await GuideBrowser.EnsureCoreWebView2Async(env); // 초기화 완료 후 대기 중인 콘텐츠 로드 if (_pendingHtml != null) GuideBrowser.NavigateToString(_pendingHtml); else if (_pendingUri != null) GuideBrowser.Source = _pendingUri; } catch (Exception ex) { // WebView2 런타임 미설치 시 오류 메시지 표시 System.Diagnostics.Debug.WriteLine($"WebView2 init error: {ex.Message}"); } } /// 개발자 모드 여부에 따라 적절한 가이드를 준비합니다. public void PrepareGuide(bool devMode) { TitleText.Text = devMode ? "AX Copilot 개발자 가이드" : "AX Copilot 사용 가이드"; Title = devMode ? "AX Copilot — 개발자 가이드" : "AX Copilot — 사용 가이드"; try { var exeDir = Path.GetDirectoryName(Environment.ProcessPath) ?? AppContext.BaseDirectory; var assetsDir = Path.Combine(exeDir, "Assets"); // 1차: 암호화된 가이드 파일 (.enc) 시도 var encFile = devMode ? Path.Combine(assetsDir, "guide_dev.enc") : Path.Combine(assetsDir, "guide_user.enc"); if (File.Exists(encFile)) { _pendingHtml = Services.GuideEncryptor.DecryptToString(encFile); return; } // 2차: 평문 가이드 파일 (.htm) 폴백 (개발 환경) var htmFile = devMode ? Path.Combine(assetsDir, "AX Copilot 개발자가이드.htm") : Path.Combine(assetsDir, "AX Copilot 사용가이드.htm"); if (File.Exists(htmFile)) { _pendingUri = new Uri(htmFile); return; } _pendingHtml = "" + "

가이드를 찾을 수 없습니다

" + $"

경로: {encFile}

"; } catch (Exception ex) { _pendingHtml = "" + $"

가이드 로드 오류

{ex.Message}

"; } } /// 외부에서 직접 가이드를 로드합니다 (WebView2 초기화 후 호출). public void LoadGuide(bool devMode) { PrepareGuide(devMode); if (GuideBrowser.CoreWebView2 != null) { if (_pendingHtml != null) GuideBrowser.NavigateToString(_pendingHtml); else if (_pendingUri != null) GuideBrowser.Source = _pendingUri; } } // ─── 타이틀 바 ─────────────────────────────────────────────── private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (e.ClickCount == 2) { ToggleMaximize(); return; } DragMove(); } private void MinBtn_Click(object sender, MouseButtonEventArgs e) { e.Handled = true; WindowState = WindowState.Minimized; } private void MaxBtn_Click(object sender, MouseButtonEventArgs e) { e.Handled = true; ToggleMaximize(); } private void CloseBtn_Click(object sender, MouseButtonEventArgs e) { e.Handled = true; Close(); } private void ToggleMaximize() { WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; } private void TitleBtn_Enter(object sender, MouseEventArgs e) { if (sender is Border b) b.Background = TryFindResource("ItemHoverBackground") as Brush ?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF)); } private void CloseBtnEnter(object sender, MouseEventArgs e) { if (sender is Border b) b.Background = new SolidColorBrush(Color.FromArgb(0x44, 0xFF, 0x40, 0x40)); } private void TitleBtn_Leave(object sender, MouseEventArgs e) { if (sender is Border b) b.Background = Brushes.Transparent; } }