KI-Feature 4.5.20: Medienvorschlag + Zwischenablage-Prompt für externe Erzeugung

Die KI liefert je Verlaufsplan-Phase optional einen kurzen Vorschlag, was ein
Medium/Material (z.B. Tafelbild) zeigen sollte (AiPhaseStep.MaterialSuggestion),
nur wenn das über das ohnehin genannte Material hinaus einen Mehrwert hätte.
Bewusst nicht ins Domänenmodell übernommen (keine Migration nötig) — nur für
die Review-Anzeige relevant.

AiPlanningService.BuildMaterialPrompt baut daraus rein lokal (kein weiterer
KI-Aufruf) einen vollständigen Prompt aus Fach/Stufe/Gruppe, Einheit, Stunde
und Phase. Im AiAssistDialog landet er per Klick in der Zwischenablage, zum
Einfügen in eine separate Claude-Sitzung für die eigentliche Materialerzeugung.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 21:22:12 +02:00
co-authored by Claude Sonnet 5
parent da62a08437
commit f703fe7b66
8 changed files with 161 additions and 3 deletions
@@ -1085,6 +1085,11 @@ public partial class GenerateLessonSeriesDialogViewModel : ObservableObject
// ── Dialog: KI-gestützte Planungsunterstützung (4.5.9) ───────────────────────
/// Ein kopierbarer Prompt für ein von der KI vorgeschlagenes Material/Medium zu einer Phase (siehe
/// AiPlanningService.BuildMaterialPrompt) — gedacht zum Einfügen in eine separate Claude-Sitzung,
/// nicht über das eigene KI-Backend erzeugt.
public record MaterialPromptItem(string PhaseName, string SuggestionText, string PromptText);
/// Eine von der KI vorgeschlagene Stunde in der Prüfliste des Dialogs — angehakt = wird beim
/// "Übernehmen" mit übertragen. <see cref="AiLesson.Id"/> unterscheidet neu/geändert (siehe
/// AiPlanningDtos.cs), <see cref="IsNew"/> steuert hier nur die Anzeige ("Neu"/"Geändert").
@@ -1098,15 +1103,21 @@ public partial class AiLessonReviewItem : ObservableObject
/// Aufzählung fürs UI — leer bei neuen Vorschlägen, da es dort nichts zu vergleichen gibt.
public string DiffText { get; }
/// Medienvorschläge je Phase dieser Lesson (nur Phasen mit gesetztem
/// AiPhaseStep.MaterialSuggestion) — leer, wenn die KI für keine Phase einen Vorschlag hatte.
public IReadOnlyList<MaterialPromptItem> MaterialPrompts { get; }
[ObservableProperty] private bool _accepted = true;
public AiLessonReviewItem(AiLesson source, bool isNew, List<string>? fieldDiffs = null)
public AiLessonReviewItem(AiLesson source, bool isNew, List<string>? fieldDiffs = null,
List<MaterialPromptItem>? materialPrompts = null)
{
Source = source;
IsNew = isNew;
var dateText = source.Date?.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture) ?? "kein Datum";
DisplayLabel = isNew ? $"Neu: {source.Topic} ({dateText})" : $"Geändert: {source.Topic} ({dateText})";
DiffText = fieldDiffs is { Count: > 0 } ? string.Join("\n", fieldDiffs.Select(d => "• " + d)) : "";
MaterialPrompts = materialPrompts ?? [];
}
}
@@ -1168,7 +1179,11 @@ public partial class AiAssistDialogViewModel : ObservableObject
// dem Nutzer eine Auswahl zu zeigen, die ApplyResponse ohnehin verwerfen würde.
if (isExisting && !AllowModifyingExisting) continue;
var fieldDiffs = isExisting ? _aiPlanning.DescribeChanges(existingLessons[l.Id!.Value], l) : null;
ReviewItems.Add(new AiLessonReviewItem(l, isNew: !isExisting, fieldDiffs));
var materialPrompts = l.Phases
.Where(p => !string.IsNullOrWhiteSpace(p.MaterialSuggestion))
.Select(p => new MaterialPromptItem(p.Name, p.MaterialSuggestion!, _aiPlanning.BuildMaterialPrompt(_unit, l, p)))
.ToList();
ReviewItems.Add(new AiLessonReviewItem(l, isNew: !isExisting, fieldDiffs, materialPrompts));
}
Summary = response.Summary;
HasResults = true;