feat: prominenterer Bestätigungsdialog + move_lesson/delete_lesson (Nutzer-Feedback)

Nach dem ersten Live-Test mit echtem Bridge-Prozess: der bisherige
ConfirmDialog fiel zu wenig auf, wenn LehrerApp im Hintergrund lief
(Normalfall, da der Anstoß vom KI-Client in einem anderen Fenster
kommt). Neuer, eigenständiger McpConfirmDialog statt Änderung am
geteilten ConfirmDialog (hätte alle anderen Aufrufer mitbetroffen):
breiter, auffälliger Kopfbereich, Topmost. AvaloniaMcpConfirmationService
holt das Hauptfenster zusätzlich aus einer möglichen Minimierung und
aktiviert es vor dem Anzeigen.

move_lesson kapselt die bereits vorhandene LessonSchedulingService.Move
(shiftFollowingLessons öffnet eine Lücke für eine neue Stunde, indem
spätere Stunden derselben Einheit mitverschoben werden) - keine neue
Terminlogik, nur Wiederverwendung.

delete_lesson ist eine bewusste, gezielte Ausnahme von "v1 ohne
Lösch-Tools" auf expliziten Nutzerwunsch: eigene
AllowedDestructiveWriteTools-Liste, Destructive=true-Annotation,
Bestätigungstext betont ausdrücklich die fehlende Papierkorb-Deckung
für Lessons.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-12 00:43:59 +02:00
co-authored by Claude Sonnet 5
parent e6c30b0bc7
commit a4733c156b
9 changed files with 308 additions and 13 deletions
@@ -2,15 +2,20 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Threading;
using LehrerApp.Desktop.Views.Shared;
using LehrerApp.Desktop.Views.Mcp;
namespace LehrerApp.Desktop.Services.Mcp;
/// <summary>
/// Produktive <see cref="IMcpConfirmationService"/>-Implementierung: zeigt den bestehenden
/// <see cref="ConfirmDialog"/> (Views/Shared) über dem Hauptfenster an. Der aufrufende Tool-Handler
/// läuft auf einem Hintergrund-Thread (MCP-Pipe-Session in <see cref="McpServerHostedService"/>),
/// deshalb Marshalling über <see cref="Dispatcher.UIThread"/>.
/// Produktive <see cref="IMcpConfirmationService"/>-Implementierung: zeigt <see cref="McpConfirmDialog"/>
/// über dem Hauptfenster an. Der aufrufende Tool-Handler läuft auf einem Hintergrund-Thread
/// (MCP-Pipe-Session in <see cref="McpServerHostedService"/>), deshalb Marshalling über
/// <see cref="Dispatcher.UIThread"/>.
///
/// 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 <see cref="Timeout"/> wird der Dialog automatisch geschlossen und die
@@ -31,9 +36,12 @@ public sealed class AvaloniaMcpConfirmationService : IMcpConfirmationService
// sicher aufgerufen werden kann.
return await Dispatcher.UIThread.InvokeAsync(async () =>
{
var dialog = new ConfirmDialog
if (owner.WindowState == WindowState.Minimized) owner.WindowState = WindowState.Normal;
owner.Activate();
var dialog = new McpConfirmDialog
{
DataContext = new ConfirmDialogInfo { Title = title, Message = message, ConfirmText = "Übernehmen" },
DataContext = new McpConfirmDialogInfo { Title = title, Message = message, ConfirmText = "Übernehmen" },
};
var dialogTask = dialog.ShowDialog<bool>(owner);
var timeoutTask = Task.Delay(Timeout, ct);