From c6ccad1d1817bd5d6984511ee2dd50ddf65f2693 Mon Sep 17 00:00:00 2001 From: Lars Date: Sun, 14 Dec 2025 11:49:13 +0100 Subject: [PATCH] bug fixes --- app/frontend/ui_components.py | 68 ++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/app/frontend/ui_components.py b/app/frontend/ui_components.py index 742063e..733c778 100644 --- a/app/frontend/ui_components.py +++ b/app/frontend/ui_components.py @@ -9,16 +9,22 @@ from ui_utils import parse_markdown_draft, build_markdown_doc, load_history_from from ui_api import save_draft_to_vault, analyze_draft_text, send_chat_message, submit_feedback from ui_config import HISTORY_FILE, COLLECTION_PREFIX, GRAPH_COLORS -# --- Helper zum Modus-Wechsel --- -def switch_to_editor(note_payload): - """Lädt eine Note in den Editor und wechselt den Tab.""" - # Wir simulieren eine Message, wie sie der Chatbot zurückgeben würde +# --- CALLBACKS --- +# Diese müssen oben definiert sein, damit sie VOR dem Re-Run bekannt sind. + +def switch_to_editor_callback(note_payload): + """ + Callback-Funktion: Wird ausgeführt, wenn der 'Bearbeiten'-Button geklickt wird. + Da dies ein Callback ist, können wir session_state Werte ändern, bevor die UI neu gezeichnet wird. + """ + # 1. Inhalt vorbereiten content = note_payload.get('fulltext', '') if not content: - # Fallback: Wir rekonstruieren minimales Markdown + # Fallback: Markdown aus Metadaten rekonstruieren, falls kein Fulltext da ist content = build_markdown_doc(note_payload, "Inhalt konnte nicht geladen werden (nur Metadaten verfügbar).") - # State setzen für den Editor + # 2. Nachricht simulieren (als ob der Chatbot sie generiert hätte) + # Dies füllt den Editor mit dem Inhalt der Notiz st.session_state.messages.append({ "role": "assistant", "intent": "INTERVIEW", @@ -26,16 +32,19 @@ def switch_to_editor(note_payload): "query_id": f"edit_{note_payload['note_id']}" }) - # Modus umschalten (muss via session_state key im Radio-Widget passieren) + # 3. Modus umschalten + # Das ist der entscheidende Fix: Wir ändern den Wert des Radio-Buttons im State direkt. st.session_state["sidebar_mode_selection"] = "📝 Manueller Editor" - st.rerun() + +# --- UI RENDERER --- def render_sidebar(): with st.sidebar: st.title("🧠 mindnet") st.caption("v2.6 | WP-19 Graph View") - # State-gebundenes Radio Widget für Modus-Wechsel + # State-gebundenes Radio Widget + # Wir nutzen 'sidebar_mode_selection' als Key, damit wir ihn programmgesteuert (Callback) ändern können. if "sidebar_mode_selection" not in st.session_state: st.session_state["sidebar_mode_selection"] = "💬 Chat" @@ -60,7 +69,7 @@ def render_sidebar(): def render_draft_editor(msg): """ - Rendert den Editor für Drafts (genutzt im Chat bei INTERVIEW Intent oder im manuellen Modus). + Der Editor-Kern. Wird sowohl im Chat (Interview-Modus) als auch im manuellen Modus verwendet. """ if "query_id" not in msg or not msg["query_id"]: msg["query_id"] = str(uuid.uuid4()) @@ -296,18 +305,18 @@ def render_manual_editor(): } render_draft_editor(mock_msg) -# --- GRAPH EXPLORER (WP-19) --- +# --- GRAPH EXPLORER --- def render_graph_explorer(graph_service): st.header("🕸️ Graph Explorer") - # State Init für den Graph-Explorer + # State Init if "graph_center_id" not in st.session_state: st.session_state.graph_center_id = None - # Defaults speichern für Persistenz während der Session (mit setdefault) + # Defaults speichern für Persistenz während der Session st.session_state.setdefault("graph_depth", 2) st.session_state.setdefault("graph_show_labels", True) - st.session_state.setdefault("graph_spacing", 200) # Standard etwas höher für mehr Luft + st.session_state.setdefault("graph_spacing", 200) st.session_state.setdefault("graph_gravity", -3000) col_ctrl, col_graph = st.columns([1, 4]) @@ -358,20 +367,21 @@ def render_graph_explorer(graph_service): center_id = st.session_state.graph_center_id if center_id: - # 1. Action Bar für die aktive Node + # Action Bar c_action1, c_action2 = st.columns([3, 1]) with c_action1: st.caption(f"Aktives Zentrum: **{center_id}**") with c_action2: - # Button um die aktuelle Zentrale Note zu editieren - if st.button("📝 Bearbeiten", use_container_width=True): - note_data = graph_service._fetch_note_cached(center_id) - if note_data: - switch_to_editor(note_data) - else: - st.error("Fehler beim Laden der Daten.") + # FIX: Button mit Callback (on_click) + note_data = graph_service._fetch_note_cached(center_id) + if note_data: + st.button("📝 Bearbeiten", + use_container_width=True, + on_click=switch_to_editor_callback, + args=(note_data,)) + else: + st.error("Datenfehler") - # 2. Graph Rendern with st.spinner(f"Lade Graph..."): nodes, edges = graph_service.get_ego_graph( center_id, @@ -382,6 +392,9 @@ def render_graph_explorer(graph_service): if not nodes: st.warning("Keine Daten gefunden. (Notiz existiert evtl. nicht mehr)") else: + # FIX: Dynamischer Key erzwingt Neu-Rendern bei Physics-Änderung + graph_key = f"graph_{center_id}_{st.session_state.graph_gravity}_{st.session_state.graph_spacing}" + config = Config( width=1000, height=800, @@ -395,26 +408,23 @@ def render_graph_explorer(graph_service): solver="forceAtlas2Based", forceAtlas2Based={ "theta": 0.5, - "gravitationalConstant": st.session_state.graph_gravity, # Dynamisch + "gravitationalConstant": st.session_state.graph_gravity, "centralGravity": 0.005, "springConstant": 0.08, - "springLength": st.session_state.graph_spacing, # Dynamisch + "springLength": st.session_state.graph_spacing, "damping": 0.4, "avoidOverlap": 1 }, stabilization={"enabled": True, "iterations": 800} ) - # Interaktion - return_value = agraph(nodes=nodes, edges=edges, config=config) + return_value = agraph(nodes=nodes, edges=edges, config=config, key=graph_key) if return_value: - # Wenn auf eine andere Node geklickt wurde: if return_value != center_id: st.session_state.graph_center_id = return_value st.rerun() else: - # Wenn auf die ZENTRALE Node geklickt wurde st.toast(f"Zentrum: {return_value}") else: