172 lines
7.1 KiB
Markdown
172 lines
7.1 KiB
Markdown
---
|
|
doc_type: technical_reference
|
|
audience: developer, frontend_architect
|
|
scope: architecture, graph_viz, state_management
|
|
status: active
|
|
version: 2.9.1
|
|
context: "Technische Dokumentation des modularen Streamlit-Frontends, der Graph-Engines und des Editors."
|
|
---
|
|
|
|
# Technical Reference: Frontend & Visualization
|
|
|
|
**Kontext:** Mindnet nutzt Streamlit nicht nur als einfaches UI, sondern als komplexe Applikation mit eigenem State-Management, Routing und Persistenz.
|
|
|
|
---
|
|
|
|
## 1. Architektur & Modularisierung (WP19)
|
|
|
|
Seit Version 2.6 ist das Frontend (`app/frontend/`) kein Monolith mehr, sondern in funktionale Module unterteilt.
|
|
|
|
### 1.1 Dateistruktur & Aufgaben
|
|
|
|
| Modul | Funktion |
|
|
| :--- | :--- |
|
|
| `ui.py` | **Router.** Der Entrypoint. Initialisiert Session-State und entscheidet anhand der Sidebar-Auswahl, welche View gerendert wird. |
|
|
| `ui_config.py` | **Konstanten.** Zentraler Ort für Farben (`GRAPH_COLORS`), API-Endpunkte und Timeouts. |
|
|
| `ui_api.py` | **Backend-Bridge.** Kapselt alle `requests`-Aufrufe an die FastAPI. |
|
|
| `ui_callbacks.py` | **State Transitions.** Logik für View-Wechsel (z.B. Sprung vom Graph in den Editor). |
|
|
| `ui_utils.py` | **Helper.** Markdown-Parsing (`parse_markdown_draft`) und String-Normalisierung. |
|
|
| `ui_graph_service.py`| **Data Logic.** Holt Daten aus Qdrant und bereitet Nodes/Edges auf (unabhängig von der Vis-Library). |
|
|
| `ui_graph_cytoscape.py`| **View: Graph.** Implementierung mit `st-cytoscape` (COSE Layout). |
|
|
| `ui_editor.py` | **View: Editor.** Logik für Drafts, manuelles Editieren und **Async Feedback**. |
|
|
|
|
### 1.2 Konfiguration (`ui_config.py`)
|
|
|
|
Zentrale Steuerung der visuellen Semantik.
|
|
|
|
# Mapping von Node-Typen zu Farben (Hex)
|
|
GRAPH_COLORS = {
|
|
"project": "#ff9f43", # Orange
|
|
"decision": "#5f27cd", # Lila
|
|
"experience": "#feca57", # Gelb (Empathie)
|
|
"concept": "#54a0ff", # Blau
|
|
"risk": "#ff6b6b" # Rot
|
|
}
|
|
|
|
---
|
|
|
|
## 2. Graph Visualisierung
|
|
|
|
Mindnet nutzt primär **Cytoscape** für Stabilität bei großen Graphen.
|
|
|
|
### 2.1 Engine: Cytoscape (`st-cytoscape`)
|
|
* **Algorithmus:** `COSE` (Compound Spring Embedder).
|
|
* **Vorteil:** Verhindert Überlappungen aktiv (`nodeRepulsion`).
|
|
|
|
### 2.2 Architektur-Pattern: "Active Inspector, Passive Graph"
|
|
Ein häufiges Problem bei Streamlit-Graphen ist das "Flackern" (Re-Render) bei Klicks. Wir lösen das durch Entkopplung:
|
|
|
|
1. **Stable Key:** Der React-Key der Komponente hängt *nicht* von der Selektion ab, sondern nur vom Zentrum (`center_id`) und Layout-Settings.
|
|
2. **CSS-Selektion:** Wir nutzen **nicht** den nativen `:selected` State (buggy bei Single-Select), sondern injizieren eine CSS-Klasse `.inspected`.
|
|
|
|
**Stylesheet Implementierung (`ui_graph_cytoscape.py`):**
|
|
|
|
stylesheet = [
|
|
{
|
|
"selector": "node",
|
|
"style": { "background-color": "data(bg_color)" }
|
|
},
|
|
# Wir steuern das Highlight manuell über eine Klasse
|
|
{
|
|
"selector": ".inspected",
|
|
"style": {
|
|
"border-width": 6,
|
|
"border-color": "#FFC300", # Gelb/Gold
|
|
"z-index": 999
|
|
}
|
|
},
|
|
# Native Selektion wird unterdrückt/unsichtbar gemacht
|
|
{
|
|
"selector": "node:selected",
|
|
"style": { "overlay-opacity": 0, "border-width": 0 }
|
|
}
|
|
]
|
|
|
|
---
|
|
|
|
## 3. Editor & Single Source of Truth
|
|
|
|
Ein kritisches Design-Pattern ist der Umgang mit Datenkonsistenz beim Editieren ("File System First").
|
|
|
|
### 3.1 Das Problem
|
|
Qdrant speichert Metadaten und Chunks, aber das Feld `fulltext` im Payload kann veraltet sein oder Formatierungen verlieren.
|
|
|
|
### 3.2 Die Lösung (Logic Flow)
|
|
Der `switch_to_editor_callback` in `ui_callbacks.py` implementiert folgende Kaskade:
|
|
|
|
def switch_to_editor_callback(note_payload):
|
|
# 1. Pfad aus Qdrant Payload lesen
|
|
origin_fname = note_payload.get('path')
|
|
|
|
content = ""
|
|
# 2. Versuch: Hard Read von der Festplatte (Source of Truth)
|
|
if origin_fname and os.path.exists(origin_fname):
|
|
with open(origin_fname, "r", encoding="utf-8") as f:
|
|
content = f.read()
|
|
else:
|
|
# 3. Fallback: Rekonstruktion aus der DB ("Stitching")
|
|
# Nur Notfall, falls Docker-Volume fehlt
|
|
content = note_payload.get('fulltext', '')
|
|
|
|
# State setzen (Transport via Message-Bus)
|
|
st.session_state.messages.append({
|
|
"role": "assistant",
|
|
"intent": "INTERVIEW",
|
|
"content": content,
|
|
"origin_filename": origin_fname
|
|
})
|
|
st.session_state["sidebar_mode_selection"] = "📝 Manueller Editor"
|
|
|
|
### 3.3 Async Save Pattern (Neu in v2.7 / WP-14)
|
|
Um Timeouts bei der Smart-Edge-Berechnung zu vermeiden, nutzt der Editor ein **"Fire & Forget"** Muster.
|
|
|
|
1. **Request:** UI sendet Markdown an `/ingest/save`.
|
|
2. **Backend:**
|
|
* Validiert Request.
|
|
* Speichert Datei auf Disk (Persistenz garantiert).
|
|
* Startet `BackgroundTasks` für LLM-Analyse und Embedding.
|
|
* Returniert sofort `status: queued`.
|
|
3. **UI Feedback:**
|
|
* Editor zeigt "Erfolgreich eingereiht".
|
|
* User muss nicht warten.
|
|
* (ToDo: WebSocket Notification bei Abschluss).
|
|
|
|
---
|
|
|
|
## 4. State Management Patterns
|
|
|
|
### 4.1 URL Persistenz (Deep Linking)
|
|
Layout-Einstellungen werden in der URL gespeichert, damit sie einen Page-Refresh (F5) überleben.
|
|
|
|
# ui_graph_cytoscape.py
|
|
def update_url_params():
|
|
st.query_params["depth"] = st.session_state.cy_depth
|
|
st.query_params["rep"] = st.session_state.cy_node_repulsion
|
|
|
|
# Init
|
|
if "cy_depth" not in st.session_state:
|
|
st.session_state.cy_depth = int(st.query_params.get("depth", 2))
|
|
|
|
### 4.2 Resurrection Pattern
|
|
Verhindert Datenverlust, wenn der Nutzer während des Tippens den Tab wechselt. Der Editor-Inhalt wird bei jedem Keystroke (`on_change`) in `st.session_state` gespiegelt und beim Neuladen der Komponente von dort wiederhergestellt.
|
|
|
|
### 4.3 Healing Parser (`ui_utils.py`)
|
|
Das LLM liefert oft invalides YAML oder Markdown. Der Parser (`parse_markdown_draft`):
|
|
* Repariert fehlende Frontmatter-Trenner (`---`).
|
|
* Extrahiert JSON/YAML aus Code-Blöcken.
|
|
* Normalisiert Tags (entfernt `#`).
|
|
|
|
---
|
|
|
|
## 5. Constraints & Security (Known Limitations)
|
|
|
|
### 5.1 File System Security
|
|
Der Editor ("File System First") vertraut dem Pfad im Qdrant-Feld `path`.
|
|
* **Risiko:** Path Traversal (z.B. `../../etc/passwd`).
|
|
* **Mitigation:** Aktuell findet keine strikte Prüfung statt, ob der Pfad innerhalb des `./vault` Ordners liegt. Das System setzt voraus, dass die Vektor-Datenbank eine **Trusted Source** ist und nur vom internen Importer befüllt wird.
|
|
* **ToDo:** Bei Öffnung der API für Dritte muss hier eine `Path.resolve().is_relative_to(VAULT_ROOT)` Prüfung implementiert werden.
|
|
|
|
### 5.2 Browser Performance
|
|
Die Graph-Visualisierung (`st-cytoscape`) rendert Client-seitig im Browser.
|
|
* **Limit:** Ab ca. **500 Knoten/Kanten** kann das Rendering träge werden.
|
|
* **Design-Entscheidung:** Das UI ist auf **Ego-Graphen** (Nachbarn eines Knotens) und gefilterte Ansichten ausgelegt, nicht auf die Darstellung des gesamten Knowledge-Graphs ("Whole Brain Visualization"). |