From aba6f0c38be8bc2ba718d8635250ce03364ae61f Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 11 Dec 2025 12:11:55 +0100 Subject: [PATCH] render draft_editor in ui.py --- app/frontend/ui.py | 67 ++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/app/frontend/ui.py b/app/frontend/ui.py index 5f04d9d..2a537bd 100644 --- a/app/frontend/ui.py +++ b/app/frontend/ui.py @@ -242,7 +242,21 @@ def render_sidebar(): def render_draft_editor(msg): qid = msg.get('query_id', str(uuid.uuid4())) key_base = f"draft_{qid}" + body_key = f"{key_base}_txt_body" + # --- CALLBACKS (Lösung für den State-Error) --- + def _append_text(k, text): + current = st.session_state.get(k, "") + st.session_state[k] = f"{current}\n\n{text}" + # Sync auch den generischen Key + st.session_state[f"{key_base}_body"] = st.session_state[k] + + def _remove_text(k, text): + current = st.session_state.get(k, "") + # Einfaches Replace (könnte man robuster machen) + st.session_state[k] = current.replace(text, "").strip() + st.session_state[f"{key_base}_body"] = st.session_state[k] + # 1. Init (Nur beim allerersten Laden) if f"{key_base}_init" not in st.session_state: meta, body = parse_markdown_draft(msg["content"]) @@ -251,7 +265,11 @@ def render_draft_editor(msg): st.session_state[f"{key_base}_title"] = meta.get("title", "") tags_raw = meta.get("tags", []) st.session_state[f"{key_base}_tags"] = ", ".join(tags_raw) if isinstance(tags_raw, list) else str(tags_raw) + + # Initialisiere beide Keys + st.session_state[body_key] = body.strip() st.session_state[f"{key_base}_body"] = body.strip() + st.session_state[f"{key_base}_meta"] = meta st.session_state[f"{key_base}_suggestions"] = [] st.session_state[f"{key_base}_init"] = True @@ -263,7 +281,6 @@ def render_draft_editor(msg): # Metadata c1, c2 = st.columns([2, 1]) with c1: - # Titel immer aus State lesen/schreiben new_title = st.text_input("Titel", key=f"{key_base}_inp_title", value=st.session_state.get(f"{key_base}_title", "")) with c2: known_types = ["concept", "project", "decision", "experience", "journal", "person", "value", "goal", "principle", "default"] @@ -278,15 +295,14 @@ def render_draft_editor(msg): # --- TAB 1: EDITOR --- with tab_edit: - # WICHTIG: Das Text-Area ist an session_state gebunden via 'key'. + # Das Widget rendert HIER. Änderungen am State müssen VORHER (via Callback) passieren. current_body = st.text_area( "Body", - key=f"{key_base}_txt_body", # Master-Key - value=st.session_state.get(f"{key_base}_body", ""), + key=body_key, height=500, label_visibility="collapsed" ) - # Sync zurück zum generischen Key für andere Tabs + # Sync manueller Änderungen in den generischen Key st.session_state[f"{key_base}_body"] = current_body # --- TAB 2: INTELLIGENCE --- @@ -295,8 +311,7 @@ def render_draft_editor(msg): if st.button("🔍 Analyse starten", key=f"{key_base}_analyze"): with st.spinner("Analysiere..."): - # Wir nehmen explizit den Text aus dem Widget-State - text_to_analyze = st.session_state[f"{key_base}_txt_body"] + text_to_analyze = st.session_state[body_key] analysis = analyze_draft_text(text_to_analyze, new_type) if "error" in analysis: @@ -313,8 +328,8 @@ def render_draft_editor(msg): for idx, sugg in enumerate(suggestions): link_text = sugg.get('suggested_markdown', '') - # Check: Ist der Link schon im Text? - is_inserted = link_text in st.session_state[f"{key_base}_txt_body"] + # Prüfe ob Text vorhanden (Case Insensitive Check wäre besser, hier simpel) + is_inserted = link_text in st.session_state[body_key] # Card Styling card_style = "border-left: 3px solid #28a745;" if is_inserted else "border-left: 3px solid #1a73e8;" @@ -328,24 +343,23 @@ def render_draft_editor(msg): """, unsafe_allow_html=True) - # Button Logik (Toggle) + # Button Logik mit CALLBACKS (on_click) if is_inserted: - if st.button(f"❌ Entfernen", key=f"del_{idx}_{key_base}"): - new_text = st.session_state[f"{key_base}_txt_body"].replace(link_text, "").strip() - st.session_state[f"{key_base}_txt_body"] = new_text - st.session_state[f"{key_base}_body"] = new_text - st.rerun() + st.button( + f"❌ Entfernen", + key=f"del_{idx}_{key_base}", + on_click=_remove_text, # Callback + args=(body_key, link_text) # Argumente für Callback + ) else: - if st.button(f"➕ Einfügen", key=f"add_{idx}_{key_base}"): - old_text = st.session_state[f"{key_base}_txt_body"] - new_text = f"{old_text}\n\n{link_text}" - st.session_state[f"{key_base}_txt_body"] = new_text - st.session_state[f"{key_base}_body"] = new_text - st.rerun() + st.button( + f"➕ Einfügen", + key=f"add_{idx}_{key_base}", + on_click=_append_text, # Callback + args=(body_key, link_text) # Argumente für Callback + ) # --- TAB 3: PREVIEW & SAVE --- - - # Reassemble Metadata & Body (Always use latest state) final_tags_list = [t.strip() for t in new_tags.split(",") if t.strip()] final_meta = { "id": "generated_on_save", @@ -355,8 +369,8 @@ def render_draft_editor(msg): "tags": final_tags_list } - # Wir nehmen den aktuellsten Body aus dem State - final_body_content = st.session_state.get(f"{key_base}_txt_body", "") + # Nimm immer den aktuellsten Text aus dem Widget-State + final_body_content = st.session_state[body_key] final_doc = build_markdown_doc(final_meta, final_body_content) with tab_view: @@ -372,9 +386,10 @@ def render_draft_editor(msg): if st.button("💾 Speichern & Indizieren", type="primary", key=f"{key_base}_save"): with st.spinner("Speichere im Vault..."): safe_title = re.sub(r'[^a-zA-Z0-9]', '-', new_title).lower()[:30] + if not safe_title: safe_title = "draft" fname = f"{datetime.now().strftime('%Y%m%d')}-{safe_title}.md" - # Hier der entscheidende Call mit dem aktuellen Dokument + # Speichern mit aktuellstem Inhalt result = save_draft_to_vault(final_doc, filename=fname) if "error" in result: