using Avalonia; using ClaudeDo.Data; using ClaudeDo.Data.Git; using ClaudeDo.Localization; using ClaudeDo.Releases; using ClaudeDo.Ui; using ClaudeDo.Ui.Localization; using ClaudeDo.Ui.Services; using ClaudeDo.Ui.Services.Interfaces; using ClaudeDo.Ui.ViewModels; using ClaudeDo.Ui.ViewModels.Islands; using ClaudeDo.Ui.ViewModels.Modals; using ClaudeDo.Ui.ViewModels.Modals.Settings; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using System; using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; using System.Reflection; using System.Runtime.InteropServices; namespace ClaudeDo.App; sealed class Program { [DllImport("shell32.dll", CharSet = CharSet.Unicode)] private static extern int SetCurrentProcessExplicitAppUserModelID(string appId); [STAThread] public static void Main(string[] args) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) SetCurrentProcessExplicitAppUserModelID("ClaudeDo.App"); var services = BuildServices(); using (var scope = services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); ClaudeDoDbContext.MigrateAndConfigure(db); } try { ConfigureAppBuilder(AppBuilder.Configure(() => new App(services))) .StartWithClassicDesktopLifetime(args); } finally { // Dispose the container so WorkerClient.DisposeAsync runs — // cancels the retry loop and closes the SignalR connection cleanly // instead of abandoning it. try { services.DisposeAsync().AsTask().GetAwaiter().GetResult(); } catch { /* best effort on shutdown */ } } } // Parameterless entry point required by the XAML previewer / designer. public static AppBuilder BuildAvaloniaApp() => ConfigureAppBuilder(AppBuilder.Configure()); private static AppBuilder ConfigureAppBuilder(AppBuilder builder) => builder .UsePlatformDetect() #if DEBUG .WithDeveloperTools() #endif .WithInterFont() .LogToTrace(); private static ServiceProvider BuildServices() { var settings = AppSettings.Load(); var dbPath = Paths.Expand(settings.DbPath); var sc = new ServiceCollection(); // Infrastructure sc.AddSingleton(settings); var localesDir = Path.Combine(AppContext.BaseDirectory, "locales"); var localeStore = LocaleStore.Load(localesDir); var initialLang = !string.IsNullOrWhiteSpace(settings.Language) ? settings.Language : CultureResolver.Resolve( CultureInfo.CurrentUICulture.Name, localeStore.Available.Select(l => l.Code).ToArray(), fallback: "en"); var localizer = new Localizer(localeStore, initialLang); TrExtension.Localizer = localizer; ClaudeDo.Ui.Localization.Loc.Current = localizer; sc.AddSingleton(localizer); sc.AddDbContextFactory(opt => opt.UseSqlite($"Data Source={dbPath}")); sc.AddScoped(sp => sp.GetRequiredService>().CreateDbContext()); // Services sc.AddSingleton(); sc.AddSingleton(sp => new WorkerClient(sp.GetRequiredService().SignalRUrl)); sc.AddSingleton(sp => sp.GetRequiredService()); // Release check + installer update sc.AddSingleton(_ => new HttpClient { Timeout = TimeSpan.FromSeconds(10) }); sc.AddSingleton(sp => new ReleaseClient(sp.GetRequiredService())); sc.AddSingleton(); sc.AddSingleton(); sc.AddSingleton(sp => { var releases = sp.GetRequiredService(); var informational = Assembly.GetEntryAssembly()? .GetCustomAttribute()?.InformationalVersion; // Strip MinVer build metadata ("+sha") and any prerelease suffix for the update-compare. var version = (informational ?? "0.0.0").Split('+')[0]; return new UpdateCheckService(releases, version); }); // ViewModels sc.AddTransient(); sc.AddTransient>(sp => () => sp.GetRequiredService()); sc.AddTransient(); sc.AddTransient>(sp => () => sp.GetRequiredService()); sc.AddSingleton(); sc.AddSingleton(); sc.AddTransient(); sc.AddTransient(); sc.AddTransient(); sc.AddTransient>(sp => () => sp.GetRequiredService()); sc.AddTransient(); sc.AddTransient(); sc.AddTransient>(sp => () => sp.GetRequiredService()); sc.AddTransient(); sc.AddTransient>(sp => () => sp.GetRequiredService()); sc.AddSingleton>(sp => taskId => new ClaudeDo.Ui.ViewModels.Conflicts.ConflictResolverViewModel( sp.GetRequiredService(), taskId)); // Islands shell VMs sc.AddSingleton(sp => new ListsIslandViewModel( sp.GetRequiredService>(), sp, sp.GetRequiredService())); sc.AddSingleton(sp => new TasksIslandViewModel( sp.GetRequiredService>(), sp.GetRequiredService())); sc.AddSingleton(sp => new DetailsIslandViewModel( sp.GetRequiredService>(), sp.GetRequiredService(), sp, sp.GetRequiredService())); sc.AddSingleton(sp => { var shell = ActivatorUtilities.CreateInstance(sp); shell.ConflictResolverFactory = sp.GetRequiredService>(); return shell; }); return sc.BuildServiceProvider(); } }