Files
pyramid/docs/feature-checklist.md
T
Bernd Steckmeister 25ed765a03 wip: Sicherungs-Commit aller Änderungen seit April + Arbeitsstruktur (CLAUDE.md, ROADMAP.md, PROGRESS.md, Autopilot)
6 Wochen uncommittete Arbeit (Voice-Channels, LiveKit-Manager, Settings-Modal u.v.m.)
als ein WIP-Commit gesichert, damit nichts verloren geht und der Pi den aktuellen
Stand klonen kann. Thematische Aufarbeitung: siehe ROADMAP M0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrAGBxBT6GfPXzeWQ4AXb
2026-07-03 05:47:18 +02:00

425 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Matrix / Element Feature Checklist für Pyramid
*Stand: April 2026 — Pyramid v0.1.x (Early Development)*
*Letzte Tiefenanalyse: 2026-04-29 (Vergleich mit FluffyChat / Element Web)*
---
## Legende
-`[x]` — Implementiert und funktioniert korrekt
- ⚠️ `[~]` — Teilweise / fehlerhaft implementiert (Details in Technische Analyse)
-`[ ]` — Fehlt komplett
- 🔴 P0 — Kritisch (Kernfunktionalität kaputt)
- 🟠 P1 — Hoch (wichtige Funktion defekt oder nicht vorhanden)
- 🟡 P2 — Mittel
- 🔵 P3 — Niedrig / Nice-to-have
---
## Nachrichten (Messaging)
### Kern-Funktionen
- [x] Textnachrichten senden
- [x] Nachrichten empfangen & anzeigen
- [x] Zeitstempel (formatiert, relativ)
- [x] Nachrichtengruppierung (gleicher Sender, <5 Min.)
- [x] Datumstrennlinien zwischen Gruppen
- [x] Verschlüsselte Nachrichten anzeigen (Entschlüsselungsstatus)
- [x] Nachrichtenverlauf paginieren (ältere Nachrichten beim Scrollen laden)
- [x] Ungelesene-Nachrichten-Tracking
- [~] Nachrichtenbearbeitung (Edit-Event erkannt, `getDisplayEvent` wird nicht gerendert) 🟠 P1
- [~] Nachrichtenvorschau bei Reply (Zitat-Preview vorhanden, kein Jump-to-original) 🟡 P2
### Nachrichtenaktionen
- [x] Nachrichtentext kopieren
- [x] Eigene Nachrichten löschen (redact)
- [x] Nachrichten anpinnen / Pinboard anzeigen
- [x] Auf Nachrichten antworten (in-reply-to)
- [ ] Nachrichten weiterleiten
- [ ] Zitat-Styling (visuelle Blockquote-Anzeige)
### Textformatierung (formatted_body)
- [~] Markdown-Rendering — `formatted_body` / `org.matrix.custom.html` wird nicht gesendet und nicht gerendert 🔵 P3
- [ ] Fett (**text**)
- [ ] Kursiv (*text*)
- [ ] Durchgestrichen (~~text~~)
- [ ] Inline-Code (`code`)
- [ ] Code-Block (```code```)
- [ ] Aufzählungslisten
- [ ] Klickbare Links
- [~] @Erwähnungen — Button vorhanden, `onTap: null` 🟡 P2
- [ ] #Kanal-Erwähnungen
- [ ] Spoiler-Tags (||text||)
### Medien & Anhänge
- [x] Dateianhänge senden (mit MIME-Erkennung)
- [x] Bilder senden & inline anzeigen
- [x] Videos senden & inline abspielen
- [x] Audio senden & inline abspielen
- [x] Sticker anzeigen (m.sticker)
- [x] Dateianhang mit Untertitel
- [x] Dateigröße berechnen & anzeigen
- [x] Verschlüsselte Medien entschlüsseln & herunterladen
- [x] Thumbnail-Generierung für Bilder/Videos
- [~] Sprachnachrichten (Struktur vorhanden, Wiedergabe-UI fehlt)
- [ ] Animierte GIFs
- [ ] Link-Vorschau (URL-Unfurling)
- [ ] Dokument-Vorschau (PDF, Office)
### Reaktionen
- [x] Emoji-Reaktionen senden
- [x] Reaktionszähler anzeigen
- [x] 6 häufigste Reaktionen als Schnellauswahl
- [x] Vollständiger Emoji-Picker mit Suche
- [x] Eigene Reaktion entfernen
- [x] Anzeigen wer reagiert hat
- [x] Mehrere Reaktionen pro Nachricht
- [x] Kategoriebasierte Emoji-Organisation
### Tipp-Indikatoren (Typing)
- [~] `room.setTyping()` wird **nirgendwo aufgerufen** — Einstellung existiert, aber Composer sendet nie "tippt…" 🟡 P2
- [ ] "X tippt gerade"-Anzeige empfangen (kein Widget)
- [ ] Timeout nach Tipp-Ende
### Threads
- [~] Thread-Button vorhanden (`onPressed: () {}` — leer) 🔵 P3
- [ ] Thread aufklappen / eigene Ansicht
- [ ] Thread-Benachrichtigungen
- [ ] Thread als gelöst markieren
---
## Lesebestätigungen (Read Receipts)
- [x] Senden von `m.read`-Receipts via `room.setReadMarker()`
- [x] Privacy-Toggle (kein Senden wenn deaktiviert)
- [x] ✓-Icon (Nachricht gesendet/zugestellt) auf eigenen Nachrichten
- [x] ✓✓-Icon (gelesen) in DMs
- [x] Sende-Status (Uhr/Fehler/gesendet/gelesen)
- [~] ✓✓-Logik nur für DMs — in Gruppenräumen wird `receiptState.global.otherUsers` nicht ausgewertet 🟡 P2
- [~] **Off-by-one**: Divider erscheint *über* der Gruppe die das `fullyRead`-Event enthält, nicht darunter 🟡 P2
- [ ] Gruppen-Read-Receipt-Display (Seen-by-Avatare wie in FluffyChat) 🟡 P2
- [ ] Hover-Tooltip "Gelesen von: ..." auf ✓✓
- [ ] **Notification-Cancellation**: `cancelNotification(roomId)` beim Setzen des Read-Markers 🟡 P2
---
## „Neue Nachrichten" Trennlinie
- [x] `_UnreadDivider`-Widget korrekt gestylt
- [x] `_sessionFullyReadId` einmalig beim Öffnen erfasst (stabil für die Session)
- [x] Keine Anzeige wenn Raum bereits gelesen
- [~] **Position falsch** — Divider über der Gruppe mit `fullyRead`-Event statt darunter 🟡 P2
- [ ] **Auto-Scroll zum Divider** beim Öffnen eines Raums mit ungelesenen Nachrichten 🟡 P2
---
## Presence / Zuletzt Online
- [x] `_PresenceLine` im DM-Header mit Live-Updates
- [x] 4s-Debounce gegen Sync-Spike-Fehlmeldungen
- [x] Relative Zeitanzeige (gerade eben / vor Xmin / vor Xh)
- [x] Guard: eigene Presence wird nie angezeigt
- [x] Presence-Dot im Rooms-Panel und Members-Panel
- [x] `setPresence(online/offline)` in den Settings
- [~] **Kein Auto-Heartbeat**`setPresence` nur in Settings, nie periodisch während Nutzung 🔵 P3
- [~] **Kein `WidgetsBindingObserver`** — App-Lifecycle (Hintergrund/Vordergrund) löst kein Presence-Event aus 🔵 P3
- [ ] Presence-Dot in DM-Listen-Items (`room_list_item.dart`) 🔵 P3
---
## Benachrichtigungen
### Local Notifications (App im Vordergrund)
- [x] Android Local Notifications (`flutter_local_notifications`)
- [x] Windows Toast-Notifications (`local_notifier`)
- [x] Windows Taskbar-Badge (Progress-Error-Mode)
- [x] Tap-Handler navigiert zum richtigen Raum (Android)
- [x] Guards: nur fremde Nachrichten, nur aktive Rooms, nur `Membership.join`
- [x] `notificationsBlockedProvider` + Banner in `app_shell.dart`
- [~] **Alle 4 Notification-Prefs werden ignoriert**`notifDesktopProvider`, `notifSoundProvider`, `notifPreviewProvider`, `notifMentionOnlyDmProvider` haben keinen Effekt 🟠 P1
- [~] Android-Grouping ohne Summary-Notification 🔵 P3
### Remote Push (App im Hintergrund / geschlossen)
- [ ] 🔴 **Keine Remote-Push-Integration** — FCM/UnifiedPush fehlt komplett, App muss laufen
- [ ] iOS APNs-Setup
- [ ] macOS-Notifications
- [ ] Android Icon-Badge (Zahlenbadge)
---
## E2EE Verschlüsselungs-Pipeline
### Implementiert
- [x] Vodozemac (Rust-Olm) via Flutter-Isolate — modern, performant
- [x] Bootstrap: SSSS, Cross-Signing, Online-Key-Backup
- [x] `_decryptLegacyEvents` — entschlüsselt ältere Events in-place
- [x] Device-Key-Preload vor erstem Senden
- [x] Key-Verification UI (SAS Emoji, QR-Code, alle States)
- [x] Megolm Session Export/Import (Element-kompatibles Format)
- [x] Encrypted-Attachment-Handling
- [x] `m.fully_read`-Marker beim Timeline-Open
- [x] `timeline.requestKeys(onlineKeyBackupOnly: false)`
### Fehlerhaft / Fehlend
- [~] **"Entschlüsselt…"-Placeholder bleibt dauerhaft** wenn Key fehlt — kein Recovery-Button 🟠 P1
- [~] `loadAllKeysFromRoom` Fehler werden silent geschluckt (`catch (_) {}`) 🟡 P2
- [~] **Kein Incoming-Verification-Request-Handler**`client.onKeyVerificationRequest` wird nicht abonniert 🔴 P0
- [~] **Doppelte Bootstrap-Implementierung** (page vs. dialog) mit leicht inkonsistenter Logik 🔵 P3
- [ ] Device-Trust-Indicator in der Timeline 🔵 P3
- [ ] Unverified-Device-Warning beim Senden 🔵 P3
- [ ] "Schlüssel anfordern"-Button bei dauerhaft verschlüsselten Events 🟠 P1
---
## Suche
- [x] Suchdialog öffnen (UI vorhanden)
- [x] Server-seitige Volltextsuche via Matrix `/search` API
- [x] Filter: Alle / Medien / Personen
- [x] Ergebnisse: Raum + Absender + Body + Zeitstempel
- [x] Keyboard-Navigation in Ergebnissen
- [x] 400ms Debounce
- [ ] Jump-to-Message bei Klick auf Suchergebnis
- [ ] Suche nach Absender filtern
- [ ] Filter nach Datum
- [ ] Suchergebnisse im Chat hervorheben
- [ ] Suchverlauf
---
## Raumverwaltung
### Grundfunktionen
- [x] Raumliste anzeigen (sortiert nach letzter Aktivität)
- [x] Räume beitreten
- [x] Räume verlassen
- [x] Raumname & Avatar anzeigen
- [x] Verschlüsselungsstatus (Schloss-Icon)
- [x] Einladungen annehmen / ablehnen
- [ ] Neuen Raum erstellen (UI-Einstiegspunkt)
- [ ] Raumname bearbeiten
- [ ] Raumthema/Beschreibung bearbeiten
- [ ] Raum-Avatar ändern
- [ ] Raumvisibilität (öffentlich / privat)
- [ ] Verlaufsvisibilität konfigurieren
### Direktnachrichten (DMs)
- [x] 1:1 DM-Räume anzeigen & öffnen
- [x] Presence-Status des Gegenübers anzeigen
- [x] Echtzeit-Presence-Updates
- [x] Sprachanruf aus DM starten
- [x] Videoanruf aus DM starten
- [ ] DM starten (neuen Chat beginnen)
### Mitglieder & Rollen
- [x] Mitgliederliste anzeigen
- [x] Mitgliederanzahl anzeigen
- [x] Avatar & Anzeigename der Mitglieder
- [x] Nutzer in Raum einladen
- [ ] Nutzer aus Raum entfernen (Kick)
- [ ] Nutzer sperren (Ban)
- [ ] Power Levels bearbeiten
---
## Spaces & Gemeinschaften
- [x] Spaces in der Seitenleiste anzeigen
- [x] Zwischen Spaces navigieren
- [x] Space-Avatar & Banner anzeigen
- [x] Räume nach Space filtern
- [x] Unterkategorien (Sub-Spaces)
- [x] Drag-and-drop Kanalreihenfolge für Admins
- [x] Space-Verwaltungsdialog
- [ ] Neuen Space erstellen
- [ ] Räume zum Space hinzufügen / entfernen
- [ ] Space-Entdeckung (öffentliche Spaces)
---
## Sprach- & Videoanrufe (LiveKit)
- [x] Sprachkanal betreten / verlassen
- [x] Mikrofon stummschalten / Deafen
- [x] Teilnehmerliste im Sprachkanal
- [x] Anruf-UI (Vollansicht + Mini-Widget)
- [x] Videoanruf mit Kamera-Toggle
- [ ] Klingelton bei eingehenden Anrufen
- [ ] Anruf annehmen / ablehnen (UI)
- [ ] Anrufdauer-Timer
- [ ] Bildschirmfreigabe
---
## Nutzereinstellungen & Profil
- [x] Hell / Dunkel-Theme, Akzentfarbe, Eckradius, Dichte
- [x] Eigene User-ID, Avatar (Initialen-Fallback), Anzeigename anzeigen
- [ ] Anzeigenamen & Avatar bearbeiten
- [ ] Passwort ändern
- [ ] Sitzungsverwaltung
- [ ] Konto deaktivieren
---
## Auto-Update
- [x] GitHub-Releases als Update-Quelle
- [x] Versions-Vergleich (SemVer), Rate-Limiting, Update-Banner
- [x] "Diese Version überspringen"-Option
- [ ] Automatischer Download & Installation
---
## Plattform-spezifisch
### Desktop
- [x] Native Desktop-App, Tastenkürzel, Drag-and-Drop
- [ ] System-Tray-Integration
- [ ] Fenster-State speichern (Größe, Position)
### Mobile
- [x] Mobile App (Flutter), Drawer-Navigation
- [ ] Touch-optimiertes UI
- [ ] Mobile Push-Benachrichtigungen
---
---
# Technische Tiefenanalyse (2026-04-29)
*Vergleichsbasis: FluffyChat (krille-chan/fluffychat) und Element Web*
*Format: Befund → betroffene Datei:Zeile → Referenz-Implementierung*
---
## IMPLEMENTIERUNGS-BACKLOG (nach Priorität)
### 🔴 P0 — Kritisch
#### [P0-1] Incoming Key-Verification-Request-Handler fehlt
**Problem:** Wenn ein anderes Gerät eine Verifikations-Anfrage (`m.key.verification.request`) an Pyramid schickt, gibt es keinen Listener. Die Anfrage läuft ab, ohne dass der Nutzer sie sieht. Das bedeutet: andere Geräte können Pyramid de facto nicht verifizieren — nur Pyramid kann aktiv verifizieren.
**Betroffene Datei:** `lib/widgets/settings_modal.dart` (kein `onKeyVerificationRequest`-Listener)
**FluffyChat-Referenz:** `lib/pages/key_verification/key_verification_dialog.dart` + `client.onKeyVerificationRequest.stream.listen(...)` in `lib/pages/matrix.dart`
**Fix:** Stream auf `client.onKeyVerificationRequest` in `matrix_client.dart` oder `app_shell.dart` subscriben und `KeyVerificationDialog` öffnen.
---
### 🟠 P1 — Hoch
#### [P1-1] Notification-Prefs werden ignoriert
**Problem:** `notifDesktopProvider`, `notifSoundProvider`, `notifPreviewProvider`, `notifMentionOnlyDmProvider` sind in `settings_prefs.dart` gespeichert, werden in `notification_service.dart/_watchEvents()` aber nie ausgelesen. Notifications erscheinen immer, unabhängig von Nutzereinstellungen.
**Betroffene Datei:** `lib/core/notification_service.dart`, Zeile 145168
**Fix:** In `_watchEvents()` die Prefs vor `showMessageNotification()` prüfen.
#### [P1-2] Edits werden nicht gerendert
**Problem:** `_isVisible()` filtert Edit-Events korrekt heraus, aber `event.getDisplayEvent(timeline)` wird nirgendwo aufgerufen. Nachrichten zeigen immer die Originalversion, auch wenn sie via `m.replace` bearbeitet wurden. Ein `(bearbeitet)`-Label fehlt ebenfalls.
**Betroffene Datei:** `lib/features/chat/message_group.dart` — Event-Rendering-Bereich
**FluffyChat-Referenz:** `event.getDisplayEvent(timeline)` vor dem Rendern jedes Events
**Fix:** Beim Rendern von Events `event.getDisplayEvent(timeline)` statt `event` verwenden; `(bearbeitet)`-Label wenn `event != displayEvent`.
#### [P1-3] Encrypted-Placeholder bleibt dauerhaft — kein Recovery-Button
**Problem:** `EventTypes.Encrypted` zeigt "Nachricht wird entschlüsselt…" dauerhaft wenn die Entschlüsselung scheitert. Es gibt keinen Button zum Anfordern fehlender Keys.
**Betroffene Datei:** `lib/features/chat/message_group.dart`, Zeilen ~408421
**FluffyChat-Referenz:** `_requestMissingKey()` mit `room.client.sendToDeviceEncrypted(...)` / SDK-`requestKey()`
**Fix:** Wenn `event.status == EventStatus.error` oder nach Timeout immer noch `EventTypes.Encrypted`: "Schlüssel anfordern"-Button anzeigen, der `timeline.requestKeys()` aufruft.
---
### 🟡 P2 — Mittel
#### [P2-1] Typing-Indikator: `room.setTyping()` nie aufgerufen
**Problem:** `privacyTypingProvider` existiert, `chat_composer.dart` ruft aber nie `room.setTyping()` auf. Andere Nutzer sehen nie "X tippt gerade…".
**Betroffene Datei:** `lib/features/chat/chat_input.dart` (oder `chat_composer.dart`)
**FluffyChat-Referenz:** `onInputBarChanged` → 2s Debounce → `room.setTyping(true, timeout: 30000)`; bei Senden/Leerfeld: `room.setTyping(false)`
**Fix:** Im Composer-Input `onChanged`-Handler mit Debounce und `room.setTyping()` ergänzen.
#### [P2-2] Typing-Indikator: Kein Display-Widget
**Problem:** Kein Widget das `room.typingUsers` anzeigt.
**Fix:** `_TypingIndicator`-Widget in `chat_view.dart` unter der Nachrichtenliste einfügen, das `room.onUpdate`-Stream abonniert und `room.typingUsers` anzeigt.
#### [P2-3] Unread-Divider: Position off-by-one
**Problem:** Divider erscheint *über* der Gruppe die das `fullyRead`-Event *enthält*, nicht darunter. Das bereits-gelesene Event liegt unter dem "Neue Nachrichten"-Strich.
**Betroffene Datei:** `lib/features/chat/chat_view.dart`, Zeile ~715
**Fix:** Divider nach der Gruppe einsetzen, die das `fullyReadId`-Event enthält, nicht davor.
#### [P2-4] Unread-Divider: Kein Auto-Scroll beim Öffnen
**Problem:** Beim Öffnen eines Raums mit ungelesenen Nachrichten landet die View unten, nicht beim Divider.
**FluffyChat-Referenz:** Initialer `scrollController.jumpTo()` zum Index des `fullyRead`-Events nach dem ersten Frame.
**Fix:** In `initState`/`WidgetsBinding.addPostFrameCallback` zum Divider-Index springen wenn `_sessionHasUnread`.
#### [P2-5] Gruppen-Read-Receipts fehlen
**Problem:** ✓✓-Logik greift nur bei `room.isDirectChat`. In Gruppenräumen sehen eigene Nachrichten nie ✓✓.
**FluffyChat-Referenz:** `SeenByRow` zeigt Avatare aller Leser unter der letzten Nachricht.
**Fix:** Receipt-Prüfung auch in Gruppenräumen aktivieren; ggf. Avatar-Reihe unter letzter Nachricht.
#### [P2-6] Notification-Cancellation beim Lesen fehlt
**Problem:** Android-Notifications bleiben in der Benachrichtigungsleiste stehen, auch wenn der Chat geöffnet und die Nachrichten gelesen wurden.
**FluffyChat-Referenz:** `backgroundPush?.cancelNotification(roomId)` in `_markRead()`
**Fix:** In `_markRead()` in `chat_view.dart` die Android-Notification für `roomId` canceln.
---
### 🔵 P3 — Niedrig
#### [P3-1] Presence: Kein Auto-Heartbeat
**Problem:** `setPresence(online)` nur in Settings — Server setzt Client nach Timeout offline.
**Fix:** Periodischer Timer (alle 60s) der `setPresence(online)` sendet solange App im Vordergrund.
#### [P3-2] Presence: Kein App-Lifecycle-Hook
**Problem:** Kein `WidgetsBindingObserver` — kein `unavailable` bei App-Hintergrund.
**Fix:** `WidgetsBindingObserver.didChangeAppLifecycleState` in `matrix_client.dart` oder `app_shell.dart`.
#### [P3-3] Markdown/`formatted_body` nicht gerendert
**Problem:** `sendTextEvent` sendet nur plain text. Empfangene `formatted_body`-Felder werden nicht gerendert.
**Fix:** Empfang: `flutter_html` oder `flutter_markdown` für `formatted_body`; Senden: simple Markdown-Erkennung im Composer.
#### [P3-4] `requestFuture()` fehlt
**Problem:** Kein Forward-Pagination-Mechanismus. Nach Jump-to-Event in der Vergangenheit kein Weg zurück.
**Fix:** `timeline.requestFuture()` im Scroll-Listener für die Vorwärts-Richtung.
#### [P3-5] `message_bubble.dart` — ungenutzter Code
**Problem:** Veraltete WhatsApp-Style-Implementation koexistiert neben aktiver Discord-Style-Implementierung.
**Fix:** Datei löschen oder auskommentieren.
---
## Statistik
| Bereich | Implementiert | Fehlerhaft | Fehlend |
|---|---|---|---|
| Chat-Mechanik | Solide Basis | Edits, Typing, `requestFuture` | Jump-to-quote, Threads, Edited-Badge |
| Read Receipts | DM-Logik ✓ | Gruppen, Off-by-one, Notif-Cancel | Seen-by-Avatare, Tooltip |
| Presence | Anzeige ✓ | Broadcasting, Lifecycle | Heartbeat, Dot in Raumliste |
| Benachrichtigungen | Local ✓ | Prefs ignoriert, Grouping | Remote Push, iOS, macOS |
| Unread-Divider | Konzept ✓ | Position, Auto-Scroll | — |
| E2EE | Stärkster Bereich | Incoming-Verif., Placeholder | Device-Trust-Indicator, Warn beim Senden |
| Suche | API-Basis ✓ | — | Jump-to-message, Filter |
**Implementierungsgrad gesamt: ~42 %** (inkl. Teilimplementierungen)
---
## Implementierungs-Fortschritt
| ID | Beschreibung | Status |
|---|---|---|
| P0-1 | Incoming Key-Verification-Request-Handler | ✅ War bereits implementiert (`app_shell.dart`) |
| P1-1 | Notification-Prefs auswerten | ✅ Implementiert (`notification_service.dart`) |
| P1-2 | Edits rendern (`getDisplayEvent`) | ✅ Implementiert (`message_group.dart`) |
| P1-3 | Encrypted-Placeholder + Recovery-Button | ✅ Implementiert (`message_group.dart`) |
| P2-1 | Typing: `room.setTyping()` senden | ✅ Implementiert (`chat_composer.dart`) |
| P2-2 | Typing: Display-Widget | ✅ Implementiert (`chat_view.dart``_TypingIndicator`) |
| P2-3 | Unread-Divider: Position + leere fullyRead-Guard | ✅ Neu implementiert (`chat_view.dart`) |
| P2-4 | Unread-Divider: Auto-Scroll (Schätzung) | ✅ Implementiert (`_scrollToUnread`) |
| P2-5 | Gruppen-Read-Receipts (DM-Guard entfernt) | ✅ Implementiert (`chat_view.dart`) |
| P2-6 | Notification-Cancellation beim Lesen | ✅ Implementiert (`chat_view.dart/_markRead`) |
| P3-1 | Presence: Auto-Heartbeat (10s) | ✅ Implementiert (`app_shell.dart`) |
| P3-2 | Presence: App-Lifecycle-Hook | ✅ Implementiert (`app_shell.dart``WidgetsBindingObserver`) |
| P3-3 | HTML/Markdown (`_MatrixHtmlText`, `_PlainLinkText`) | ✅ Implementiert (`message_group.dart`) |
| P3-4 | `requestFuture()` | ✅ Implementiert (`chat_view.dart/_onScroll`) |
| P3-5 | `message_bubble.dart` entfernen | ⏳ Offen |