docs: ADR-0022 chat extensions, roadmap phase 11b, architecture

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
JannisandClaude Sonnet 5.5 committed 2026-10-10 19:06:51 +02:00
1 parent 5e67075f8a
commit 4d9842f9bd
4 files changed
+90 -7

No files matched your search

+4 -4
View File
@@ -80,13 +80,13 @@ Alle Zeitpunkte werden als UTC (`DateTimeOffset`/`DateTime` UTC) gespeichert und
| Bereich | Entitäten |
|---|---|
| Identity ✅ | `AppUser` (DisplayName, Color, AvatarUrl, MustChangePassword, IsActive, ExpiresAt, PinHash, LastLoginAt, Soft Delete), `AppRole` (Admin, Familie, Kind, Gast), `UserAvatar` (Bild ≤ 1 MB) |
| Einstellungen ✅ | `FamilySettings` (FamilyName, TimeZoneId, MealPlanDays (7–10), MealTypes, TaskCategories, ChorePointsEnabled, ChildTasksRequireApproval, GuestsCanEditShoppingList, GuestChatEnabled) |
| Einstellungen ✅ | `FamilySettings` (FamilyName, TimeZoneId, MealPlanDays (7–10), MealTypes, TaskCategories, ChorePointsEnabled, ChildTasksRequireApproval, GuestsCanEditShoppingList, GuestChatEnabled, LearningPhotoRetentionDays, AiChatEnabled, AiChatChildrenAllowed) |
| Einkauf ✅ | `ShoppingList` (Name, IsDefault, SortOrder, Soft Delete), `ShoppingItem` (Client-Id, Name, Quantity, Category, IsDone, DoneAt, DoneBy, RowVersion, Soft Delete), `ShoppingSuggestion` (Name, Category, CategoryIsManual, UseCount, LastUsedAt) |
| Essen ✅ | `MealPlanEntry` (Date als `DateOnly`, MealType, DishName, Note, RecipeUrl, Ingredients, WishId, RowVersion, Soft Delete; eindeutig je Tag + Mahlzeit), `Dish` (Name, RecipeUrl, Ingredients, UseCount – für Autovervollständigung), `MealWish` (DishName, Note, Wunschtag/-mahlzeit, Status offen/übernommen/abgelehnt, Soft Delete) |
| Aufgaben ✅ | `TodoTask` (Title, Notes, DueDate + DueTime → DueAt, ReminderMinutesBefore → RemindAt, Priority, Status inkl. „wartet auf Bestätigung“, Category, IsPrivate, IsChore, Points, NeedsApproval, AssignedToFamily, RecurrenceRule + RecurrenceMode, SeriesId/SeriesStart/SeriesIndex, CompletedBy/At, ApprovedBy/At, RowVersion, Soft Delete), `TaskAssignment`, `TaskChecklistItem`, `TaskHistoryEntry` (Verlauf), `PointsLedgerEntry` (Punktebuch). Kategorien kommen aus `FamilySettings.TaskCategories`; „wer/wann/genehmigt“ steht an der Aufgabe und im Verlauf (ADR-0014) |
| Kalender ✅ | `FamilyCalendar` (Name, Color, IsDefault, Soft Delete), `CalendarPermission` (Rolle **oder** Benutzer → None/Read/WriteOwn/Write), `CalendarEvent` (Title, Location, Notes, IsAllDay, StartUtc, EndUtc, TimeZoneId, RecurrenceRule, ExceptionDates, Uid, PersonIds, ForWholeFamily, Sequence, RowVersion, Soft Delete; Overrides als eigene Zeile mit `SeriesMasterId` + `RecurrenceId`), `IcsFeed` (Owner, Scope, CalendarId, TaskMode, TokenHash, TokenPrefix, LastAccessAt, RevokedAt) – ADR-0015/0016 |
| KI ✅ | `AiProviderConfig` (Name, Type Ollama/Claude/OpenAiCompatible, BaseUrl, Model, ApiKeyProtected, TimeoutSeconds, IsActive, AllowImages, Purposes, DailyLimitPerUser, RowVersion, Soft Delete), `AiUsage` (Person, Anbieter, Zweck, Dauer, Tokens, Erfolg/Fehlerart – ohne Inhalt) – ADR-0020 |
| Chat ✅ | `ChatChannel` (Family / Direct), `ChatMember` (LastReadAt), `ChatMessage` (Text, HasImage), `ChatAttachment`, `ChatReaction` |
| KI ✅ | `AiProviderConfig` (Name, Type Ollama/Claude/OpenAiCompatible, BaseUrl, Model, ApiKeyProtected, TimeoutSeconds, IsActive, AllowImages, Purposes, DailyLimitPerUser, RowVersion, Soft Delete), `AiUsage` (Person, Anbieter, Zweck, Dauer, Tokens, Erfolg/Fehlerart – ohne Inhalt) – ADR-0020; Zwecke Standard, Lernaufgaben, Foto-Prüfung, KI-Chat (letztere beiden ohne Rückfall auf den Standard, ADR-0022) |
| Chat ✅ | `ChatChannel` (Family / Direct / Ai – privater KI-Kanal je Person, `DirectKey = ai:{userId}`), `ChatMember` (LastReadAt), `ChatMessage` (Text, HasImage; KI-Antworten mit fester Absender-Id `ChatRules.AiSenderId`, kein Benutzerkonto), `ChatAttachment` (PNG/JPEG/WebP; GIF nur aus der Server-GIF-Suche), `ChatReaction` – ADR-0019, ADR-0022 |
| Lernen ✅ (10a) | `LearningSet` (Titel, Fach Mathe/Deutsch, Klassenstufe 1–6, Zielkind, Fälligkeit, Status, Modus Übung/Prüfung, MaxAttempts, TimeLimitMinutes, ShowSolutionsAfterSubmit, ShuffleItems, TotalPoints, GradeThresholds, Source Manual/Ai + AiPrompt/AiProvider/AiModel (von 10b für KI-Entwürfe gesetzt, nie für Kinder sichtbar), RowVersion, Soft Delete), `LearningItem` (Frage, Antworttyp Number/Text/SingleChoice, Paper vorgesehen, CorrectAnswer, NumberTolerance, Options, Hint, Explanation, Difficulty, Points, RowVersion, Soft Delete), `LearningAttempt` (Nummer, Status, StartedAt, DeadlineAt, SubmittedAt, AutoSubmitted, ItemOrder, Ergebnis/Note, RowVersion), `LearningAnswer` (Antwort, Verdict Open/Correct/Partial/Wrong, Grader Auto/Ai/Parent, AwardedPoints, Reason, AiConfidence, PhotoPurgedAt), `LearningPhoto` ✅ (10c: Foto der Papierlösung je Versuch und Aufgabe, Bytes in der Datenbank); `FamilySettings.LearningPhotoRetentionDays`; `PointsLedgerEntry.LearningSetId` – ADR-0021 |
| Betrieb | `AuditLogEntry` ✅ (ActorName, Action, TargetType/Id/Name, Details, CreatedAt), `PushSubscription` ✅ (UserId, Endpoint + Hash, P256dh, Auth, LastSuccessAt, FailureCount) |
@@ -154,7 +154,7 @@ Das UI bekommt die wirksamen Berechtigungen über `/api/auth/me` und blendet anh
| Kalender-Abos ✅ | `GET/POST /api/calendar/feeds`, `POST /api/calendar/feeds/{id}/regenerate`, `DELETE /api/calendar/feeds/{id}`; Admin: `GET/DELETE /api/admin/calendar-feeds` |
| Admin – Kalender ✅ | `GET/POST /api/admin/calendars`, `PUT/DELETE /api/admin/calendars/{id}` |
| ICS-Feed | `GET /ics/{token}.ics` (öffentlich, Token-gesichert, ETag/Last-Modified) |
| Chat ✅ | `GET /api/chat/channels`, `POST /api/chat/direct`, `GET /api/chat/channels/{id}/messages?before=&take=`, `POST …/messages`, `POST …/images` (Multipart), `POST …/read`, `DELETE /api/chat/messages/{id}`, `PUT /api/chat/messages/{id}/reactions`, `GET /api/chat/attachments/{id}` (ADR-0019) |
| Chat ✅ | `GET /api/chat/channels`, `POST /api/chat/direct`, `GET /api/chat/channels/{id}/messages?before=&take=`, `POST …/messages`, `POST …/images` (Multipart), `POST …/read`, `DELETE /api/chat/messages/{id}`, `PUT /api/chat/messages/{id}/reactions`, `GET /api/chat/attachments/{id}` (ADR-0019); Erweiterungen (ADR-0022): `GET /api/chat/features`, `POST /api/chat/ai` (eigener KI-Kanal, get-or-create), `GET /api/chat/gifs?q=`, `GET /api/chat/gifs/{id}/preview`, `POST …/channels/{id}/gifs`; Konfiguration `Giphy__ApiKey` (leer = keine GIF-Suche) |
| KI ✅ | Admin: `GET/POST /api/admin/ai/providers`, `PUT/DELETE …/{id}`, `POST …/{id}/test`, `GET /api/admin/ai/usage`; Eltern: `GET /api/ai/status` (ADR-0020) |
| Lernen ✅ (10a) | Eltern (`LearningManage`): `GET/POST /api/learning/sets`, `GET/PUT/DELETE …/sets/{id}`, `POST …/sets/{id}/assign`, `POST …/unassign`, `POST …/sets/{id}/items`, `PUT/DELETE …/items/{itemId}`, `POST …/items/order`, `GET /api/learning/review`, `GET /api/learning/attempts/{id}`, `PUT …/attempts/{id}/answers/{itemId}/review`, `POST …/attempts/{id}/confirm`, `POST …/attempts/{id}/reopen`; Kinder (`LearningSolve`): `GET /api/learning/my`, `POST …/my/{setId}/attempts`, `GET …/my/attempts/{id}`, `PUT …/my/attempts/{id}/answers/{itemId}`, `POST …/my/attempts/{id}/submit`; Fotos (10c): `POST/DELETE /api/learning/my/attempts/{id}/items/{itemId}/photo` (Kind), `GET /api/learning/attempts/{id}/items/{itemId}/photo` (Eltern nach Abgabe, Kind; sonst 404), `POST /api/learning/attempts/{id}/ai-grade` (`LearningManage` + `AiUse`, Foto-KI-Prüfung) (ADR-0021) |
| Dashboard ✅ | `GET /api/dashboard` (alle Kacheln der Rolle in einem Request, ADR-0018) |
+6 -3
View File
@@ -140,6 +140,12 @@ Legende: `[x]` erledigt, `[ ]` offen. Nach jeder Phase: Build ohne Warnungen, Te
- [ ] Tests vervollständigen (Policies, ICS, Wiederholungen, Integration)
- [ ] Barrierefreiheit (WCAG 2.1 AA), Performance (AOT/Trimming prüfen, Lazy Loading)
## Phase 11b – Chat-Erweiterungen (ADR-0022)
- [x] Emoji-Auswahl: Button neben „Bild senden“, kleine eingebaute Liste nach Kategorien (ohne Pakete/Dienste), Einfügen an der Schreibmarke (sonst am Ende)
- [x] GIF-/Meme-Suche: Server-Proxy (nur der Suchbegriff geht an Giphy, `rating=g`; auch die Vorschaubilder lädt der Server), Schlüssel aus `Giphy__ApiKey` (Aspire-Parameter/`.env`), ohne Schlüssel kein Button (`GET /api/chat/features`); gesendete GIFs werden nach Host-Whitelist, Größenlimit und Signaturprüfung wie Bilder gespeichert, Uploads bleiben ohne GIF
- [x] KI-Chat-Kanal: `ChatChannelKind.Ai` (privat je Person, Absender „KI“ ohne Benutzerkonto), `AiPurpose.ChatAssistant` ohne Rückfall auf den Standard-Anbieter, Admin-Schalter und Kinder-Freigabe in den Familieneinstellungen (Standard aus/nur Eltern), fester kindgerechter System-Prompt, kurzer Kontext, Tageslimit, Protokoll ohne Inhalt, Fehlermeldungen ohne Anbietertext; Migration `Phase11bChat`
- [x] Tests: Unit (Regeln, Prompt, GIF-Prüfung, Giphy-Adapter mit Stub), bUnit (Emoji, GIF, KI-Kanal), Integration (Rechte je Rolle, Privatsphäre, kein Rückfall, Limit, Fehler, Idempotenz)
## Phase 12 – Deployment
- [ ] Finale `docker-compose.yml` (inkl. optionalem Caddy), `.env.example`
- [ ] Backup-/Restore-Skripte
@@ -159,9 +165,6 @@ Legende: `[x]` erledigt, `[ ]` offen. Nach jeder Phase: Build ohne Warnungen, Te
- Rezeptdatenbank
- Geburtstage und Jahrestage
- Wand-Tablet-Modus (Kiosk-Dashboard)
- Chat: GIF-/Meme-Suche (Anbieter und Datenschutz klären, z. B. nur über den Server abfragen, keine Weitergabe von Kinderdaten)
- Chat: Emoji-Auswahl (Button neben „Bild senden“)
- Chat: KI-Chat als zusätzlicher Kanal (z. B. „KI-Chat“ unter „Familienchat“): Chattext wird zum Prompt, die Antwort erscheint im Verlauf; Admin schaltet ihn ein/aus und legt fest, welche Anbieter/Modelle auswählbar sind (neuer `AiPurpose`, Tageslimit und Datenschutz-Opt-in aus ADR-0020; Kinder-Schutz: Rechte, Inhaltsfilter, Protokoll ohne Inhalt klären)
- Essen: Wunschliste – Wünsche und bereits hinterlegte Gerichte landen in einer dauerhaften, durchsuchbaren Wunschliste und lassen sich jederzeit wieder in den Plan übernehmen
- Lernen: tolerantere Antwort-Auswertung für Zahlen mit Einheiten und Schreibweisen (z. B. „1€ 30 Cent“ = „1,30“, „2 Euro 50“); unklare Fälle optional zur Gegenprüfung an die KI (Purpose und Opt-in aus ADR-0020), Ergebnis bleibt Vorschlag, Eltern bestätigen
- Lernen: Kind sieht die Note auch auf dem Lernzettel selbst (Kopfbereich des Blatts/Ergebnisansicht), nicht nur in der Liste
+69
View File
@@ -0,0 +1,69 @@
# ADR-0022: Chat-Erweiterungen – Emoji-Auswahl, GIF-Suche über den Server, KI-Kanal
* Status: akzeptiert (Phase 11b)
* Datum: 2026-10-10
## Kontext
Der Chat (ADR-0019) soll drei Dinge dazubekommen: eine Emoji-Auswahl, eine GIF-/Meme-Suche und einen KI-Chat. Alle drei berühren den Datenschutz von Kindern.
Die KI-Anbindung (ADR-0020) bleibt die einzige Stelle, die Anbieter aufruft; die Rechtematrix (ADR-0008) bleibt unverändert.
## Entscheidung
### 1. Emoji-Auswahl
* Eingebaute, kategorisierte Liste (`EmojiCatalog`: Gesichter, Hände & Menschen, Tiere & Natur, Essen & Trinken, Spiel & Freizeit, Orte & Dinge) – keine Pakete, keine externen Dienste, keine Bilder.
* Der Button sitzt neben „Bild senden“ und öffnet ein Feld über der Eingabe. Das Emoji wird an der **Schreibmarke** eingefügt (kleine JS-Hilfe `fpChat.insertAtCursor`, löst das `input`-Ereignis aus);
ohne Schreibmarke (oder ohne JS) wird am Ende angehängt. Die Längenbegrenzung (2000 Zeichen) bleibt gewahrt. Die Reaktionen (ADR-0019 Punkt 9) bleiben eine feste Auswahl.
### 2. GIF-/Meme-Suche – Server-Proxy, GIFs werden gespeichert
* **Anbieter:** Giphy hinter der kleinen Schnittstelle `IGifProvider` (`SearchAsync`, `FetchAsync`); Tests ersetzen sie durch Fakes bzw. einen Stub-HTTP-Handler.
Suche mit `rating=g` und `lang=de`.
* **Datenschutz:** Nur der **Suchbegriff** geht an Giphy (zusammen mit dem Schlüssel der Familie). Weder Name, Konto noch Adresse des Kindes. Auch die **Vorschaubilder** lädt der Server
(`GET /api/chat/gifs/{id}/preview`) – der Browser spricht nie direkt mit Giphy (keine IP-Weitergabe, kein Referrer, keine Cookies von Giphy).
Das Chat-Bild liegt nach dem Senden wie jedes Chatbild in der Datenbank (ADR-0009): kein Nachladen von Fremdservern beim Ansehen.
* **Schlüssel:** Konfiguration `Giphy:ApiKey` (Umgebungsvariable `Giphy__ApiKey`); Aspire-Parameter `giphy-api-key` (secret, Standard leer), in Docker `GIPHY_API_KEY` in der `.env`. Nie im Repository.
Der HttpClient hat kein Anfrage-Protokoll (der Schlüssel steht in Giphys Such-Adresse), Fehler tragen weder Adresse noch Anbietertext.
* **Ohne Schlüssel ist die Funktion aus:** `GET /api/chat/features` liefert `GifSearchAvailable = false`, der „GIF“-Button wird nicht angezeigt; Suche, Vorschau und Senden antworten 404.
* **Sicherheit beim Laden:** Der Client schickt nur eine **Kennung** (3–40 Buchstaben/Ziffern), nie eine Adresse. Der Server baut die Adresse selbst, nur `https` auf den festen Hosts
`api.giphy.com`/`media.giphy.com`, **ohne Weiterleitungen**, mit Zeitlimit und hartem Lesen-Limit (Größe + 1 Byte). Danach prüft `ChatRules.ValidateGif` **Signatur** (`GIF87a`/`GIF89a`) und **Größe** (höchstens 3 MB wie Chatbilder).
Typ und Größe stammen nur aus den Bytes.
* **Hochgeladene Bilder:** `ChatRules.ValidateImage` bleibt unverändert und erlaubt weiterhin **kein** GIF – nur der Server lädt GIFs, über die Suche. Gespeichert wird `image/gif` in `ChatAttachment`;
Anzeige und Löschen wie bei Bildern.
* **Endpunkte:** `GET /api/chat/gifs?q=` (2–50 Zeichen, 10 Minuten im Serverspeicher zwischengespeichert), `GET /api/chat/gifs/{id}/preview`, `POST /api/chat/channels/{id}/gifs` (`{id, gifId, text?}`, idempotent wie Senden).
Rechte: wer den Chat nutzen darf (Familienchat oder Direktnachrichten); Rate Limit je Person (Suche 20/min, Vorschau 200/min), damit niemand den Schlüssel der Familie leerfragt.
Im KI-Kanal gibt es keine GIFs.
* **Verworfen:** GIFs direkt von Giphy im Browser einbetten (IP-/Referrer-Weitergabe an einen Dritten bei jedem Kind); GIFs nur verlinken (Dritt-Aufrufe beim Lesen, Inhalte können sich ändern).
* **Grenze:** Giphy bestimmt die Treffer. „Rating g“ ist ein Filter des Anbieters, keine Garantie. Familien, die das nicht wollen, lassen den Schlüssel leer.
### 3. KI-Kanal
* **Modell:** `ChatChannelKind.Ai`. Je Person genau ein Kanal (`DirectKey = "ai:{userId}"`, eindeutiger Index, gleichzeitige Anlage wird abgefangen), einziges Mitglied ist die Person. Die **KI ist kein Benutzerkonto**:
Nachrichten haben die feste Absender-Id `ChatRules.AiSenderId`, Anzeigename „KI“. Damit taucht sie in keiner Benutzerliste, Zuweisung oder Rangliste auf und kann sich nicht anmelden (es gibt keinen Datensatz).
`ChatMessage.SenderId` hat keinen Fremdschlüssel; der Name wird bei der Anzeige gesetzt.
* **Privat für die Person:** `ChatAccessService.ChannelAsync` liefert den Kanal nur dem Mitglied (und nur solange der KI-Chat für seine Rolle erlaubt ist); alle anderen – auch Administratoren – bekommen **404**, Empfänger für Live-Ereignisse
sind nur das Mitglied, die Kanalliste enthält nur den eigenen Kanal, Administratoren moderieren ihn nicht (`ChatRules.CanDelete`). In der Datenbank liegt der Inhalt natürlich trotzdem (Backup).
* **Wer darf:** Der Admin schaltet den KI-Chat mit `FamilySettings.AiChatEnabled` ein (Standard aus). **Eltern** (Familie/Admin, wie `AiUse`) dürfen ihn dann nutzen; **Kinder** nur, wenn der Admin zusätzlich
`AiChatChildrenAllowed` setzt (Standard aus). Gäste nie. Bewusst **keine neue Matrixzeile und keine Änderung an `AiUse`**: die Kinder-Freigabe gilt nur für den Chat (`ChatRules.CanUseAiChat`);
Lern-KI-Funktionen und `GET /api/ai/status` bleiben Eltern vorbehalten. Wird der Schalter ausgeschaltet, verschwindet der Kanal sofort (Verlauf bleibt gespeichert, wird aber nicht mehr ausgeliefert).
* **Anbieter:** Neuer `AiPurpose.ChatAssistant` (Bit 8, im KI-Tab zuordenbar). Wie bei `LearningImageGrading` gibt es **keinen Rückfall auf den Standard-Anbieter** (`AiRules.FallsBackToDefault`): ein Gespräch mit Kindern
soll nie unbemerkt an einen Anbieter gehen, den der Admin nur „allgemein“ eingerichtet hat. Ohne Zuordnung liefert `POST /api/chat/ai` 409 und der Button fehlt (`ChatFeaturesDto.AiChatAvailable`).
* **Opt-in:** Der Opt-in aus ADR-0020 für Chat bedeutet: (1) Admin-Schalter `AiChatEnabled` (Standard aus), (2) eigene, ausdrückliche Zuordnung eines Anbieters für „KI-Chat“, (3) Kinder nur mit eigener Freigabe.
Bei Cloud-Anbietern verlassen die Texte das Heimnetz; die Einstellungsseite weist darauf hin, `ChatFeaturesDto.AiChatSendsToCloud` steht für Hinweise in der Oberfläche bereit. Bilder gibt es im KI-Kanal nicht
(Bild- und GIF-Senden sind dort abgelehnt, ebenso der Bild-Opt-in).
* **Ablauf:** Die Nachricht der Person wird normal gespeichert und live angezeigt (idempotent über die Client-Id – ein wiederholtes Senden löst **keinen** zweiten KI-Aufruf aus), danach ruft der Server `AiService.RunAsync`
mit dem Zweck `ChatAssistant` und speichert die Antwort als Nachricht der KI; sie kommt über den Live-Hub. Die Antwort wird gekürzt (höchstens 2000 Zeichen, Ausgabe-Budget 600 Tokens), HTML wird nicht interpretiert.
* **Prompt:** Fester, kindgerechter deutscher System-Prompt (`ChatAssistantPrompt.System`: kurz, einfach, freundlich, ehrlich; Hausaufgaben erklären statt nur lösen; Gewalt/Sexualität/Drogen/Selbstverletzung/Hass/gefährliche Anleitungen ablehnen
und auf Eltern verweisen; nie nach persönlichen Daten fragen; Regeln nicht änderbar). Kontext: höchstens die letzten **6** Textnachrichten (nur Text, je höchstens 1000 Zeichen, gleiche Rollen zusammengefasst, beginnt mit einer Nutzer-Nachricht).
Keine Namen, Konten oder Familiendaten (ADR-0020 Punkt 5). Eingabe an die KI höchstens 1000 Zeichen.
* **Limits und Protokoll:** Tageslimit und Nutzungsprotokoll des Anbieters (ADR-0020 Punkt 6) gelten unverändert (Limit je Person und Anbieter); das Protokoll enthält Zweck, Dauer, Tokens, Erfolg/Fehlerart – **nie den Inhalt**.
* **Fehlermeldungen** kommen aus festen Texten je Fehlerart (`ChatAssistantPrompt.ErrorMessage`), nie aus Anbieterantworten. Die Frage bleibt im Verlauf, die Oberfläche zeigt den Hinweis und leert die Eingabe;
es wird keine Fehler-Antwort als Nachricht gespeichert.
## Bewusst nicht umgesetzt
* Inhaltsfilter über den System-Prompt hinaus (keine zweite Moderations-KI), Eltern-Einsicht in Kinder-KI-Gespräche, Streaming der Antwort, Bilder/Fotos im KI-Chat.
* Eigene GIF-Allowlist je Familie, Giphy-Alternativen (Tenor), Zwischenspeicher für Vorschaubilder auf dem Server (der Browser cached `private`).
* Aufräumen alter KI-Gespräche (Backlog „Aufräumen alter Nachrichten“).
## Folgen
* Migration `Phase11bChat`: zwei Spalten in `FamilySettings` (`AiChatEnabled`, `AiChatChildrenAllowed`), sonst keine Schemaänderung (Kanalart und Zweck sind Text bzw. Bitmaske).
* Neue Konfiguration `Giphy__ApiKey`; ohne sie ändert sich für die Familie nichts.
* `ChatChannelKind` hat einen dritten Wert; jede Stelle, die Kanäle nach Art unterscheidet (Zugriff, Empfänger, Senden, Titel, Löschen), behandelt ihn ausdrücklich.
* Integrationstests nutzen nie Giphy oder echte KI-Anbieter (Fake-KI-Server); der GIF-Pfad ist über den Stub-Handler (Unit) und die „Funktion aus“-Pfade (Integration) abgedeckt.
@@ -119,6 +119,17 @@
<MudText Typo="Typo.h6" HtmlTag="h2">@channel.Title</MudText>
</header>
@if (channel.Kind == ChatChannelKind.Ai)
{
<MudAlert Severity="Severity.Info" Dense="true" Class="fp-chat-ai-note">
Nur du siehst dieses Gespräch. Die KI kann sich irren – frag bei wichtigen Dingen deine Eltern.
@if (Store.Features.AiChatSendsToCloud)
{
<span> Deine Nachrichten werden an einen Internet-Dienst gesendet; schreib keine persönlichen Daten.</span>
}
</MudAlert>
}
<div class="fp-chat-messages" id="@ListId" tabindex="0">
@if (Store.HasMore)
{