Ctrl+C in a Mission Control terminal tile is bound by the terminal library to CopyAsync, which throws IndexOutOfRangeException out of XTerm's selection buffer on some selections. It runs from an async void key handler, so the exception reached the dispatcher unhandled and terminated the process -- every open ConPTY session with it. Handle it instead and surface the message in the footer error strip. Iciclecreek.Avalonia.Terminal 2.0.3 is the newest release, so there is no upstream fix to take.
71 lines
2.6 KiB
C#
71 lines
2.6 KiB
C#
using System;
|
|
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.ApplicationLifetimes;
|
|
using Avalonia.Markup.Xaml;
|
|
using Avalonia.Threading;
|
|
using ClaudeDo.Ui;
|
|
using ClaudeDo.Ui.Localization;
|
|
using ClaudeDo.Ui.Services;
|
|
using ClaudeDo.Ui.ViewModels;
|
|
using ClaudeDo.Ui.Views;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace ClaudeDo.App;
|
|
|
|
public partial class App : Application
|
|
{
|
|
private readonly IServiceProvider? _services;
|
|
|
|
// Parameterless ctor is required by the XAML previewer / designer.
|
|
public App() { }
|
|
|
|
public App(IServiceProvider services) => _services = services;
|
|
|
|
public override void Initialize()
|
|
{
|
|
AvaloniaXamlLoader.Load(this);
|
|
if (_services?.GetService<AppSettings>() is { } settings)
|
|
AccentPresetService.Apply(AccentPresets.Find(settings.AccentPreset));
|
|
}
|
|
|
|
public override void OnFrameworkInitializationCompleted()
|
|
{
|
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
|
{
|
|
var services = _services
|
|
?? throw new InvalidOperationException("App was constructed without a service provider.");
|
|
|
|
FocusClearing.Install();
|
|
|
|
// The main window is authoritative — closing it shuts the app down even if the
|
|
// modeless Mission Control window is still open.
|
|
desktop.ShutdownMode = ShutdownMode.OnMainWindowClose;
|
|
|
|
var shell = services.GetRequiredService<IslandsShellViewModel>();
|
|
|
|
// Last-resort backstop: an exception escaping a dispatcher job kills the process, and
|
|
// ClaudeDo hosts third-party UI (the ConPTY terminal control) whose async void key and
|
|
// render handlers have done exactly that — one bad keystroke took the whole app down,
|
|
// losing every open session. Swallowing is the lesser evil here: the failing job is
|
|
// already dead either way, and the user still gets told via the footer error strip.
|
|
Dispatcher.UIThread.UnhandledException += (_, e) =>
|
|
{
|
|
e.Handled = true;
|
|
shell.FlashFooterError(Loc.T("vm.shell.unexpectedError", e.Exception.Message));
|
|
};
|
|
|
|
desktop.MainWindow = new MainWindow
|
|
{
|
|
DataContext = shell,
|
|
};
|
|
|
|
// Kick off the SignalR retry loop — reconnects indefinitely if the worker
|
|
// is not up yet, or goes down and comes back.
|
|
_ = services.GetRequiredService<WorkerClient>().StartAsync();
|
|
}
|
|
|
|
base.OnFrameworkInitializationCompleted();
|
|
}
|
|
}
|