WP11 #8

Merged
Lars merged 30 commits from WP11 into main 2025-12-11 17:00:38 +01:00
Showing only changes of commit 9d792e11ce - Show all commits

View File

@ -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.8", page_icon="🧠", layout="wide")
st.set_page_config(page_title="mindnet v2.3.9", page_icon="🧠", layout="wide")
# --- CSS STYLING ---
st.markdown("""
@ -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.8 | Fixed State Sync")
st.caption("v2.3.9 | Stable ID Fix")
mode = st.radio("Modus", ["💬 Chat", "📝 Manueller Editor"], index=0)
st.divider()
st.subheader("⚙️ Settings")
@ -240,15 +240,23 @@ def render_sidebar():
return mode, top_k, explain
def render_draft_editor(msg):
qid = msg.get('query_id', str(uuid.uuid4()))
# --- 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.
if "query_id" not in msg or not msg["query_id"]:
msg["query_id"] = str(uuid.uuid4())
qid = msg["query_id"]
key_base = f"draft_{qid}"
# State Keys
data_meta_key = f"{key_base}_data_meta"
data_sugg_key = f"{key_base}_data_suggestions"
widget_body_key = f"{key_base}_widget_body"
data_body_key = f"{key_base}_data_body"
# --- 1. INIT STATE (Nur einmalig) ---
# --- 1. INIT STATE (Nur einmalig pro stabiler ID) ---
if f"{key_base}_init" not in st.session_state:
meta, body = parse_markdown_draft(msg["content"])
@ -258,24 +266,32 @@ def render_draft_editor(msg):
tags = meta.get("tags", [])
meta["tags_str"] = ", ".join(tags) if isinstance(tags, list) else str(tags)
# Persistent Data
st.session_state[data_meta_key] = meta
st.session_state[data_sugg_key] = []
st.session_state[data_body_key] = body.strip()
# WICHTIG: Wir initialisieren den Widget Key direkt!
# 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
# --- CALLBACKS (Modifizieren direkt den Widget-Key) ---
# --- CALLBACKS ---
def _sync_body():
# Sync vom Widget zurück in den persistenten Speicher
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}"
# Kein Rerun nötig, Button-Klick macht das implizit
# Sync auch data_body
st.session_state[data_body_key] = st.session_state[widget_body_key]
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]
# --- UI LAYOUT ---
st.markdown(f'<div class="draft-box">', unsafe_allow_html=True)
@ -299,12 +315,13 @@ def render_draft_editor(msg):
# --- TAB 1: EDITOR ---
with tab_edit:
# State-Fix: Wir übergeben KEIN 'value' Argument, wenn der Key existiert.
# So hat das Widget Vorrang vor veraltetem Code.
# Hier kein 'value=' übergeben, wenn der Key im Session State ist.
# Streamlit nimmt automatisch den Wert aus session_state[widget_body_key].
st.text_area(
"Body",
key=widget_body_key,
height=500,
on_change=_sync_body,
label_visibility="collapsed"
)
@ -316,12 +333,9 @@ def render_draft_editor(msg):
# 1. Reset suggestions
st.session_state[data_sugg_key] = []
# 2. Text DIREKT aus dem Widget State lesen
# 2. Text DIREKT aus dem Widget State lesen (das ist der aktuellste Stand im Browser)
text_to_analyze = st.session_state[widget_body_key]
# 3. Debug Output (optional)
# st.info(f"Sende {len(text_to_analyze)} Zeichen an API...")
with st.spinner("Analysiere..."):
analysis = analyze_draft_text(text_to_analyze, meta_ref["type"])
@ -338,7 +352,6 @@ def render_draft_editor(msg):
# Render List
suggestions = st.session_state[data_sugg_key]
if suggestions:
# Hole aktuellen Text für Vergleich
current_text_state = st.session_state[widget_body_key]
for idx, sugg in enumerate(suggestions):
@ -370,7 +383,7 @@ def render_draft_editor(msg):
"status": "draft",
"tags": final_tags
}
# Auch hier: Nimm den aktuellsten Text für die Vorschau
# Nimm den Widget Content
final_body = st.session_state[widget_body_key]
final_doc = build_markdown_doc(final_meta, final_body)
@ -384,11 +397,10 @@ def render_draft_editor(msg):
b1, b2 = st.columns([1, 1])
with b1:
if st.button("💾 Speichern & Indizieren", type="primary", key=f"{key_base}_save"):
with st.spinner("Speichere..."):
with st.spinner("Speichere im Vault..."):
safe_title = re.sub(r'[^a-zA-Z0-9]', '-', meta_ref["title"]).lower()[:30] or "draft"
fname = f"{datetime.now().strftime('%Y%m%d')}-{safe_title}.md"
# Sende das finale Dokument an die API
result = save_draft_to_vault(final_doc, filename=fname)
if "error" in result:
st.error(f"Fehler: {result['error']}")