This commit is contained in:
Lars 2025-12-12 17:21:15 +01:00
parent 0ba8ae8d1e
commit fb6e35ed01

View File

@ -73,10 +73,18 @@ if "user_id" not in st.session_state: st.session_state.user_id = str(uuid.uuid4(
# --- HELPER FUNCTIONS ---
def slugify(value):
"""Erzeugt saubere Dateinamen aus Titeln."""
value = str(value)
"""
Erzeugt saubere Dateinamen (German-Aware).
z.B. "Müller & Söhne" -> "mueller-und-soehne"
"""
value = str(value).lower()
# Deutsche Umlaute manuell ersetzen, da NFKD sie oft nur strippt
replacements = {'ä': 'ae', 'ö': 'oe', 'ü': 'ue', 'ß': 'ss', '&': 'und'}
for k, v in replacements.items():
value = value.replace(k, v)
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
value = re.sub(r'[^\w\s-]', '', value).strip().lower()
value = re.sub(r'[^\w\s-]', '', value).strip()
return re.sub(r'[-\s]+', '-', value)
def normalize_meta_and_body(meta, body):
@ -85,11 +93,9 @@ def normalize_meta_and_body(meta, body):
clean_meta = {}
extra_content = []
# Title Normalization
if "titel" in meta and "title" not in meta:
meta["title"] = meta.pop("titel")
# Tag Normalization
tag_candidates = ["tags", "emotionale_keywords", "keywords", "schluesselwoerter"]
all_tags = []
for key in tag_candidates:
@ -109,7 +115,6 @@ def normalize_meta_and_body(meta, body):
extra_content.append(f"## {header}\n{val}\n")
if all_tags:
# Bereinige Tags von '#' und Duplikaten
clean_tags = []
for t in all_tags:
t_clean = str(t).replace("#", "").strip()
@ -125,16 +130,19 @@ def normalize_meta_and_body(meta, body):
return clean_meta, final_body
def parse_markdown_draft(full_text):
"""Robustes Parsing + Sanitization (YAML + Fallbacks)."""
clean_text = full_text
"""
Klassischer Parser (mit YAML-Fixes).
Funktioniert am besten, wenn Struktur grob eingehalten wird.
"""
clean_text = full_text.strip()
# 1. Markdown Code-Blöcke entfernen
pattern_block = r"```(?:markdown|md)?\s*(.*?)\s*```"
match_block = re.search(pattern_block, full_text, re.DOTALL | re.IGNORECASE)
# Code Blocks entfernen
pattern_block = r"```(?:markdown|md|yaml)?\s*(.*?)\s*```"
match_block = re.search(pattern_block, clean_text, re.DOTALL | re.IGNORECASE)
if match_block:
clean_text = match_block.group(1).strip()
# 2. Split YAML / Body
# Split an '---'
parts = re.split(r"^---+\s*$", clean_text, maxsplit=2, flags=re.MULTILINE)
meta = {}
@ -144,7 +152,7 @@ def parse_markdown_draft(full_text):
yaml_str = parts[1]
body_candidate = parts[2]
# FIX 1: Hashtag-Cleaner für YAML (gegen Syntaxfehler)
# YAML Cleanup: Entferne '#' innerhalb der YAML-Sektion
yaml_str_clean = yaml_str.replace("#", "")
try:
@ -155,14 +163,14 @@ def parse_markdown_draft(full_text):
except Exception as e:
print(f"YAML Parsing Warning: {e}")
body = body_candidate.strip()
# FIX 3: Titel-Fallback aus H1
# Fallback: Titel aus H1 suchen, wenn nicht im YAML
if not meta.get("title"):
h1_match = re.search(r"^#\s+(.*)$", body, re.MULTILINE)
if h1_match:
meta["title"] = h1_match.group(1).strip()
# FIX 4: Type/Status Swap Korrektur
# Correction: type/status swap
if meta.get("type") == "draft":
meta["status"] = "draft"
meta["type"] = "experience"
@ -171,10 +179,10 @@ def parse_markdown_draft(full_text):
def build_markdown_doc(meta, body):
"""Baut das finale Dokument zusammen."""
# ID Generation
if "id" not in meta or meta["id"] == "generated_on_save":
# Nutze slugify für ID
clean_slug = slugify(meta.get('title', 'note'))[:40] or "note"
# Hier nutzen wir jetzt die verbesserte slugify Funktion
raw_title = meta.get('title', 'note')
clean_slug = slugify(raw_title)[:50] or "note"
meta["id"] = f"{datetime.now().strftime('%Y%m%d')}-{clean_slug}"
meta["updated"] = datetime.now().strftime("%Y-%m-%d")
@ -258,7 +266,7 @@ def submit_feedback(query_id, node_id, score, comment=None):
def render_sidebar():
with st.sidebar:
st.title("🧠 mindnet")
st.caption("v2.4.2 | Robust UI")
st.caption("v2.4.3 | Filename Fix")
mode = st.radio("Modus", ["💬 Chat", "📝 Manueller Editor"], index=0)
st.divider()
st.subheader("⚙️ Settings")
@ -298,7 +306,7 @@ def render_draft_editor(msg):
st.session_state[data_sugg_key] = []
st.session_state[data_body_key] = body.strip()
# Widgets Init
# Init Widgets Keys (Resurrection)
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"]
@ -412,11 +420,11 @@ def render_draft_editor(msg):
final_tags_str = st.session_state.get(f"{key_base}_wdg_tags", "")
final_tags = [t.strip() for t in final_tags_str.split(",") if t.strip()]
# WICHTIG: Hier ziehen wir die Daten explizit aus dem Widget-State
final_meta = {
"id": "generated_on_save",
"type": st.session_state.get(f"{key_base}_wdg_type", "default"),
# Title mit Fallback (Widget > Meta > Untitled)
"title": st.session_state.get(f"{key_base}_wdg_title", meta_ref.get("title", "Untitled")),
"title": st.session_state.get(f"{key_base}_wdg_title", "Untitled"),
"status": "draft",
"tags": final_tags
}
@ -435,9 +443,12 @@ def render_draft_editor(msg):
with b1:
if st.button("💾 Speichern & Indizieren", type="primary", key=f"{key_base}_save"):
with st.spinner("Speichere im Vault..."):
# DATEINAMEN-LOGIK (Fix)
raw_title = final_meta.get("title", "draft")
# Slugify für saubere Dateinamen
safe_title = slugify(raw_title)[:40] or "draft"
safe_title = slugify(raw_title)[:50]
if not safe_title: safe_title = "draft"
fname = f"{datetime.now().strftime('%Y%m%d')}-{safe_title}.md"
result = save_draft_to_vault(final_doc, filename=fname)