using System.Diagnostics; using System.IO; using System.Reflection; using System.Text.Json; using System.Windows; using System.Windows.Input; using System.Windows.Media.Imaging; namespace AxCopilot.Views; public partial class AboutWindow : Window { public AboutWindow() { InitializeComponent(); Loaded += OnLoaded; KeyDown += (_, e) => { if (e.Key == Key.Escape) Close(); }; } private void OnLoaded(object sender, RoutedEventArgs e) { // 버전 정보 var version = Assembly.GetExecutingAssembly().GetName().Version; VersionText.Text = version != null ? $"v{version.Major}.{version.Minor}.{version.Build}" : "v1.0"; BuildInfoText.Text = $"AX Copilot \u00b7 Commander + Agent \u00b7 \u00a9 2026"; // about.json에서 모든 텍스트 로드 (없으면 기본값 유지) TryLoadBranding(version); // 상단: 앱 아이콘 로드 TryLoadAppIcon(); // 마스코트: 숨겨진 상태로 로드 (사용자 클릭 시 오버레이용) TryLoadMascot(); } private void TryLoadBranding(Version? version) { try { var uri = new Uri("pack://application:,,,/Assets/about.json"); var info = System.Windows.Application.GetResourceStream(uri); if (info == null) return; using var reader = new StreamReader(info.Stream); using var doc = JsonDocument.Parse(reader.ReadToEnd()); var root = doc.RootElement; string Get(string key) => root.TryGetProperty(key, out var v) ? v.GetString() ?? "" : ""; var appName = Get("appName"); var company = Get("companyName"); var author = Get("authorName"); var title = Get("authorTitle"); var purpose = Get("purpose"); var copyright = Get("copyright"); var blogUrl = Get("blogUrl"); // 앱 이름 (헤더) if (!string.IsNullOrWhiteSpace(appName)) AppNameText.Text = appName; // 회사명 (조직명만 표시 — 이름/직급은 하단에 별도 표시) if (!string.IsNullOrWhiteSpace(company)) CompanyNameText.Text = company; // 목적 if (!string.IsNullOrWhiteSpace(purpose)) PurposeText.Text = purpose; // 저작권 + 빌드 정보 if (!string.IsNullOrWhiteSpace(copyright)) { var ver = version != null ? $"v{version.Major}.{version.Minor}.{version.Build}" : "v1.0"; BuildInfoText.Text = $"{appName} \u00b7 {ver} \u00b7 Commander + Agent \u00b7 {copyright}"; } // 블로그 URL if (!string.IsNullOrWhiteSpace(blogUrl) && BlogLinkBtn != null) BlogLinkBtn.Content = blogUrl; // 기여자 목록 var contributors = Get("contributors"); if (!string.IsNullOrWhiteSpace(contributors)) { ContributorsText.Text = contributors; ContributorsGrid.Visibility = Visibility.Visible; } } catch { /* 브랜딩 로드 실패 시 기본값 유지 */ } } private void TryLoadAppIcon() { try { var uri = new Uri("pack://application:,,,/Assets/icon.ico"); var stream = System.Windows.Application.GetResourceStream(uri)?.Stream; if (stream != null) { var bmp = new BitmapImage(); bmp.BeginInit(); bmp.StreamSource = stream; bmp.CacheOption = BitmapCacheOption.OnLoad; bmp.DecodePixelWidth = 176; bmp.EndInit(); bmp.Freeze(); AppIconImage.Source = bmp; FallbackIcon.Visibility = Visibility.Collapsed; } } catch { } } private void TryLoadMascot() { // 내장 리소스에서 마스코트 로드 (오버레이용, 상단에는 표시 안 함) foreach (var name in new[] { "mascot.png", "mascot.jpg", "mascot.webp" }) { try { var uri = new Uri($"pack://application:,,,/Assets/{name}"); var stream = System.Windows.Application.GetResourceStream(uri)?.Stream; if (stream == null) continue; var bmp = new BitmapImage(); bmp.BeginInit(); bmp.StreamSource = stream; bmp.CacheOption = BitmapCacheOption.OnLoad; bmp.DecodePixelWidth = 400; bmp.EndInit(); bmp.Freeze(); MascotImage.Source = bmp; return; } catch { } } // 파일 시스템 폴백 var exeDir = Path.GetDirectoryName(Environment.ProcessPath) ?? AppContext.BaseDirectory; foreach (var name in new[] { "mascot.png", "mascot.jpg", "mascot.webp" }) { var path = Path.Combine(exeDir, "Assets", name); if (!File.Exists(path)) continue; try { var bmp = new BitmapImage(); bmp.BeginInit(); bmp.UriSource = new Uri(path, UriKind.Absolute); bmp.CacheOption = BitmapCacheOption.OnLoad; bmp.DecodePixelWidth = 400; bmp.EndInit(); MascotImage.Source = bmp; return; } catch { } } } private void ShowMascot_Click(object sender, MouseButtonEventArgs e) { if (MascotImage.Source == null) return; MascotOverlayImage.Source = MascotImage.Source; MascotOverlay.Visibility = Visibility.Visible; e.Handled = true; // DragMove 방지 } private void HideMascot_Click(object sender, MouseButtonEventArgs e) { MascotOverlay.Visibility = Visibility.Collapsed; e.Handled = true; } // 창 드래그 이동 private void Window_MouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left && e.LeftButton == MouseButtonState.Pressed) try { DragMove(); } catch { } } private void Close_Click(object sender, RoutedEventArgs e) => Close(); private void Blog_Click(object sender, RoutedEventArgs e) { try { Process.Start(new ProcessStartInfo("https://www.swarchitect.net") { UseShellExecute = true }); } catch { /* 브라우저 열기 실패 무시 */ } } }