using System.Windows.Controls; using ClaudeDo.Installer.Core; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Win32; namespace ClaudeDo.Installer.Pages.ServicePage; public partial class ServicePageViewModel : ObservableObject, IInstallerPage { private readonly InstallContext _context; private ServicePageView? _view; public string Title => "Service"; public string Icon => "\uE912"; public int Order => 2; public bool ShowInWizard => true; public bool ShowInSettings => true; public UserControl View => _view ??= new ServicePageView { DataContext = this }; [ObservableProperty] private int _signalRPort = 47_821; [ObservableProperty] private int _queueBackstopIntervalMs = 30_000; [ObservableProperty] private string _claudeBin = "claude"; [ObservableProperty] private bool _isLocalSystem = true; [ObservableProperty] private bool _isCurrentUser; [ObservableProperty] private bool _autoStart = true; [ObservableProperty] private int _restartDelayMs = 5000; [ObservableProperty] private string? _validationError; public ServicePageViewModel(InstallContext context) => _context = context; public Task LoadAsync() { var cfg = InstallerWorkerConfig.Load(); SignalRPort = cfg.SignalRPort; QueueBackstopIntervalMs = cfg.QueueBackstopIntervalMs; ClaudeBin = cfg.ClaudeBin; return Task.CompletedTask; } public Task ApplyAsync() { _context.SignalRPort = SignalRPort; _context.QueueBackstopIntervalMs = QueueBackstopIntervalMs; _context.ClaudeBin = ClaudeBin; _context.ServiceAccount = IsCurrentUser ? "CurrentUser" : "LocalSystem"; _context.AutoStart = AutoStart; _context.RestartDelayMs = RestartDelayMs; _context.SignalRUrl = $"http://127.0.0.1:{SignalRPort}/hub"; return Task.CompletedTask; } public bool Validate() { if (SignalRPort < 1024 || SignalRPort > 65535) { ValidationError = "Port must be between 1024 and 65535."; return false; } if (QueueBackstopIntervalMs <= 0) { ValidationError = "Queue backstop interval must be greater than 0."; return false; } if (string.IsNullOrWhiteSpace(ClaudeBin)) { ValidationError = "Claude CLI path is required."; return false; } ValidationError = null; return true; } [RelayCommand] private void BrowseClaude() { var dialog = new OpenFileDialog { Title = "Select Claude CLI executable", Filter = "Executables (*.exe)|*.exe|All files (*.*)|*.*", }; if (dialog.ShowDialog() == true) ClaudeBin = dialog.FileName; } }