bug fix
This commit is contained in:
parent
c6ccad1d18
commit
ccc848f2e2
|
|
@ -10,30 +10,33 @@ from ui_api import save_draft_to_vault, analyze_draft_text, send_chat_message, s
|
|||
from ui_config import HISTORY_FILE, COLLECTION_PREFIX, GRAPH_COLORS
|
||||
|
||||
# --- 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.
|
||||
Lädt eine Note in den Editor.
|
||||
Versucht, den Original-Dateinamen zu erraten oder zu finden, um Duplikate zu vermeiden.
|
||||
"""
|
||||
# 1. Inhalt vorbereiten
|
||||
# 1. Inhalt holen
|
||||
content = note_payload.get('fulltext', '')
|
||||
if not content:
|
||||
# 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).")
|
||||
|
||||
# 2. Nachricht simulieren (als ob der Chatbot sie generiert hätte)
|
||||
# Dies füllt den Editor mit dem Inhalt der Notiz
|
||||
# 2. Dateinamen-Heuristik (Single Source of Truth)
|
||||
# Idealfall: Qdrant hat das Feld 'file_path' oder 'filename' gespeichert.
|
||||
# Fallback: Wir nutzen die note_id oder den Titel, müssen aber beim Speichern aufpassen.
|
||||
origin_fname = note_payload.get('file_path') or note_payload.get('filename')
|
||||
|
||||
# Nachricht simulieren, die Daten in den Editor trägt
|
||||
st.session_state.messages.append({
|
||||
"role": "assistant",
|
||||
"intent": "INTERVIEW",
|
||||
"content": content,
|
||||
"query_id": f"edit_{note_payload['note_id']}"
|
||||
"query_id": f"edit_{note_payload['note_id']}",
|
||||
"origin_filename": origin_fname, # WICHTIG: Pfad mitschleifen
|
||||
"origin_note_id": note_payload['note_id'] # ID für Fallback mitschleifen
|
||||
})
|
||||
|
||||
# 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"
|
||||
|
||||
# --- UI RENDERER ---
|
||||
|
|
@ -44,7 +47,6 @@ def render_sidebar():
|
|||
st.caption("v2.6 | WP-19 Graph View")
|
||||
|
||||
# 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"
|
||||
|
||||
|
|
@ -69,7 +71,7 @@ def render_sidebar():
|
|||
|
||||
def render_draft_editor(msg):
|
||||
"""
|
||||
Der Editor-Kern. Wird sowohl im Chat (Interview-Modus) als auch im manuellen Modus verwendet.
|
||||
Smart Editor: Unterscheidet zwischen 'Neu' und 'Update'.
|
||||
"""
|
||||
if "query_id" not in msg or not msg["query_id"]:
|
||||
msg["query_id"] = str(uuid.uuid4())
|
||||
|
|
@ -77,7 +79,7 @@ def render_draft_editor(msg):
|
|||
qid = msg["query_id"]
|
||||
key_base = f"draft_{qid}"
|
||||
|
||||
# State Keys für Persistenz
|
||||
# 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"
|
||||
|
|
@ -85,27 +87,40 @@ def render_draft_editor(msg):
|
|||
|
||||
# --- INIT STATE ---
|
||||
if f"{key_base}_init" not in st.session_state:
|
||||
# Metadaten parsen
|
||||
meta, body = parse_markdown_draft(msg["content"])
|
||||
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)
|
||||
|
||||
# Daten in Session State laden
|
||||
st.session_state[data_meta_key] = meta
|
||||
st.session_state[data_sugg_key] = []
|
||||
st.session_state[data_body_key] = body.strip()
|
||||
|
||||
# Widget States initialisieren
|
||||
# Widget States
|
||||
st.session_state[f"{key_base}_wdg_title"] = meta["title"]
|
||||
st.session_state[f"{key_base}_wdg_type"] = meta["type"]
|
||||
st.session_state[f"{key_base}_wdg_tags"] = meta["tags_str"]
|
||||
|
||||
# --- EDITOR LOGIK: Origin Filename ---
|
||||
# Wir speichern den Original-Namen im State, um beim Speichern zu wissen, ob wir überschreiben müssen.
|
||||
origin_file = msg.get("origin_filename")
|
||||
if not origin_file and "origin_note_id" in msg:
|
||||
# Fallback: Wenn wir keinen Pfad haben, aber eine ID, merken wir uns diese,
|
||||
# um später ggf. intelligent zu speichern (z.B. {id}.md suchen)
|
||||
# Hier vereinfacht: Wir setzen es erstmal auf None, User muss aufpassen.
|
||||
pass
|
||||
|
||||
st.session_state[f"{key_base}_origin_filename"] = origin_file
|
||||
st.session_state[f"{key_base}_init"] = True
|
||||
|
||||
# --- STATE RESURRECTION ---
|
||||
# --- RESURRECTION ---
|
||||
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 ---
|
||||
# --- SYNC FUNCTIONS ---
|
||||
def _sync_meta():
|
||||
meta = st.session_state[data_meta_key]
|
||||
meta["title"] = st.session_state.get(f"{key_base}_wdg_title", "")
|
||||
|
|
@ -129,11 +144,19 @@ def render_draft_editor(msg):
|
|||
st.session_state[data_body_key] = new_text
|
||||
|
||||
# --- UI LAYOUT ---
|
||||
st.markdown(f'<div class="draft-box">', unsafe_allow_html=True)
|
||||
st.markdown("### 📝 Entwurf bearbeiten")
|
||||
|
||||
# Header: Status anzeigen
|
||||
origin_fname = st.session_state.get(f"{key_base}_origin_filename")
|
||||
if origin_fname:
|
||||
st.info(f"📝 Bearbeitungs-Modus: Du editierst **{origin_fname}**")
|
||||
st.markdown(f'<div class="draft-box" style="border-left: 5px solid #ff9f43;">', unsafe_allow_html=True)
|
||||
else:
|
||||
st.info("✨ Neuer Entwurf (Wird als neue Datei angelegt)")
|
||||
st.markdown(f'<div class="draft-box">', unsafe_allow_html=True)
|
||||
|
||||
st.markdown("### Editor")
|
||||
|
||||
meta_ref = st.session_state[data_meta_key]
|
||||
|
||||
c1, c2 = st.columns([2, 1])
|
||||
with c1:
|
||||
st.text_input("Titel", key=f"{key_base}_wdg_title", on_change=_sync_meta)
|
||||
|
|
@ -174,7 +197,6 @@ def render_draft_editor(msg):
|
|||
for idx, sugg in enumerate(suggestions):
|
||||
link_text = sugg.get('suggested_markdown', '')
|
||||
is_inserted = link_text in current_text_state
|
||||
|
||||
bg_color = "#e6fffa" if is_inserted else "#ffffff"
|
||||
border = "3px solid #28a745" if is_inserted else "3px solid #1a73e8"
|
||||
|
||||
|
|
@ -201,6 +223,11 @@ def render_draft_editor(msg):
|
|||
"status": "draft",
|
||||
"tags": final_tags
|
||||
}
|
||||
|
||||
# ID wiederherstellen, falls vorhanden
|
||||
if "origin_note_id" in msg:
|
||||
final_meta["id"] = msg["origin_note_id"]
|
||||
|
||||
final_body = st.session_state.get(widget_body_key, st.session_state[data_body_key])
|
||||
|
||||
if not final_meta["title"]:
|
||||
|
|
@ -218,20 +245,33 @@ 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"):
|
||||
# Button Text dynamisch machen
|
||||
btn_label = "💾 Update speichern" if origin_fname else "💾 Neu anlegen & Indizieren"
|
||||
|
||||
if st.button(btn_label, type="primary", key=f"{key_base}_save"):
|
||||
with st.spinner("Speichere im Vault..."):
|
||||
raw_title = final_meta.get("title", "")
|
||||
if not raw_title:
|
||||
clean_body = re.sub(r"[#*_\[\]()]", "", final_body).strip()
|
||||
raw_title = clean_body[:40] if clean_body else "draft"
|
||||
safe_title = slugify(raw_title)[:60] or "draft"
|
||||
fname = f"{datetime.now().strftime('%Y%m%d')}-{safe_title}.md"
|
||||
|
||||
result = save_draft_to_vault(final_doc, filename=fname)
|
||||
if "error" in result: st.error(f"Fehler: {result['error']}")
|
||||
# ENTSCHEIDUNG: Update oder Neu?
|
||||
if origin_fname:
|
||||
# UPDATE: Wir nutzen den existierenden Dateinamen
|
||||
target_filename = origin_fname
|
||||
else:
|
||||
# NEU: Wir generieren einen Namen
|
||||
raw_title = final_meta.get("title", "")
|
||||
if not raw_title:
|
||||
clean_body = re.sub(r"[#*_\[\]()]", "", final_body).strip()
|
||||
raw_title = clean_body[:40] if clean_body else "draft"
|
||||
safe_title = slugify(raw_title)[:60] or "draft"
|
||||
target_filename = f"{datetime.now().strftime('%Y%m%d')}-{safe_title}.md"
|
||||
|
||||
result = save_draft_to_vault(final_doc, filename=target_filename)
|
||||
|
||||
if "error" in result:
|
||||
st.error(f"Fehler: {result['error']}")
|
||||
else:
|
||||
st.success(f"Gespeichert: {result.get('file_path')}")
|
||||
st.balloons()
|
||||
|
||||
with b2:
|
||||
if st.button("📋 Code anzeigen", key=f"{key_base}_btn_copy"):
|
||||
st.code(final_doc, language="markdown")
|
||||
|
|
@ -239,9 +279,6 @@ def render_draft_editor(msg):
|
|||
st.markdown("</div>", unsafe_allow_html=True)
|
||||
|
||||
def render_chat_interface(top_k, explain):
|
||||
"""
|
||||
Rendert den Chat-Verlauf und das Eingabefeld.
|
||||
"""
|
||||
for idx, msg in enumerate(st.session_state.messages):
|
||||
with st.chat_message(msg["role"]):
|
||||
if msg["role"] == "assistant":
|
||||
|
|
@ -298,7 +335,6 @@ def render_chat_interface(top_k, explain):
|
|||
st.rerun()
|
||||
|
||||
def render_manual_editor():
|
||||
"""Rendert einen leeren Editor für manuelle Eingaben."""
|
||||
mock_msg = {
|
||||
"content": "---\ntype: concept\ntitle: Neue Notiz\nstatus: draft\ntags: []\n---\n# Titel\n",
|
||||
"query_id": "manual_mode_v2"
|
||||
|
|
@ -310,10 +346,8 @@ def render_manual_editor():
|
|||
def render_graph_explorer(graph_service):
|
||||
st.header("🕸️ 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
|
||||
st.session_state.setdefault("graph_depth", 2)
|
||||
st.session_state.setdefault("graph_show_labels", True)
|
||||
st.session_state.setdefault("graph_spacing", 200)
|
||||
|
|
@ -324,7 +358,6 @@ def render_graph_explorer(graph_service):
|
|||
with col_ctrl:
|
||||
st.subheader("Fokus")
|
||||
|
||||
# 1. Suchfeld
|
||||
search_term = st.text_input("Suche Notiz", placeholder="Titel eingeben...")
|
||||
|
||||
options = {}
|
||||
|
|
@ -344,7 +377,6 @@ def render_graph_explorer(graph_service):
|
|||
|
||||
st.divider()
|
||||
|
||||
# --- VIEW SETTINGS ---
|
||||
with st.expander("👁️ Ansicht & Layout", expanded=True):
|
||||
st.session_state.graph_depth = st.slider("Tiefe (Tier)", 1, 3, st.session_state.graph_depth)
|
||||
st.session_state.graph_show_labels = st.checkbox("Kanten-Beschriftung", st.session_state.graph_show_labels)
|
||||
|
|
@ -367,12 +399,11 @@ def render_graph_explorer(graph_service):
|
|||
center_id = st.session_state.graph_center_id
|
||||
|
||||
if center_id:
|
||||
# Action Bar
|
||||
c_action1, c_action2 = st.columns([3, 1])
|
||||
with c_action1:
|
||||
st.caption(f"Aktives Zentrum: **{center_id}**")
|
||||
with c_action2:
|
||||
# FIX: Button mit Callback (on_click)
|
||||
# Button mit Callback (on_click)
|
||||
note_data = graph_service._fetch_note_cached(center_id)
|
||||
if note_data:
|
||||
st.button("📝 Bearbeiten",
|
||||
|
|
@ -390,11 +421,10 @@ def render_graph_explorer(graph_service):
|
|||
)
|
||||
|
||||
if not nodes:
|
||||
st.warning("Keine Daten gefunden. (Notiz existiert evtl. nicht mehr)")
|
||||
st.warning("Keine Daten gefunden.")
|
||||
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}"
|
||||
|
||||
# FIX: Key entfernt, da er in deiner Version TypeError verursacht.
|
||||
# Wir verlassen uns auf Config-Update.
|
||||
config = Config(
|
||||
width=1000,
|
||||
height=800,
|
||||
|
|
@ -404,7 +434,6 @@ def render_graph_explorer(graph_service):
|
|||
nodeHighlightBehavior=True,
|
||||
highlightColor="#F7A7A6",
|
||||
collapsible=False,
|
||||
# Solver Wechsel: ForceAtlas2Based
|
||||
solver="forceAtlas2Based",
|
||||
forceAtlas2Based={
|
||||
"theta": 0.5,
|
||||
|
|
@ -418,7 +447,7 @@ def render_graph_explorer(graph_service):
|
|||
stabilization={"enabled": True, "iterations": 800}
|
||||
)
|
||||
|
||||
return_value = agraph(nodes=nodes, edges=edges, config=config, key=graph_key)
|
||||
return_value = agraph(nodes=nodes, edges=edges, config=config)
|
||||
|
||||
if return_value:
|
||||
if return_value != center_id:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user