docs: rewrite documentation and README from current codebase, add flaw analysis
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 8s

Remove outdated claims (CRDT, DocRooms, presence, .md files on disk) and
document the actual architecture, API, permissions and known flaws.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Sonnet 5.5 committed 2026-10-27 23:09:05 +01:00
1 parent 4e9c4ffa8c
commit bc13235da6
14 files changed
+733 -1618

No files matched your search

+14 -57
View File
@@ -1,62 +1,19 @@
# 🛒 Kernmodul 4: Geteilte Listen & Kollaboration
# Modul: Geteilte Listen
Das Modul für geteilte Listen ([`SharedListsView.tsx`](../../frontend/src/features/lists/SharedListsView.tsx)) ist auf **Echtzeit-Kollaboration im Alltag** ausgelegt (z.B. gemeinsamer Wocheneinkauf im Supermarkt, Packlisten für Reisen, Inventarlisten oder schnelle Checklisten).
Einkaufs-, Pack- und Checklisten für gemeinsames Arbeiten. Code: [SharedListsView.tsx](../../frontend/src/features/lists/SharedListsView.tsx); Backend: `*List*`-Handler in [server.go](../../internal/api/server.go), `Store.CreateList` und Verwandte.
---
## Datenmodell
## 🛍️ 1. Listen-Typen & Kategorien
- `lists`: Eigentümer, `title`, `icon`, `list_type` (`shopping`, `checklist`, `vacation`).
- `list_items`: `content`, `category`, `quantity`, `is_completed`, `completed_by(_name)`, `sort_order`.
- `list_shares`: Ziel (Benutzer oder Gruppe) und Rolle `editor` oder `viewer`.
Jede Liste besitzt einen Typ, ein Icon und eine Farb-Codierung:
## Funktionen
| Listen-Typ | Icon | Einsatzzweck | Typische Kategorien |
| :--- | :--- | :--- | :--- |
| `shopping` | 🛒 | Supermarkt- & Lebensmitteleinkauf | Obst & Gemüse, Kühlregal, Vorrat, Drogerie |
| `packing` | 🎒 | Urlaubs- & Reise-Packlisten | Kleidung, Elektronik, Dokumente, Kosmetik |
| `inventory` | 📦 | Vorratskammer, Keller, Homelab-Hardware | Regal 1, Gefrierschrank, Server-Rack |
| `todo` | 📝 | Einfache Gruppen-Checklisten | Heute, Später, Ideen |
### Automatische Kategorisierung & Mengenangaben
- Einträge können mit einer Mengenangabe (z.B. `3x`, `500g`, `1 Kasten`) versehen werden.
- Die intelligente Eingabe erkennt Einheiten automatisch oder trennt Mengen über Eingabefelder.
- Einträge werden übersichtlich nach Kategorien gruppiert. Jede Kategorie kann zur besseren Übersicht eingeklappt werden.
---
## ⚡ 2. Echtzeit-Abhaken (Multi-User Live Sync)
Wenn mehrere Familienmitglieder oder Teammitglieder gleichzeitig im Supermarkt unterwegs sind, synchronisiert MindSift jeden Schritt in Echtzeit über WebSockets:
```mermaid
sequenceDiagram
autonumber
actor Alice as Alice (im Markt)
participant WS as WebSocket Hub
actor Bob as Bob (zu Hause)
Alice->>WS: PATCH /items/123 (isCompleted: true)
WS->>Bob: Event: ITEM_TOGGLED (itemId: "123", completedBy: "Alice")
Note over Bob: Eintrag wird sofort durchgestrichen<br/>Grünes Badge "von Alice" erscheint
```
- **Transparenz:** Beim Abhaken speichert MindSift `completed_by_name`. Ein dezentes grünes Pillen-Badge zeigt an, wer den Artikel eingepackt hat.
- **Erledigte Einträge:** Abgehakte Artikel wandern nach unten in einen einklappbaren Bereich „Erledigt".
- **Liste leeren:** Ein Klick auf „Erledigte entfernen" löscht alle abgehakten Einträge (`LIST_CLEARED`) für alle Teilnehmer.
---
## 🖐️ 3. Drag & Drop Umsortierung
Innerhalb einer Liste können Einträge frei per Drag & Drop sortiert werden:
1. **Innerhalb einer Kategorie:** Ändert die Reihenfolge (`sort_order`).
2. **Kategorie-Übergreifend:** Zieht der Benutzer einen Eintrag in eine andere Kategorie (z.B. von „Kühlregal" nach „Obst & Gemüse"), aktualisiert das Frontend optimistisch die Kategorie und sendet das Update ans Backend.
---
## 👥 4. Freigaben & Berechtigungen
Listen können über den einheitlichen Freigabe-Dialog ([`UnifiedShareModal.tsx`](../../frontend/src/components/UnifiedShareModal.tsx)) geteilt werden:
- **Freigabe für Teams:** Alle aktuellen und zukünftigen Mitglieder der gewählten Gruppe erhalten automatisch Zugriff.
- **Freigabe für Einzelbenutzer:** Gezielter Zugriff für einzelne Personen.
- **Rollen:**
- `editor`: Darf Einträge hinzufügen, bearbeiten, abhaken und löschen.
- `viewer`: Nur Lesezugriff (Einträge ansehen, aber nicht verändern).
- **Kategorien per Hashtag:** `Hafermilch #Kühlregal` landet in der Kategorie „Kühlregal“. Kategorien werden alphabetisch als Abschnitte angezeigt; die Eingabe schlägt vorhandene Hashtags vor.
- **Menge:** Feld `quantity` (z. B. `2x`, `500g`).
- **Drag & Drop** ordnet Einträge um; in eine andere Kategorie gezogen, wird der Hashtag im Text angepasst (`items/reorder` mit `category` und `content`).
- **Abhaken** speichert, wer und wann; Erledigte stehen in einem eigenen Bereich und lassen sich per „Erledigte entfernen“ löschen (`clear-completed`).
- **Freigabe** über den Dialog `UnifiedShareModal` (Bearbeiten oder Nur Lesen). Ein Leser sieht Änderungen live, ändert aber nichts.
- **Live-Sync:** Ereignisse `ITEM_*` und `LIST_*` im Raum der Liste ([websocket.md](../api/websocket.md)). Abhaken erscheint bei allen Beteiligten sofort.
- Aus der Triage lassen sich Gedanken direkt als Eintrag in eine Liste übernehmen.
+26 -74
View File
@@ -1,86 +1,38 @@
# 📋 Kernmodul 2: Aufgaben & Kanban-System
# Modul: Aufgaben, Kanban & Alarme
Das Aufgaben-Modul ([`TasksView.tsx`](../../frontend/src/features/triage/TasksView.tsx)) bietet eine flexible Aufgabenverwaltung mit frei konfigurierbaren Listen, Kanban-Boards, Subtask-Checklisten und einem automatisierten Multi-Channel Alarm-System.
Code: [TasksView.tsx](../../frontend/src/features/triage/TasksView.tsx), [TaskDetailModal.tsx](../../frontend/src/features/triage/TaskDetailModal.tsx), [ManageColumnsModal.tsx](../../frontend/src/features/triage/ManageColumnsModal.tsx); Backend: `handleUpdateThought`, `handleReorderTasks`, `*TaskList*`-Handler, `startAlarmTicker`.
---
## Datenmodell
## 🗂️ 1. Datenmodell & Listen-Hierarchie
Eine Aufgabe ist ein `thought` ([database.md](../database.md)) mit `kanbanStatus`, `priority` (`low`/`normal`/`high`/`urgent`), `dueDate` (nur Datum), `notes` (Markdown), `subtasks` (`[{id, title, isCompleted}]`), `tags`, `taskListId` und `sortOrder`. Aufgaben ohne Liste sind persönlich.
Aufgaben basieren technisch auf der Tabelle **`thoughts`** in SQLite. Ein Eintrag gilt als Aufgabe, wenn `status = 'kept'` oder `(status = 'converted' AND converted_type = 'task')` zutrifft.
## Ansichten
```
Benutzer (users)
└── Aufgabenlisten (task_lists)
├── Eigene Listen (z.B. "Privat", "Homelab")
└── Mit Teams geteilte Listen (task_list_shares)
└── Aufgaben (thoughts)
├── Kanban-Status (todo, in_progress, done)
├── Priorität (low, normal, high, urgent)
├── Fälligkeitsdatum (dueDate)
├── Notizen (notes)
└── Subtasks (JSON Array von Teilaufgaben)
```
- **Liste** mit Filtern (offen, erledigt, Papierkorb), Gruppierung (z. B. nach Status oder Fälligkeit), Sortierung und Hashtag-Filter.
- **Kanban:** Spalten pro Liste frei konfigurierbar (Name, Farbe, „Erledigt“-Spalte). Standard: *Zu erledigen*, *In Bearbeitung*, *Wartet auf andere*, *Erledigt*. Eine Karte in eine „Erledigt“-Spalte zu ziehen setzt `isCompleted`.
- Drag & Drop sortiert in der Liste und zwischen Spalten; ein Klick öffnet das Detail-Fenster (Titel, Status, Priorität, Fälligkeit mit Schnellwahl, Liste, Hashtags, Unteraufgaben mit Fortschrittsbalken, Notizen).
- Aufgaben lassen sich in eine Notiz umwandeln („Als Notiz vertiefen“). Aufgaben aus Notiz-Checkboxen (`enhanced-task`) bleiben verknüpft: Abhaken wirkt in beide Richtungen (`SyncDocBlockAndTask`), „In Notiz öffnen“ springt zum Block.
### Listen-Sharing & Avatar-Stack
Aufgabenlisten können für Gruppen/Teams freigegeben werden. Ein Avatar-Stack im Listen-Header und in der Tab-Leiste visualisiert alle Mitglieder, die Zugriff auf die jeweilige Liste haben.
## Aufgabenlisten und Freigabe
---
- Jede Liste hat Eigentümer, Titel, Symbol, Farbe und eigene Kanban-Spalten.
- Freigabe an Gruppen oder Benutzer über den Dialog `UnifiedShareModal` (Stufe pro Freigabe):
- **Bearbeiter:** darf Aufgaben anlegen, ändern, abhaken, verschieben, löschen sowie Liste umbenennen und Spalten ändern.
- **Leser:** sieht alles, kann nichts ändern. Der Server antwortet mit `403`; die Oberfläche blendet Eingaben aus und zeigt „Nur Lesen“.
- Gruppenmitglieder mit mehreren Wegen erhalten die höchste Stufe. Nur der Eigentümer legt Freigaben an oder entfernt sie und kann die Liste löschen.
- Umsetzung: `Store.GetTaskListPermission` liefert `owner`/`editor`/`viewer`/`""`; `ensureTaskListWritable` schützt Schreibpfade.
- Bekannte Lücken: [FLAW-03](../flaws.md) (Umsortieren per ID), [FLAW-06](../flaws.md) (Editor kann weiterteilen).
## 🖥️ 2. Ansichten: Liste vs. Kanban-Board
## Alarme
Der Benutzer kann jederzeit nahtlos zwischen zwei Ansichten umschalten:
Der Ticker läuft jede Minute ([architecture.md](../architecture.md)). Für offene Aufgaben mit `due_date <= heute` und `alarm_sent = 0` sendet er an die Kanäle des Aufgaben-**Eigentümers**:
### 1. Kompakte Listenansicht
- Optimiert für schnelles Abarbeiten und Tastatursteuerung.
- Zeigt Checkbox, Titel, Prioritäts-Badge, Herkunfts-Pille (`Webhook`, `Diktat`), Fälligkeitsdatum und Subtask-Zähler (`2/5`).
- 1-Klick-Prioritäts-Wechsel: Klick auf ein Badge rotiert durch die Prioritäten.
| Kanal | Ziel | Konfiguration |
|---|---|---|
| E-Mail | `alarmEmail` (sonst Konto-Adresse) | SMTP aus Admin-Einstellungen oder Umgebung (Port 465 mit TLS wird direkt unterstützt, sonst STARTTLS über `smtp.SendMail`) |
| Discord | Webhook-URL | Benutzereinstellungen |
| Webhook | beliebige URL (JSON: `event`, `title`, `notes`, `dueDate`, `priority`, `timestamp`) | Benutzereinstellungen |
### 2. Kanban-Board (Drag & Drop)
- Standardmäßig 3 Spalten: **Zu erledigen** (`todo`), **In Arbeit** (`in_progress`) und **Erledigt** (`done`).
- **Freie Spalten-Konfiguration:** Über das Spalten-Menü können Spalten umbenannt, farblich markiert oder neue Spalten hinzugefügt werden.
- **Drag & Drop:** Aufgaben können per HTML5 Drag & Drop zwischen Spalten verschoben werden. Das Backend speichert die relative Position (`sort_order`) und den neuen `kanban_status` automatisch.
Danach wird `alarm_sent = 1` gesetzt, sobald mindestens ein Kanal erfolgreich war.
---
## 🎯 3. Prioritäten & Subtasks
### 4-Stufen-Prioritätensystem
Aufgaben unterstützen vier einheitliche Prioritätsstufen:
- **Niedrig (`low`):** Dezent grau hinterlegt.
- **Normal (`normal`):** Akzentfarbe.
- **Wichtig (`high`):** Gelb hervorgehoben mit Warndreieck (`⚠️ Wichtig`).
- **Dringend (`urgent`):** Rot pulsierend mit Flammen-Icon (`🔥 Dringend`).
### Subtasks (Teilaufgaben)
Jede Aufgabe kann eine hierarchische Checkliste von Subtasks enthalten:
- Gespeichert als JSON in der Spalte `subtasks`.
- Fortschrittsanzeige im Kartenkopf (z.B. `3/5 erledigt`).
- Einzelnes Abhaken synchronisiert direkt in die Datenbank.
---
## ⏰ 4. Multi-Channel Alarm-Scheduler
MindSift enthält einen serverseitigen Hintergrund-Ticker in Go ([`internal/api/server.go`](../../internal/api/server.go)), der fällige Aufgaben proaktiv an externe Kanäle meldet.
```mermaid
flowchart TD
Ticker["Go Background Ticker (alle 30s)"] --> Query["SELECT thoughts WHERE due_date <= now + Vorwarnzeit AND alarm_sent = 0"]
Query --> CheckChannels{"Welche Kanäle sind beim User aktiv?"}
CheckChannels -->|email| SendSMTP["E-Mail via SMTP versenden"]
CheckChannels -->|discord| SendDiscord["Discord Webhook POST"]
CheckChannels -->|webhook| SendCustom["Custom HTTP POST Webhook"]
SendSMTP --> MarkSent["UPDATE thoughts SET alarm_sent = 1"]
SendDiscord --> MarkSent
SendCustom --> MarkSent
```
### Alarm-Konfiguration
1. **Vorwarnzeit:** Einstellbar in Minuten (z.B. 15, 30, 60 Minuten vor Fälligkeit).
2. **Kanal-Multi-Select:** Benutzer können beliebig viele Kanäle parallel aktivieren:
- **E-Mail:** Versand über den in den Admin-Einstellungen hinterlegten SMTP-Server.
- **Discord:** Benachrichtigung mit formatierter Embed-Karte in einen Discord-Kanal.
- **Eigener Webhook:** Beliebige HTTP POST JSON Endpunkte (z.B. Gotify, Ntfy, Home Assistant).
3. **Schutz vor Doppel-Alarmen:** Sobald ein Alarm abgesetzt wurde, wird `alarm_sent = 1` gesetzt. Wird das Datum der Aufgabe später geändert, setzt der Server `alarm_sent = 0` automatisch zurück.
Einschränkungen ([FLAW-12](../flaws.md)): Fälligkeiten haben keine Uhrzeit, `notifyBeforeMinutes` wird ignoriert, die Serverzeit gilt, und ein verschobener Termin löst nicht erneut aus. URLs werden nicht geprüft ([FLAW-05](../flaws.md)).
+25 -77
View File
@@ -1,88 +1,36 @@
# 📥 Kernmodul 1: Triage & Quick-Dump Engine
# Modul: Schnellerfassung & Triage
Das Triage-Modul von MindSift basiert auf dem *Getting Things Done (GTD)*-Prinzip. Es trennt die **Erfassung von Gedanken** strikt von der **Entscheidung, was damit geschehen soll**.
Ziel: Gedanken in Sekunden festhalten und später in Ruhe sortieren. Code: [InstantCaptureBar.tsx](../../frontend/src/features/capture/InstantCaptureBar.tsx), [TriageDeck.tsx](../../frontend/src/features/triage/TriageDeck.tsx), Backend `handleCaptureInbox`, `handleTriageAction` und `Store.UpdateThoughtTriage`.
---
## Erfassung
## 💡 1. Das 2-Phasen-Konzept
- Die Eingabeleiste sitzt im Header; `C` oder `/` fokussiert sie (außer beim Tippen in einem Feld).
- Ziel (`target`): **Posteingang** (`inbox`, Standard), **Direkt-Aufgabe** (`task`), **Listeneintrag** (`list_item`, mit Zielliste) oder **Direkt-Notiz** (`document`).
- Mehrzeilige Eingabe wird unterstützt. Spracheingabe nutzt die Web Speech API des Browsers; Quelle `voice` fügt den Tag `#diktat` an.
- Quelle `webhook` (per API-Key/Webhook) fügt den Tag `#webhook` an. Hashtags im Text werden als Tags extrahiert (`domain.ExtractTags`).
- Per API: `POST /inbox` und `POST /webhooks/ingest` (siehe [rest.md](../api/rest.md)).
1. **Phase 1: Schnelles Erfassen (Instant Dump)**
Ein Gedanke, ein Anruf, eine Idee oder ein Server-Alarm wird ohne Reibung erfasst. Der Benutzer muss in diesem Moment weder ein Projekt auswählen, noch Fälligkeitsdaten eingeben oder Ordner durchsuchen.
2. **Phase 2: Fokussierte Triage (Inbox Zero)**
Zu einem späteren Zeitpunkt (z.B. morgens oder beim Feierabend) sichtet der Benutzer den Stapel im Triage-Deck Karte für Karte und trifft für jeden Eintrag eine eindeutige Entscheidung.
## Triage-Deck
---
Der Posteingang (`status = inbox`) wird Karte für Karte abgearbeitet.
## 🔄 2. Triage State Machine
| Aktion | Desktop | Mobil | Ergebnis |
|---|---|---|---|
| Verwerfen | `H` oder `←` | Wischen links | `status = discarded` (Papierkorb) |
| Zur Aufgabe | `L` oder `→` | Wischen rechts | `kept`/`converted` als Aufgabe |
| Aufgabe mit Termin | `T` | Aktionsmenü | wie oben, mit Fälligkeit |
| In Notiz wandeln | `D` | Aktionsmenü | neues Dokument, Gedanke wird `converted` (`document`) |
| Später | Leertaste | Aktionsmenü | Karte ans Stapelende |
| Menü schließen | `Esc` | – | – |
```mermaid
stateDiagram-v2
[*] --> Capture: Gedanke erfasst (Desktop / Mobile / Webhook)
Capture --> Inbox: Standard (status: 'inbox')
Capture --> Task: Direkt als Aufgabe (target: 'task')
Capture --> Document: Direkt als Notiz (target: 'document')
Capture --> ListItem: Direkt auf Liste (target: 'list_item')
Die Umwandlung in eine Notiz übernimmt Titel, Erledigt-Status, Notizen, Unteraufgaben sowie Fälligkeit und Priorität als eingerückte Blöcke. Eine Umwandlung in einen Listeneintrag legt einen Eintrag in der gewählten Liste an und benachrichtigt deren Teilnehmer.
state Inbox {
[*] --> ActiveCard: Oben auf Stapel
ActiveCard --> Skipped: Leertaste / Später (Ans Ende)
Skipped --> ActiveCard
}
## Zustände eines Gedankens
ActiveCard --> Discarded: Swipe links / H (Wegwerfen)
ActiveCard --> Task: Swipe rechts / L (Als Aufgabe)
ActiveCard --> TaskDated: T (Mit Fälligkeitsdatum)
ActiveCard --> Document: D (In Notiz umwandeln)
ActiveCard --> ListItem: Auf Liste schieben
Discarded --> Trash: status: 'discarded'
Task --> TasksModule: status: 'kept', converted: 'task'
TaskDated --> TasksModule: status: 'kept', converted: 'task', dueDate
Document --> WorkspaceModule: status: 'converted', converted: 'document'
ListItem --> ListsModule: status: 'converted', converted: 'list_item'
```
inbox ──keep──▶ kept ──convert──▶ converted (task | document | list_item)
│ │
└─discard─▶ discarded ──restore──▶ inbox (Papierkorb leeren: DELETE /thoughts/discarded/purge)
```
---
## 🖐️ 3. Steuerung & Gesten (Mobile vs. Desktop)
### Desktop-Bedienung (Tastatur-First)
- `C` oder `/`: Fokussiert sofort die Eingabeleiste oben im Header.
- `Enter`: Speichert den Gedanken sofort im Deck.
- `←` oder `H`: Aktive Karte wegwerfen (`discard`).
- `→` oder `L`: Aktive Karte sofort zur Aufgabe machen (`task`).
- `T`: Datumsauswahl für Termin-Aufgabe öffnen.
- `D`: Aktive Karte in eine vollwertige Workspace-Notiz umwandeln.
- `Leertaste`: Karte überspringen und ans Ende des Stapels schieben („Später").
### Mobile Bedienung (Touch & Swipe)
- **Mittlerer Plus-Button (`+`):** Öffnet das design-angeglichene Bottom-Sheet mit Spracheingabe, 4 Prioritäts-Buttons (`Niedrig`, `Normal`, `Wichtig`, `Dringend`) und Fälligkeitsdatum.
- **Wischgeste nach links (Swipe Left):** Roter Indikator erscheint, Karte wird verworfen.
- **Wischgeste nach rechts (Swipe Right):** Grüner Indikator erscheint, Karte wird sofort zur Aufgabe.
- **Long-Press (Langes Drücken auf Karte):** Öffnet das Schnell-Aktions-Menü (Auf Liste, Mit Termin, Als Notiz, Später).
---
## 🎙️ 4. Spracheingabe (Speech-to-Text)
In der Desktop-Schnelleingabeleiste ([`InstantCaptureBar.tsx`](../../frontend/src/features/capture/InstantCaptureBar.tsx)) sowie im mobilen Triage-Modal ([`App.tsx`](../../frontend/src/App.tsx)) ist ein nativer Browser-Sprachassistent integriert:
- Verwendet die standardisierte `SpeechRecognition` bzw. `webkitSpeechRecognition` API.
- Erkennt Sprache auf Deutsch (`lang: 'de-DE'`).
- Live-Transkription: Gesprochener Text wird in Echtzeit in das Textfeld eingefügt.
- Haptisches Feedback: Vibrieren beim Starten und Stoppen der Aufnahme.
- Markiert erfasste Gedanken automatisch mit dem Herkunfts-Attribut `source: 'voice'` (Diktat-Badge).
---
## ⚡ 5. Webhook-Ingestion & Smarthome-Pille
MindSift kann externe Alarme (Home Assistant, Zigbee2MQTT, n8n, Proxmox, Monitoring) automatisch ins Triage-Deck einspeisen:
### Herkunftserkennung
1. Wird ein Gedanke über `/api/v1/inbox` mit `X-API-Key` oder über `/api/v1/webhooks/ingest` eingeliefert, setzt das Backend automatisch `source = "webhook"`.
2. Im Frontend visualisiert die Triage-Karte eine dezente Webhook-Pille (`<Webhook className="w-3 h-3 text-accent" /> Webhook`).
3. Diese Kennzeichnung bleibt auch erhalten, wenn die Karte später zu einer Aufgabe umgewandelt wird.
### Live-WebSocket Update
Nach erfolgreichem Ingest sendet der Server sofort ein `TASK_CREATED` Event über den WebSocket-Hub. Das geöffnete Triage-Deck fügt die neue Alarm-Karte sofort oben in den Stapel ein – ohne dass der Benutzer die Seite neu laden muss.
Ein Gedanke mit `status = kept` zählt bereits als Aufgabe und erscheint in der Aufgabenansicht.
+28 -93
View File
@@ -1,104 +1,39 @@
# 📝 Kernmodul 3: Workspace Outliner-Engine, Wiki-Links & Pure-DB Storage
# Modul: Workspace (Outliner-Notizen)
Der MindSift Workspace ([`WorkspaceView.tsx`](../../frontend/src/features/workspace/WorkspaceView.tsx)) kombiniert ein **hierarchisches Bullet-Point Notizsystem (inspiriert von Logseq)** mit einem **persönlichen Wiki**, **Live-Kollaboration** und einem **globalen To-Do Rollup Dashboard**.
Code: [WorkspaceView.tsx](../../frontend/src/features/workspace/WorkspaceView.tsx) (Explorer, Tags, To-Do-Rollup), [DocumentEditor.tsx](../../frontend/src/features/workspace/DocumentEditor.tsx), [OutlinerTree.tsx](../../frontend/src/features/workspace/OutlinerTree.tsx), [OutlinerBlock.tsx](../../frontend/src/features/workspace/OutlinerBlock.tsx), [CodeBlockItem.tsx](../../frontend/src/features/workspace/CodeBlockItem.tsx), [LinkModal.tsx](../../frontend/src/features/workspace/LinkModal.tsx); Backend: Dokument-, Ordner- und Tag-Handler, `Store.PatchDocumentBlocks`.
---
## Datenhaltung
## 🌳 1. Die Logseq-Outliner-Engine
- Eine Notiz ist ein Datensatz in `documents` plus geordnete Zeilen in `blocks` (`position` als Kommazahl, `level` für die Einrückung). Alles liegt in SQLite; es gibt keine Dateien auf der Platte.
- Bei jeder Änderung wird `documents.markdown_cache` neu erzeugt; `block_tags` wird aus `#Hashtags` der Blöcke neu aufgebaut.
- Der Export als `.md` erfolgt im Browser (Button im Editor, Blob-Download).
- Blocktypen: Überschriften `h1`–`h3`, `paragraph`/`text`, `bullet`, `todo`, `code`, `callout`, `divider`, `table`, `progress`.
Im Gegensatz zu klassischen Fließtext-Editoren basiert der MindSift Workspace auf einer **flachen, relationalen Liste von Blöcken** (`blocks`-Tabelle in SQLite). Jeder Block ist ein gedanklicher Knotenpunkt mit einer flexiblen Einrückungsebene (`level: 0, 1, 2, ...`) und einer fortlaufenden Fließkommaposition (`position`):
## Editor
```
• Projekt MindSift v2.0 (level 0)
• Architektur-Entscheidung (level 1)
- [x] Database-First Blocks implementieren (level 2)
- [ ] Performance Benchmark mit 10.000 Blöcken (level 2)
• Notizen aus dem Dev-Meeting (level 1)
> Wichtig: Keine verschachtelten SQLite Queries ausführen! (level 2)
```
- **Outliner:** `Enter` neuer Block, `Tab`/`Shift+Tab` ein-/ausrücken, `Shift+Enter` weicher Umbruch, Blöcke ein-/ausklappen.
- **Zoom (Hoisting):** Klick auf den Bullet fokussiert den Ast; die Breadcrumb-Leiste führt zurück.
- **Drag & Drop** verschiebt Blöcke samt Unterästen; auf einen anderen Block gezogen, wird der Block zum Unterpunkt.
- **Slash-Befehle:** `/todo`, `/link`, `/h1`, `/h2`, `/h3`, `/code`, `/callout`, `/divider` (Liste in `OutlinerBlock.tsx`).
- **Links:** `[[Notiztitel]]`, Markdown-Links `[Titel](url)` und reine URLs werden als Token dargestellt; `LinkModal` hilft beim Verknüpfen von Notizen.
- **Einfügen:** Mehrzeiliger Text wird anhand von Markdown-Präfixen und Einrückung in Blöcke zerlegt; Überschriften gruppieren darunterliegenden Text und Code.
- **Speichern:** Der Editor speichert 800 ms nach der letzten Eingabe das gesamte Dokument (`PUT /documents/{id}`). Der schlanke Block-Endpunkt `PATCH /documents/{id}/blocks` wird derzeit vom To-Do-Rollup beim Abhaken genutzt.
### Tastatur-Navigation & Shortcuts
- `Enter`: Erstellt einen neuen Block auf derselben Hierarchieebene.
- `Tab`: Rückt den aktuellen Block ein (wird Kindknoten des vorherigen Blocks).
- `Shift + Tab`: Rückt den Block um eine Ebene aus (verringert die Tiefe).
- `Shift + Enter`: Weicher Zeilenumbruch innerhalb desselben Blocks (z.B. für mehrzeilige Notizen oder Code).
- `Backspace` (auf leerem Block): Löscht den Block sofort. Das Ausrücken erfolgt strikt und kontrolliert über `Shift + Tab`.
- `/` (Slash-Commands): Öffnet das Umwandlungsmenü (`/todo`, `/link`, `/h1`, `/h2`, `/h3`, `/code`, `/callout`, `/quote`, `/divider`).
- **Markdown-Tastatur-Kürzel:** Tippen von `[] ` oder `[ ] ` wandelt den Block sofort in ein Todo um; `# `, `## `, `### ` erzeugt Überschriften.
## Explorer, Tags, To-Dos
### Eingabestabilität & Zombie-Fokus-Schutz (`OutlinerBlock.tsx`)
1. **Echter Fokus-Check:** Remote-Updates (`block_updated`) werden nur dann abgewehrt, wenn der Block tatsächlich aktiv im Betriebssystem editiert wird:
`isFocusedRef.current === true && document.hasFocus()`.
2. **Sofort-Flush bei Fensterwechsel:** Sobald das Browser-Fenster den Fokus verliert (`window.addEventListener('blur', ...)`), wird ein offener 150ms-Debounce sofort gecancelt und der Text augenblicklich per WebSocket übertragen.
3. **Stale Closures eliminiert:** Alle Callbacks werden in `useRef` gehalten, sodass asynchrone Debounce-Timer garantiert die aktuellste Komponenten-Referenz verwenden.
- Ordnerbaum mit Favoriten (`doc_pins`), Verschieben per Drag & Drop (`/documents/{id}/move`, `/folders/move`).
- **Tags:** Alle `#Hashtags` werden aggregiert (`/workspace/tags`). Eine Tag-Seite zeigt alle Fundstellen gruppiert nach Notiz und kann eine eigene Freitext-Notiz tragen (`tag_pages`).
- **To-Do-Rollup:** Die Ansicht „To-Dos“ sammelt alle `todo`-Blöcke aller Notizen, filterbar nach Ordner. Abhaken im Rollup ändert den Block in der Notiz.
- **Enhanced Tasks:** Ein `todo`-Block lässt sich als verknüpfte Aufgabe führen (`/documents/{id}/blocks/{blockId}/enhanced-task`); Erledigt-Status bleibt zwischen Notiz und Aufgabenansicht synchron.
### Zoom-In Hoisting (Fokus-Modus)
Klickt der Benutzer auf den Bullet-Dot (`•`) eines Blocks, blendet der Editor alle übergeordneten und benachbarten Blöcke aus und zoomt **ausschließlich in diesen Ast** hinein:
- Der gewählte Punkt wird zur prominenten Überschrift im Fokus-Modus.
- Eine klickbare Breadcrumb-Leiste oben zeigt den Pfad an (z.B. `Workspace / Projekte / MindSift / Architektur`).
- Ein Klick auf ein beliebiges Breadcrumb-Element zoomt sofort auf die jeweilige Ebene zurück.
## Freigabe
### Drag & Drop von Blöcken
Der Bullet-Dot dient gleichzeitig als Drag-Anfasser. Wird ein Block verschoben, erkennt die Engine automatisch alle untergeordneten Kind-Blöcke (`isDescendantBlock`) und verschiebt den **kompletten Unterbaum** an die neue Zielposition.
- Einzelne Notizen und ganze **geteilte Ordner** lassen sich an Benutzer oder Gruppen geben. Empfänger haben **nur Leserechte**; nur der Eigentümer schreibt ([sharing-and-permissions.md](../sharing-and-permissions.md), [FLAW-17](../flaws.md)).
- Empfänger ordnen geteilte Notizen und Ordner in ihrem eigenen Baum ein (`doc_user_folders`, `workspace_folder_mounts`), ohne dass der Eigentümer etwas davon merkt.
- Änderungen erreichen offene Fenster über `DOC_BLOCKS_PATCHED`/`DOC_UPDATED` ([websocket.md](../api/websocket.md)).
---
## Grenzen
## 🔗 2. Wiki-Links (`[[Link]]`), Web-Links (`/link`) & Hashtags
1. **Interne Wiki-Links:**
- Text in doppelten eckigen Klammern (z.B. `[[Reiseplanung 2026]]`) wird automatisch als klickbare Notiz-Pille gerendert.
- Klick darauf öffnet die verknüpfte Notiz direkt im Editor.
2. **Slash-Befehl `/link` & Link-Modal:**
- `/link` öffnet ein Modal mit zwei Modi:
- **Notiz verknüpfen:** Durchsucht bestehende Notizen und fügt `[[Titel]]` ein.
- **Web-Link:** Eingabe von Titel und URL, fügt Standard-Markdown `[Titel](url)` ein.
3. **Hashtags (`#tag`):**
- Hashtags im Fließtext werden automatisch indiziert (`block_tags`-Tabelle).
- In der linken Leiste listet der Tag-Explorer alle verwendeten Schlagworte auf. Klick auf ein Tag filtert alle Dokumente und Blöcke, die dieses Tag enthalten.
---
## ⚡ 3. Echtzeit-Kollaboration & Automatische Markdown-Kompilierung
MindSift setzt auf eine latenzfreie In-Memory Kollaborations-Architektur ([`internal/ws/docroom.go`](../../internal/ws/docroom.go)):
### Funktionsweise
1. **In-Memory DocRoom pro geöffnetem Dokument:**
- Hält den synchronen Zustand von `title` und `blocks[]` im RAM.
- Granulare Mutations-Events (`block_updated`, `block_created`, `block_deleted`, `block_moved`, `title_updated`).
- Ephemeres Presence-Tracking (`cursor_moved`, `client_left`) zeigt farbige Benutzer-Avatare im Header und Cursors am aktiven Block an.
2. **Echo-Schutz:**
- Jeder Client identifiziert sich mit einer eindeutigen `clientId`.
- Der Server sendet Mutations-Events ausschließlich an andere Clients im selben Raum (`clientId != senderClientId`), um Cursor-Springen und Selbstechos zu verhindern.
### Automatische Markdown-Kompilierung & SQLite Debounce-Flush
```
Eingabe im OutlinerBlock
│
├──▶ WebSocket (block_updated Event)
│ │
│ ▼
│ Go DocRoom: In-Memory Mutation & Broadcast an Peers
│
└──▶ 1.5s Inaktivitäts-Debounce (oder Raum-Exit)
│
▼
SQLite Transaktion:
1. Persistiert Blöcke in `blocks`
2. Regex-Extraktion von #tags in `block_tags`
3. Kompiliert Block-Hierarchie zu Standard-Markdown
und speichert Cache in `documents.markdown_cache`
```
1. **Kompiliertes Markdown:** Jeder Block wird gemäß seiner `level`-Tiefe mit 2 Leerzeichen pro Einrückungsebene als sauberes Markdown formatiert (`documents.markdown_cache`).
2. **Pure Database Architecture:** Alle Daten liegen transaktional und sicher in SQLite. Es werden keine unkontrollierten Dateien auf der Festplatte abgelegt; Notizen können jederzeit per Klick als `.md`-Datei exportiert werden.
---
## 📊 4. Globaler To-Do Rollup Dashboard
Über den Tab **„Globale To-Dos"** bietet der Workspace eine aggregierte Übersicht aller im gesamten Wiki verteilten Aufgaben:
- Scannt in Echtzeit alle unvollendeten `- [ ] ` Checkboxen über alle Ordner und Notizen hinweg.
- Übersichtliche Gruppierung nach **Ordner ➔ Dokument ➔ Aufgabe**.
- **Ordner-Filter:** Klick auf einen Ordner isoliert nur dessen Aufgaben für fokussiertes Arbeiten.
- **2-Wege-Live-Sync:** Wird eine Checkbox im Rollup abgehakt, wird der Block in SQLite sofort auf `checked = 1` aktualisiert und via WebSocket an alle aktiven Clients gebroadcastet.
- Keine gleichzeitige Mehrbenutzerbearbeitung, keine Cursor-Anzeige, keine Versionshistorie.
- Keine Virtualisierung langer Dokumente; sehr große Notizen können auf Mobilgeräten träge werden *(nicht gemessen)*.
- Suche (`/search`) erfasst nur Titel und Inhalt eigener Notizen ([FLAW-18](../flaws.md)).