using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Threading; using LehrerApp.Desktop.Views.Mcp; namespace LehrerApp.Desktop.Services.Mcp; /// /// Produktive -Implementierung: zeigt /// über dem Hauptfenster an. Der aufrufende Tool-Handler läuft auf einem Hintergrund-Thread /// (MCP-Pipe-Session in ), deshalb Marshalling über /// . /// /// Nutzer-Feedback: der Dialog fiel zu wenig auf, wenn LehrerApp im Hintergrund lief (naheliegend, /// da der Anstoß von einem KI-Client in einem anderen Fenster kommt) — deshalb wird das Hauptfenster /// vor dem Anzeigen aus einer möglichen Minimierung geholt und aktiviert, und der Dialog selbst /// läuft `Topmost`. /// /// Ohne Reaktion des Nutzers würde die Pipe-Session (und damit der wartende KI-Client) unbegrenzt /// hängen bleiben — nach wird der Dialog automatisch geschlossen und die /// Änderung als abgelehnt gewertet. /// public sealed class AvaloniaMcpConfirmationService : IMcpConfirmationService { private static readonly TimeSpan Timeout = TimeSpan.FromMinutes(2); public async Task ConfirmAsync(string title, string message, CancellationToken ct) { if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) return false; // Die gesamte Warte-/Timeout-Logik läuft als ein Stück innerhalb des UI-Thread-Callbacks: // Avalonias Dispatcher-Synchronisationskontext sorgt dafür, dass die Fortsetzung nach // "await Task.WhenAny(...)" wieder auf dem UI-Thread läuft, sodass dialog.Close() dort // sicher aufgerufen werden kann. return await Dispatcher.UIThread.InvokeAsync(async () => { if (owner.WindowState == WindowState.Minimized) owner.WindowState = WindowState.Normal; owner.Activate(); var dialog = new McpConfirmDialog { DataContext = new McpConfirmDialogInfo { Title = title, Message = message, ConfirmText = "Übernehmen" }, }; var dialogTask = dialog.ShowDialog(owner); var timeoutTask = Task.Delay(Timeout, ct); var completed = await Task.WhenAny(dialogTask, timeoutTask); if (completed != dialogTask) { dialog.Close(false); return false; } return await dialogTask; }); } }