using System.Diagnostics; using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform.Storage; using CodexProviderSync.Core; namespace CodexProviderSync.Mac; public sealed class MainWindow : Window { private readonly CodexSyncService _syncService = new(); private readonly SettingsService _settingsService = new(); private readonly TextBox _codexHomeText = new(); private readonly ComboBox _recentHomes = new(); private readonly TextBlock _languageLabel = new(); private readonly ComboBox _languageCombo = new(); private readonly Button _browseButton = new(); private readonly Button _refreshButton = new(); private readonly TextBlock _busyText = new(); private readonly TextBox _statusText = new(); private readonly ListBox _providerList = new(); private readonly TextBox _manualProviderText = new(); private readonly Button _addProviderButton = new(); private readonly Button _removeProviderButton = new(); private readonly TextBlock _selectedProviderText = new(); private readonly CheckBox _switchConfigCheck = new(); private readonly NumericUpDown _backupRetentionInput = new(); private readonly Button _executeButton = new(); private readonly CheckBox _restoreConfigCheck = new(); private readonly CheckBox _restoreDatabaseCheck = new(); private readonly CheckBox _restoreSessionsCheck = new(); private readonly Button _restoreButton = new(); private readonly Button _openBackupButton = new(); private readonly Button _pruneBackupsButton = new(); private readonly TextBox _logText = new(); private readonly TextBlock _codexHomeLabel = new(); private readonly TextBlock _statusTitle = new(); private readonly TextBlock _providersTitle = new(); private readonly TextBlock _actionsTitle = new(); private readonly TextBlock _logTitle = new(); private readonly TextBlock _targetProviderLabel = new(); private readonly TextBlock _restoreContentsLabel = new(); private readonly TextBlock _keepBackupsLabel = new(); private readonly TextBlock _writeWarningText = new(); private AppSettings _settings = new(); private StatusSnapshot? _currentStatus; private bool _loadingSettings; private string _language = MacUiText.English; public MainWindow() { Title = "Codex Provider Sync"; MinWidth = 1120; MinHeight = 760; Width = 1240; Height = 820; Background = Brush.Parse("#f5f5f7"); Content = BuildLayout(); WireEvents(); } protected override async void OnOpened(EventArgs e) { base.OnOpened(e); await LoadStateAsync(); } protected override void OnClosing(WindowClosingEventArgs e) { PersistUiState(); base.OnClosing(e); } private Control BuildLayout() { Grid root = new() { RowDefinitions = new RowDefinitions("Auto,*,220"), Margin = new Thickness(18), RowSpacing = 14 }; root.Children.Add(BuildCodexHomePanel()); root.Children.Add(BuildMainPanel()); root.Children.Add(BuildLogPanel()); Grid.SetRow(root.Children[1], 1); Grid.SetRow(root.Children[2], 2); return root; } private Control BuildCodexHomePanel() { Grid panel = new() { ColumnDefinitions = new ColumnDefinitions("Auto,*,170,Auto,Auto,Auto,132"), ColumnSpacing = 10 }; ConfigureLabel(_codexHomeLabel, "Codex Home"); ConfigureLabel(_languageLabel, "Language"); _codexHomeText.PlaceholderText = AppConstants.DefaultCodexHome(); _codexHomeText.MinHeight = 34; _recentHomes.MinHeight = 34; _recentHomes.PlaceholderText = "Recent"; _languageCombo.MinHeight = 34; _languageCombo.Items.Add("English"); _languageCombo.Items.Add("中文"); _languageCombo.SelectedIndex = 0; _browseButton.Content = "Browse"; _browseButton.MinHeight = 34; _refreshButton.Content = "Refresh"; _refreshButton.MinHeight = 34; panel.Children.Add(_codexHomeLabel); panel.Children.Add(_codexHomeText); panel.Children.Add(_recentHomes); panel.Children.Add(_browseButton); panel.Children.Add(_refreshButton); panel.Children.Add(_languageLabel); panel.Children.Add(_languageCombo); Grid.SetColumn(_codexHomeText, 1); Grid.SetColumn(_recentHomes, 2); Grid.SetColumn(_browseButton, 3); Grid.SetColumn(_refreshButton, 4); Grid.SetColumn(_languageLabel, 5); Grid.SetColumn(_languageCombo, 6); return Card(panel); } private Control BuildMainPanel() { Grid main = new() { ColumnDefinitions = new ColumnDefinitions("1.15*,1*,340"), ColumnSpacing = 14 }; main.Children.Add(BuildStatusPanel()); main.Children.Add(BuildProviderPanel()); main.Children.Add(BuildActionsPanel()); Grid.SetColumn(main.Children[1], 1); Grid.SetColumn(main.Children[2], 2); return main; } private Control BuildStatusPanel() { _statusText.IsReadOnly = true; _statusText.AcceptsReturn = true; _statusText.TextWrapping = TextWrapping.NoWrap; _statusText.FontFamily = FontFamily.Parse("Menlo, Consolas, monospace"); _statusText.FontSize = 12; _statusText.Background = Brushes.White; return Section(_statusTitle, _statusText); } private Control BuildProviderPanel() { Grid panel = new() { RowDefinitions = new RowDefinitions("*,Auto,Auto"), RowSpacing = 10 }; _providerList.SelectionMode = SelectionMode.Single; _providerList.Background = Brushes.White; Grid addPanel = new() { ColumnDefinitions = new ColumnDefinitions("*,Auto"), ColumnSpacing = 8 }; _manualProviderText.PlaceholderText = "Add provider id"; _manualProviderText.MinHeight = 34; _addProviderButton.Content = "Add"; _addProviderButton.MinHeight = 34; addPanel.Children.Add(_manualProviderText); addPanel.Children.Add(_addProviderButton); Grid.SetColumn(_addProviderButton, 1); _removeProviderButton.Content = "Remove Manual"; _removeProviderButton.HorizontalAlignment = HorizontalAlignment.Left; panel.Children.Add(_providerList); panel.Children.Add(addPanel); panel.Children.Add(_removeProviderButton); Grid.SetRow(addPanel, 1); Grid.SetRow(_removeProviderButton, 2); return Section(_providersTitle, panel); } private Control BuildActionsPanel() { StackPanel panel = new() { Spacing = 12 }; _selectedProviderText.Text = "No provider selected"; _selectedProviderText.FontSize = 18; _selectedProviderText.FontWeight = FontWeight.SemiBold; _selectedProviderText.TextWrapping = TextWrapping.Wrap; _switchConfigCheck.Content = "Switch config.toml and sync"; _backupRetentionInput.Minimum = 1; _backupRetentionInput.Maximum = 100000; _backupRetentionInput.Value = AppConstants.DefaultBackupRetentionCount; _backupRetentionInput.Width = 112; _executeButton.Content = "Sync Metadata Only"; _executeButton.MinHeight = 40; _executeButton.HorizontalAlignment = HorizontalAlignment.Stretch; _restoreConfigCheck.Content = "Restore config.toml"; _restoreDatabaseCheck.Content = "Restore SQLite"; _restoreSessionsCheck.Content = "Restore rollout metadata"; _restoreConfigCheck.IsChecked = false; _restoreDatabaseCheck.IsChecked = true; _restoreSessionsCheck.IsChecked = true; _restoreButton.Content = "Restore Backup"; _restoreButton.MinHeight = 38; _openBackupButton.Content = "Open Backup Folder"; _openBackupButton.MinHeight = 38; _pruneBackupsButton.Content = "Clean Old Backups"; _pruneBackupsButton.MinHeight = 38; _busyText.Text = "Ready"; _busyText.Foreground = Brush.Parse("#166534"); ConfigureMutedLabel(_targetProviderLabel, "Target provider"); ConfigureMutedLabel(_restoreContentsLabel, "Restore contents"); panel.Children.Add(_targetProviderLabel); panel.Children.Add(_selectedProviderText); panel.Children.Add(_switchConfigCheck); panel.Children.Add(BuildRetentionPanel()); panel.Children.Add(WarningBlock(_writeWarningText)); panel.Children.Add(_executeButton); panel.Children.Add(Separator()); panel.Children.Add(_restoreContentsLabel); panel.Children.Add(_restoreConfigCheck); panel.Children.Add(_restoreDatabaseCheck); panel.Children.Add(_restoreSessionsCheck); panel.Children.Add(_restoreButton); panel.Children.Add(_openBackupButton); panel.Children.Add(_pruneBackupsButton); panel.Children.Add(_busyText); return Section(_actionsTitle, panel); } private Control BuildRetentionPanel() { StackPanel panel = new() { Orientation = Orientation.Horizontal, Spacing = 8, VerticalAlignment = VerticalAlignment.Center }; ConfigureMutedLabel(_keepBackupsLabel, "Keep backups"); panel.Children.Add(_keepBackupsLabel); panel.Children.Add(_backupRetentionInput); return panel; } private Control BuildLogPanel() { _logText.IsReadOnly = true; _logText.AcceptsReturn = true; _logText.FontFamily = FontFamily.Parse("Menlo, Consolas, monospace"); _logText.FontSize = 12; _logText.Background = Brushes.White; return Section(_logTitle, _logText); } private void WireEvents() { _browseButton.Click += async (_, _) => await BrowseCodexHomeAsync(); _refreshButton.Click += async (_, _) => await RefreshStatusAsync(); _languageCombo.SelectionChanged += async (_, _) => await ChangeLanguageAsync(); _recentHomes.SelectionChanged += (_, _) => { if (_recentHomes.SelectedItem is string home) { _codexHomeText.Text = home; } }; _addProviderButton.Click += async (_, _) => await AddManualProviderAsync(); _removeProviderButton.Click += async (_, _) => await RemoveManualProviderAsync(); _providerList.SelectionChanged += (_, _) => UpdateSelectionLabel(); _switchConfigCheck.PropertyChanged += (_, args) => { if (args.Property.Name == nameof(CheckBox.IsChecked)) { UpdateExecuteButtonText(); } }; _backupRetentionInput.ValueChanged += async (_, _) => await PersistBackupRetentionAsync(); _executeButton.Click += async (_, _) => await ExecuteSyncOrSwitchAsync(); _restoreButton.Click += async (_, _) => await RestoreBackupAsync(); _openBackupButton.Click += async (_, _) => await OpenBackupFolderAsync(); _pruneBackupsButton.Click += async (_, _) => await PruneBackupsAsync(); _codexHomeText.LostFocus += async (_, _) => await PersistHomeSelectionAsync(); _manualProviderText.KeyDown += async (_, args) => { if (args.Key == Key.Enter) { args.Handled = true; await AddManualProviderAsync(); } }; } private async Task LoadStateAsync() { _loadingSettings = true; _settings = await _settingsService.LoadAsync(); _language = MacUiText.NormalizeLanguage(_settings.UiLanguage); _languageCombo.SelectedIndex = MacUiText.IsChinese(_language) ? 1 : 0; ApplyLanguage(); ApplyWindowBounds(_settings.WindowBounds); ReloadRecentHomes(); _codexHomeText.Text = _settings.LastCodexHome ?? AppConstants.DefaultCodexHome(); _backupRetentionInput.Value = Math.Max(1, _settings.BackupRetentionCount); AppendLog($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {T("loadedSettings")}: {_settingsService.SettingsPath}"); _loadingSettings = false; await RefreshStatusAsync(); } private async Task RefreshStatusAsync() { string codexHome = CurrentCodexHome(); await RunBusyAsync(T("refreshing"), () => RefreshStatusCoreAsync(codexHome)); } private async Task ChangeLanguageAsync() { if (_loadingSettings) { return; } _language = _languageCombo.SelectedIndex == 1 ? MacUiText.Chinese : MacUiText.English; _settings = _settingsService.UpdateUiLanguage(_settings, _language); await _settingsService.SaveAsync(_settings); ApplyLanguage(); if (_currentStatus is not null) { _statusText.Text = MacDisplayFormatter.FormatStatus(_currentStatus, _language); } ReloadProviderList(); AppendLog($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {T("languageChanged")}: {(_language == MacUiText.Chinese ? "中文" : "English")}"); } private async Task BrowseCodexHomeAsync() { IStorageFolder? start = null; string current = CurrentCodexHome(); if (Directory.Exists(current)) { start = await StorageProvider.TryGetFolderFromPathAsync(current); } IReadOnlyList folders = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions { Title = T("chooseCodexFolder"), AllowMultiple = false, SuggestedStartLocation = start }); if (folders.Count == 0 || folders[0].Path.LocalPath is not { Length: > 0 } selected) { return; } _codexHomeText.Text = selected; await PersistHomeSelectionAsync(); await RefreshStatusAsync(); } private async Task PersistHomeSelectionAsync() { string codexHome = CurrentCodexHome(); if (string.IsNullOrWhiteSpace(codexHome)) { return; } _settings = _settingsService.RecordCodexHome(_settings, codexHome); _settings = _settingsService.UpdateState(_settings, SelectedProvider(), _settings.LastBackupDirectory, CaptureWindowBounds(), CurrentBackupRetentionCount()); await _settingsService.SaveAsync(_settings); ReloadRecentHomes(); } private async Task PersistBackupRetentionAsync() { if (_loadingSettings) { return; } _settings = _settingsService.UpdateState( _settings, SelectedProvider(), _settings.LastBackupDirectory, CaptureWindowBounds(), CurrentBackupRetentionCount()); await _settingsService.SaveAsync(_settings); AppendLog($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] Backup retention set to {CurrentBackupRetentionCount()}"); } private async Task AddManualProviderAsync() { string provider = (_manualProviderText.Text ?? string.Empty).Trim(); if (string.IsNullOrWhiteSpace(provider)) { await ShowInfoAsync(T("enterProvider")); return; } _settings = _settingsService.AddManualProvider(_settings, provider); await _settingsService.SaveAsync(_settings); _manualProviderText.Clear(); ReloadProviderList(); SelectProvider(provider); AppendLog($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {T("addedManualProvider")}: {provider}"); } private async Task RemoveManualProviderAsync() { string? provider = SelectedProvider(); if (string.IsNullOrWhiteSpace(provider)) { await ShowInfoAsync(T("selectProvider")); return; } _settings = _settingsService.RemoveManualProvider(_settings, provider); await _settingsService.SaveAsync(_settings); ReloadProviderList(); SelectProvider(_currentStatus?.CurrentProvider.Provider); AppendLog($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {T("removedManualProvider")}: {provider}"); } private async Task ExecuteSyncOrSwitchAsync() { string? provider = SelectedProvider(); if (string.IsNullOrWhiteSpace(provider)) { await ShowInfoAsync(T("selectTargetProvider")); return; } string mode = IsSwitchMode() ? T("confirmSwitchMode") : T("confirmSyncMode"); if (!await ConfirmAsync( T("confirmWriteTitle"), string.Format(T("confirmWriteMessage"), mode, provider))) { return; } await RunBusyAsync(T("executing"), async () => { string codexHome = CurrentCodexHome(); int backupRetentionCount = CurrentBackupRetentionCount(); SyncResult result = IsSwitchMode() ? await Task.Run(async () => await _syncService.RunSwitchAsync(codexHome, provider, backupRetentionCount)) : await Task.Run(async () => await _syncService.RunSyncAsync(codexHome, provider: provider, keepCount: backupRetentionCount)); _settings = _settingsService.UpdateState(_settings, provider, result.BackupDir, CaptureWindowBounds(), backupRetentionCount); await _settingsService.SaveAsync(_settings); AppendLog($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {T("executionFinished")}"); AppendLog(MacDisplayFormatter.FormatSyncResult(result, IsSwitchMode() ? T("switchedAndSynced") : T("synced"), _language)); AppendLog(string.Empty); await RefreshStatusCoreAsync(codexHome); SelectProvider(provider); }); } private async Task RestoreBackupAsync() { string backupRoot = _currentStatus?.BackupRoot ?? AppConstants.DefaultBackupRoot(CurrentCodexHome()); string initialBackupDir = Directory.Exists(_settings.LastBackupDirectory) ? _settings.LastBackupDirectory! : backupRoot; IStorageFolder? start = Directory.Exists(initialBackupDir) ? await StorageProvider.TryGetFolderFromPathAsync(initialBackupDir) : null; IReadOnlyList folders = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions { Title = T("chooseBackupFolder"), AllowMultiple = false, SuggestedStartLocation = start }); if (folders.Count == 0 || folders[0].Path.LocalPath is not { Length: > 0 } backupDir) { return; } bool restoreConfig = _restoreConfigCheck.IsChecked == true; bool restoreDatabase = _restoreDatabaseCheck.IsChecked == true; bool restoreSessions = _restoreSessionsCheck.IsChecked == true; if (!restoreConfig && !restoreDatabase && !restoreSessions) { await ShowInfoAsync(T("chooseRestoreTarget")); return; } string restoreTargets = string.Join(", ", new[] { restoreConfig ? "config.toml" : null, restoreDatabase ? "SQLite" : null, restoreSessions ? "rollout metadata" : null }.Where(static value => value is not null)); if (!await ConfirmAsync( T("restoreTitle"), string.Format(T("restoreMessage"), backupDir, restoreTargets))) { return; } await RunBusyAsync(T("restoring"), async () => { string codexHome = CurrentCodexHome(); RestoreResult result = await Task.Run(async () => await _syncService.RunRestoreAsync( codexHome, backupDir, new RestoreBackupOptions { RestoreConfig = restoreConfig, RestoreDatabase = restoreDatabase, RestoreSessions = restoreSessions })); _settings = _settingsService.UpdateState(_settings, SelectedProvider(), backupDir, CaptureWindowBounds(), CurrentBackupRetentionCount()); await _settingsService.SaveAsync(_settings); AppendLog($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {T("restoreFinished")}"); AppendLog(MacDisplayFormatter.FormatRestoreResult(result, _language)); AppendLog(string.Empty); await RefreshStatusCoreAsync(codexHome); }); } private async Task OpenBackupFolderAsync() { string path = _currentStatus?.BackupRoot ?? AppConstants.DefaultBackupRoot(CurrentCodexHome()); Directory.CreateDirectory(path); try { Process.Start(new ProcessStartInfo { FileName = OperatingSystem.IsMacOS() ? "open" : path, Arguments = OperatingSystem.IsMacOS() ? QuoteShellArgument(path) : string.Empty, UseShellExecute = !OperatingSystem.IsMacOS() }); } catch (Exception error) { await ShowErrorAsync(error); } } private async Task PruneBackupsAsync() { if (!await ConfirmAsync( T("cleanTitle"), string.Format(T("cleanMessage"), CurrentBackupRetentionCount()))) { return; } await RunBusyAsync(T("cleaning"), async () => { string codexHome = CurrentCodexHome(); BackupPruneResult result = await Task.Run(async () => await _syncService.RunPruneBackupsAsync(codexHome, CurrentBackupRetentionCount())); AppendLog($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {T("backupCleanupFinished")}"); AppendLog(MacDisplayFormatter.FormatBackupPruneResult(result, _language)); AppendLog(string.Empty); await RefreshStatusCoreAsync(codexHome); }); } private async Task RefreshStatusCoreAsync(string codexHome) { _currentStatus = await Task.Run(async () => await _syncService.GetStatusAsync(codexHome)); _settings = _settingsService.RecordCodexHome(_settings, _currentStatus.CodexHome); _settings = _settingsService.MergeDetectedProviders(_settings, _syncService.ExtractDetectedProviderIds(_currentStatus)); _settings = _settingsService.UpdateState(_settings, SelectedProvider(), _settings.LastBackupDirectory, CaptureWindowBounds(), CurrentBackupRetentionCount()); await _settingsService.SaveAsync(_settings); _statusText.Text = MacDisplayFormatter.FormatStatus(_currentStatus, _language); ReloadRecentHomes(); ReloadProviderList(); _codexHomeText.Text = _currentStatus.CodexHome; AppendLog($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {T("refreshed")}: {_currentStatus.CodexHome}"); } private void ReloadRecentHomes() { string? selected = _codexHomeText.Text; _recentHomes.Items.Clear(); foreach (string home in _settings.RecentCodexHomes) { _recentHomes.Items.Add(home); } _codexHomeText.Text = selected; } private void ReloadProviderList() { _providerList.Items.Clear(); if (_currentStatus is not null) { foreach (ProviderOption option in _syncService.BuildProviderOptions(_currentStatus, _settings)) { ProviderListItem item = new() { Option = option, SourcesText = MacDisplayFormatter.FormatProviderSources(option, _language), DetailText = ProviderDetailText(option) }; _providerList.Items.Add(BuildProviderRow(item)); } } SelectProvider(_settings.LastSelectedProvider ?? _currentStatus?.CurrentProvider.Provider); UpdateSelectionLabel(); } private ListBoxItem BuildProviderRow(ProviderListItem provider) { Grid row = new() { ColumnDefinitions = new ColumnDefinitions("*,Auto"), RowDefinitions = new RowDefinitions("Auto,Auto"), Margin = new Thickness(8, 7) }; TextBlock title = new() { Text = provider.Option.Id, FontWeight = provider.Option.IsCurrentProvider ? FontWeight.SemiBold : FontWeight.Normal, TextTrimming = TextTrimming.CharacterEllipsis }; TextBlock sources = new() { Text = provider.SourcesText, Foreground = Brush.Parse("#52525b"), FontSize = 12, TextWrapping = TextWrapping.Wrap }; TextBlock detail = new() { Text = provider.DetailText, Foreground = Brush.Parse("#71717a"), FontSize = 12 }; row.Children.Add(title); row.Children.Add(detail); row.Children.Add(sources); Grid.SetColumn(detail, 1); Grid.SetRow(sources, 1); Grid.SetColumnSpan(sources, 2); return new ListBoxItem { Tag = provider.Option.Id, Content = row }; } private string ProviderDetailText(ProviderOption option) { List details = []; if (option.IsCurrentProvider) { details.Add(T("current")); } if (option.IsManual) { details.Add(T("manual")); } if (option.IsSaved) { details.Add(T("saved")); } return details.Count == 0 ? string.Empty : string.Join(" · ", details); } private void SelectProvider(string? provider) { if (string.IsNullOrWhiteSpace(provider)) { return; } foreach (object? item in _providerList.Items) { if (item is ListBoxItem row && string.Equals(row.Tag as string, provider, StringComparison.Ordinal)) { _providerList.SelectedItem = row; break; } } } private void UpdateSelectionLabel() { string? provider = SelectedProvider(); _selectedProviderText.Text = string.IsNullOrWhiteSpace(provider) ? T("noProvider") : provider; } private void UpdateExecuteButtonText() { _executeButton.Content = IsSwitchMode() ? T("switchSync") : T("syncMetadata"); } private string? SelectedProvider() { return _providerList.SelectedItem is ListBoxItem row ? row.Tag as string : null; } private string CurrentCodexHome() { string text = (_codexHomeText.Text ?? string.Empty).Trim(); return string.IsNullOrWhiteSpace(text) ? AppConstants.DefaultCodexHome() : text; } private int CurrentBackupRetentionCount() { decimal value = _backupRetentionInput.Value ?? AppConstants.DefaultBackupRetentionCount; return Decimal.ToInt32(Math.Max(1, value)); } private bool IsSwitchMode() { return _switchConfigCheck.IsChecked == true; } private void PersistUiState() { try { _settings = _settingsService.RecordCodexHome(_settings, CurrentCodexHome()); _settings = _settingsService.UpdateUiLanguage(_settings, _language); _settings = _settingsService.UpdateState(_settings, SelectedProvider(), _settings.LastBackupDirectory, CaptureWindowBounds(), CurrentBackupRetentionCount()); _settingsService.Save(_settings); } catch { // Ignore shutdown persistence failures. } } private WindowBoundsState CaptureWindowBounds() { return new WindowBoundsState { X = Position.X, Y = Position.Y, Width = Decimal.ToInt32((decimal)Math.Max(Width, MinWidth)), Height = Decimal.ToInt32((decimal)Math.Max(Height, MinHeight)), Maximized = WindowState == WindowState.Maximized }; } private void ApplyWindowBounds(WindowBoundsState? bounds) { if (bounds is null || bounds.Width < 800 || bounds.Height < 600) { return; } Position = new PixelPoint(bounds.X, bounds.Y); Width = bounds.Width; Height = bounds.Height; if (bounds.Maximized) { WindowState = WindowState.Maximized; } } private async Task RunBusyAsync(string stateText, Func action) { SetBusy(true, stateText); try { await action(); } catch (Exception error) { AppendLog($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {T("error")}: {error}"); await ShowErrorAsync(error); } finally { SetBusy(false, T("ready")); } } private void SetBusy(bool busy, string stateText) { Cursor = busy ? new Cursor(StandardCursorType.Wait) : Cursor.Default; _busyText.Text = stateText; _busyText.Foreground = busy ? Brush.Parse("#b45309") : Brush.Parse("#166534"); _browseButton.IsEnabled = !busy; _refreshButton.IsEnabled = !busy; _addProviderButton.IsEnabled = !busy; _removeProviderButton.IsEnabled = !busy; _switchConfigCheck.IsEnabled = !busy; _backupRetentionInput.IsEnabled = !busy; _restoreConfigCheck.IsEnabled = !busy; _restoreDatabaseCheck.IsEnabled = !busy; _restoreSessionsCheck.IsEnabled = !busy; _executeButton.IsEnabled = !busy; _restoreButton.IsEnabled = !busy; _openBackupButton.IsEnabled = !busy; _pruneBackupsButton.IsEnabled = !busy; _providerList.IsEnabled = !busy; _manualProviderText.IsEnabled = !busy; _codexHomeText.IsEnabled = !busy; _recentHomes.IsEnabled = !busy; _languageCombo.IsEnabled = !busy; } private void AppendLog(string message) { _logText.Text = string.IsNullOrEmpty(_logText.Text) ? message : $"{_logText.Text}{Environment.NewLine}{message}"; _logText.CaretIndex = _logText.Text?.Length ?? 0; } private async Task ConfirmAsync(string title, string message) { ConfirmationDialog dialog = new(title, message, T("continue"), T("cancel")); return await dialog.ShowDialog(this); } private async Task ShowInfoAsync(string message) { ConfirmationDialog dialog = new("Codex Provider Sync", message, T("ok"), null); await dialog.ShowDialog(this); } private async Task ShowErrorAsync(Exception error) { ConfirmationDialog dialog = new(T("error"), error.Message, T("ok"), null); await dialog.ShowDialog(this); } private void ApplyLanguage() { _languageLabel.Text = T("language"); _recentHomes.PlaceholderText = T("recent"); _browseButton.Content = T("browse"); _refreshButton.Content = T("refresh"); _statusTitle.Text = T("status"); _providersTitle.Text = T("providers"); _actionsTitle.Text = T("actions"); _logTitle.Text = T("executionLog"); _manualProviderText.PlaceholderText = T("addProvider"); _addProviderButton.Content = T("add"); _removeProviderButton.Content = T("removeManual"); _switchConfigCheck.Content = T("switchConfig"); _restoreConfigCheck.Content = T("restoreConfig"); _restoreDatabaseCheck.Content = T("restoreSqlite"); _restoreSessionsCheck.Content = T("restoreRollout"); _restoreButton.Content = T("restoreBackup"); _openBackupButton.Content = T("openBackupFolder"); _pruneBackupsButton.Content = T("cleanBackups"); _targetProviderLabel.Text = T("targetProvider"); _restoreContentsLabel.Text = T("restoreContents"); _keepBackupsLabel.Text = T("keepBackups"); _writeWarningText.Text = T("writeWarning"); _busyText.Text = T("ready"); UpdateExecuteButtonText(); UpdateSelectionLabel(); } private string T(string key) { return MacUiText.Get(_language, key); } private static Border Card(Control content) { return new Border { Background = Brushes.White, BorderBrush = Brush.Parse("#d4d4d8"), BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(8), Padding = new Thickness(12), Child = content }; } private static Control Section(TextBlock title, Control content) { Grid panel = new() { RowDefinitions = new RowDefinitions("Auto,*"), RowSpacing = 8 }; ConfigureSectionTitle(title); panel.Children.Add(title); panel.Children.Add(content); Grid.SetRow(content, 1); return Card(panel); } private static void ConfigureLabel(TextBlock label, string text) { label.Text = text; label.VerticalAlignment = VerticalAlignment.Center; label.FontWeight = FontWeight.SemiBold; label.Foreground = Brush.Parse("#18181b"); } private static void ConfigureSectionTitle(TextBlock title) { title.FontSize = 13; title.FontWeight = FontWeight.SemiBold; title.Foreground = Brush.Parse("#18181b"); } private static void ConfigureMutedLabel(TextBlock label, string text) { label.Text = text; label.Foreground = Brush.Parse("#52525b"); label.FontSize = 12; } private static Border WarningBlock(TextBlock text) { text.Foreground = Brush.Parse("#9a3412"); text.TextWrapping = TextWrapping.Wrap; return new Border { Background = Brush.Parse("#fff7ed"), BorderBrush = Brush.Parse("#fed7aa"), BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(8), Padding = new Thickness(10), Child = text }; } private static Separator Separator() { return new Separator { Margin = new Thickness(0, 2) }; } private static string QuoteShellArgument(string value) { return "\"" + value.Replace("\"", "\\\"", StringComparison.Ordinal) + "\""; } }