From 00aecf692d604e3a13467998688669adc07bdb66 Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 11 Dec 2025 15:12:30 +0100 Subject: [PATCH] UI Texteditor merkt sich den Inhalt bei Umschalten --- app/frontend/ui.py | 73 +++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/app/frontend/ui.py b/app/frontend/ui.py index b68014c..733bcb4 100644 --- a/app/frontend/ui.py +++ b/app/frontend/ui.py @@ -23,7 +23,7 @@ timeout_setting = os.getenv("MINDNET_API_TIMEOUT") or os.getenv("MINDNET_LLM_TIM API_TIMEOUT = float(timeout_setting) if timeout_setting else 300.0 # --- PAGE SETUP --- -st.set_page_config(page_title="mindnet v2.3.9", page_icon="🧠", layout="wide") +st.set_page_config(page_title="mindnet v2.3.10", page_icon="🧠", layout="wide") # --- CSS STYLING --- st.markdown(""" @@ -207,7 +207,7 @@ def save_draft_to_vault(markdown_content: str, filename: str = None): response = requests.post( INGEST_SAVE_ENDPOINT, json={"markdown_content": markdown_content, "filename": filename}, - timeout=60 # Indizierung kann dauern + timeout=60 ) response.raise_for_status() return response.json() @@ -225,7 +225,7 @@ def submit_feedback(query_id, node_id, score, comment=None): def render_sidebar(): with st.sidebar: st.title("🧠 mindnet") - st.caption("v2.3.9 | Stable ID Fix") + st.caption("v2.3.10 | Mode Switch Fix") mode = st.radio("Modus", ["💬 Chat", "📝 Manueller Editor"], index=0) st.divider() st.subheader("⚙️ Settings") @@ -240,10 +240,7 @@ def render_sidebar(): return mode, top_k, explain def render_draft_editor(msg): - # --- STABLE ID FIX (Der entscheidende Teil) --- - # Wir prüfen, ob die Nachricht schon eine ID hat. Wenn nicht, erzeugen wir eine - # und SPEICHERN sie zurück in das msg-Objekt (das Teil von session_state ist). - # So bleibt die ID über Reruns hinweg identisch. + # Ensure ID Stability if "query_id" not in msg or not msg["query_id"]: msg["query_id"] = str(uuid.uuid4()) @@ -256,42 +253,44 @@ def render_draft_editor(msg): widget_body_key = f"{key_base}_widget_body" data_body_key = f"{key_base}_data_body" - # --- 1. INIT STATE (Nur einmalig pro stabiler ID) --- + # --- 1. INIT STATE (Nur beim allerersten Laden der Message) --- if f"{key_base}_init" not in st.session_state: meta, body = parse_markdown_draft(msg["content"]) - - # Defaults if "type" not in meta: meta["type"] = "default" if "title" not in meta: meta["title"] = "" tags = meta.get("tags", []) meta["tags_str"] = ", ".join(tags) if isinstance(tags, list) else str(tags) - # Persistent Data + # Persistent Data (Source of Truth) st.session_state[data_meta_key] = meta st.session_state[data_sugg_key] = [] st.session_state[data_body_key] = body.strip() - # Widget Init (wichtig: Hier wird der "Default Value" des Widgets gesetzt) - st.session_state[widget_body_key] = body.strip() - st.session_state[f"{key_base}_init"] = True + # --- 2. RESURRECTION FIX (WICHTIG!) --- + # Wenn wir vom Manuellen Editor zurückkommen, wurde der widget_key von Streamlit gelöscht. + # Wir müssen ihn aus dem persistenten data_body_key wiederherstellen. + if widget_body_key not in st.session_state and data_body_key in st.session_state: + st.session_state[widget_body_key] = st.session_state[data_body_key] + # --- CALLBACKS --- def _sync_body(): - # Sync vom Widget zurück in den persistenten Speicher + # Sync Widget -> Data (Source of Truth) st.session_state[data_body_key] = st.session_state[widget_body_key] def _insert_text(text_to_insert): - # Einfügen in Widget State - current = st.session_state[widget_body_key] - st.session_state[widget_body_key] = f"{current}\n\n{text_to_insert}" - # Sync auch data_body - st.session_state[data_body_key] = st.session_state[widget_body_key] + # Insert in Widget Key und Sync Data + current = st.session_state.get(widget_body_key, "") + new_text = f"{current}\n\n{text_to_insert}" + st.session_state[widget_body_key] = new_text + st.session_state[data_body_key] = new_text def _remove_text(text_to_remove): - current = st.session_state[widget_body_key] - st.session_state[widget_body_key] = current.replace(text_to_remove, "").strip() - st.session_state[data_body_key] = st.session_state[widget_body_key] + current = st.session_state.get(widget_body_key, "") + new_text = current.replace(text_to_remove, "").strip() + st.session_state[widget_body_key] = new_text + st.session_state[data_body_key] = new_text # --- UI LAYOUT --- st.markdown(f'
', unsafe_allow_html=True) @@ -301,22 +300,29 @@ def render_draft_editor(msg): meta_ref = st.session_state[data_meta_key] c1, c2 = st.columns([2, 1]) with c1: - meta_ref["title"] = st.text_input("Titel", key=f"{key_base}_wdg_title", value=meta_ref["title"]) + # Auch hier Keys für Widgets nutzen, um Resets zu vermeiden + title_key = f"{key_base}_wdg_title" + if title_key not in st.session_state: st.session_state[title_key] = meta_ref["title"] + meta_ref["title"] = st.text_input("Titel", key=title_key) + with c2: known_types = ["concept", "project", "decision", "experience", "journal", "value", "goal", "principle"] curr = meta_ref["type"] if curr not in known_types: known_types.append(curr) - meta_ref["type"] = st.selectbox("Typ", known_types, index=known_types.index(curr), key=f"{key_base}_wdg_type") + type_key = f"{key_base}_wdg_type" + if type_key not in st.session_state: st.session_state[type_key] = meta_ref["type"] + meta_ref["type"] = st.selectbox("Typ", known_types, index=known_types.index(curr) if curr in known_types else 0, key=type_key) - meta_ref["tags_str"] = st.text_input("Tags", key=f"{key_base}_wdg_tags", value=meta_ref.get("tags_str", "")) + tags_key = f"{key_base}_wdg_tags" + if tags_key not in st.session_state: st.session_state[tags_key] = meta_ref.get("tags_str", "") + meta_ref["tags_str"] = st.text_input("Tags", key=tags_key) # Tabs tab_edit, tab_intel, tab_view = st.tabs(["✏️ Inhalt", "🧠 Intelligence", "👁️ Vorschau"]) # --- TAB 1: EDITOR --- with tab_edit: - # Hier kein 'value=' übergeben, wenn der Key im Session State ist. - # Streamlit nimmt automatisch den Wert aus session_state[widget_body_key]. + # Hier kein 'value' Argument mehr, da wir den Key oben (Resurrection) initialisiert haben. st.text_area( "Body", key=widget_body_key, @@ -330,11 +336,10 @@ def render_draft_editor(msg): st.info("Klicke auf 'Analysieren', um Verknüpfungen für den AKTUELLEN Text zu finden.") if st.button("🔍 Analyse starten", key=f"{key_base}_analyze"): - # 1. Reset suggestions st.session_state[data_sugg_key] = [] - # 2. Text DIREKT aus dem Widget State lesen (das ist der aktuellste Stand im Browser) - text_to_analyze = st.session_state[widget_body_key] + # Lese vom Widget (aktuell) oder Data (Fallback) + text_to_analyze = st.session_state.get(widget_body_key, st.session_state.get(data_body_key, "")) with st.spinner("Analysiere..."): analysis = analyze_draft_text(text_to_analyze, meta_ref["type"]) @@ -352,7 +357,7 @@ def render_draft_editor(msg): # Render List suggestions = st.session_state[data_sugg_key] if suggestions: - current_text_state = st.session_state[widget_body_key] + current_text_state = st.session_state.get(widget_body_key, "") for idx, sugg in enumerate(suggestions): link_text = sugg.get('suggested_markdown', '') @@ -383,8 +388,8 @@ def render_draft_editor(msg): "status": "draft", "tags": final_tags } - # Nimm den Widget Content - final_body = st.session_state[widget_body_key] + # Final Doc aus Data + final_body = st.session_state.get(widget_body_key, st.session_state[data_body_key]) final_doc = build_markdown_doc(final_meta, final_body) with tab_view: