--- 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").