126 lines
5.4 KiB
Python
126 lines
5.4 KiB
Python
import streamlit as st
|
|
from streamlit_agraph import agraph, Config
|
|
from qdrant_client import models
|
|
from ui_config import COLLECTION_PREFIX, GRAPH_COLORS
|
|
from ui_callbacks import switch_to_editor_callback
|
|
|
|
def render_graph_explorer(graph_service):
|
|
st.header("🕸️ Graph Explorer")
|
|
|
|
if "graph_center_id" not in st.session_state: st.session_state.graph_center_id = None
|
|
|
|
# Defaults
|
|
st.session_state.setdefault("graph_depth", 2)
|
|
st.session_state.setdefault("graph_show_labels", True)
|
|
st.session_state.setdefault("graph_spacing", 150)
|
|
st.session_state.setdefault("graph_gravity", -3000)
|
|
|
|
col_ctrl, col_graph = st.columns([1, 4])
|
|
|
|
with col_ctrl:
|
|
st.subheader("Fokus")
|
|
search_term = st.text_input("Suche Notiz", placeholder="Titel eingeben...")
|
|
|
|
if search_term:
|
|
hits, _ = graph_service.client.scroll(
|
|
collection_name=f"{COLLECTION_PREFIX}_notes",
|
|
scroll_filter=models.Filter(must=[models.FieldCondition(key="title", match=models.MatchText(text=search_term))]),
|
|
limit=10
|
|
)
|
|
options = {h.payload['title']: h.payload['note_id'] for h in hits}
|
|
if options:
|
|
selected_title = st.selectbox("Ergebnisse:", list(options.keys()))
|
|
if st.button("Laden", use_container_width=True):
|
|
st.session_state.graph_center_id = options[selected_title]
|
|
st.rerun()
|
|
|
|
st.divider()
|
|
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)
|
|
|
|
st.markdown("**Physik (BarnesHut)**")
|
|
st.session_state.graph_spacing = st.slider("Federlänge", 50, 500, st.session_state.graph_spacing)
|
|
st.session_state.graph_gravity = st.slider("Abstoßung", -20000, -500, st.session_state.graph_gravity)
|
|
|
|
if st.button("Reset Layout"):
|
|
st.session_state.graph_spacing = 150
|
|
st.session_state.graph_gravity = -3000
|
|
st.rerun()
|
|
|
|
st.divider()
|
|
st.caption("Legende (Top Typen)")
|
|
for k, v in list(GRAPH_COLORS.items())[:8]:
|
|
st.markdown(f"<span style='color:{v}'>●</span> {k}", unsafe_allow_html=True)
|
|
|
|
with col_graph:
|
|
center_id = st.session_state.graph_center_id
|
|
|
|
if center_id:
|
|
# Action Container oben
|
|
action_container = st.container()
|
|
|
|
with st.spinner(f"Lade Graph..."):
|
|
nodes, edges = graph_service.get_ego_graph(
|
|
center_id,
|
|
depth=st.session_state.graph_depth,
|
|
show_labels=st.session_state.graph_show_labels
|
|
)
|
|
note_data = graph_service._fetch_note_cached(center_id)
|
|
|
|
# Action Bar rendern
|
|
with action_container:
|
|
c1, c2 = st.columns([3, 1])
|
|
with c1: st.caption(f"Aktives Zentrum: **{center_id}**")
|
|
with c2:
|
|
if note_data:
|
|
st.button("📝 Bearbeiten", use_container_width=True, on_click=switch_to_editor_callback, args=(note_data,))
|
|
else:
|
|
st.error("Datenfehler")
|
|
|
|
with st.expander("🕵️ Data Inspector", expanded=False):
|
|
if note_data:
|
|
st.json(note_data)
|
|
if 'path' in note_data: st.success(f"Pfad OK: {note_data['path']}")
|
|
else: st.error("Pfad fehlt!")
|
|
else: st.info("Leer.")
|
|
|
|
if not nodes:
|
|
st.warning("Keine Daten gefunden.")
|
|
else:
|
|
# Physik Config für BarnesHut + Height-Trick
|
|
dyn_height = 800 + (abs(st.session_state.graph_gravity) % 5)
|
|
|
|
config = Config(
|
|
width=1000,
|
|
height=dyn_height,
|
|
directed=True,
|
|
physics={
|
|
"enabled": True,
|
|
"solver": "barnesHut",
|
|
"barnesHut": {
|
|
"gravitationalConstant": st.session_state.graph_gravity,
|
|
"centralGravity": 0.3,
|
|
"springLength": st.session_state.graph_spacing,
|
|
"springConstant": 0.04,
|
|
"damping": 0.09,
|
|
"avoidOverlap": 0.1
|
|
},
|
|
"stabilization": {"enabled": True, "iterations": 600}
|
|
},
|
|
hierarchical=False,
|
|
nodeHighlightBehavior=True,
|
|
highlightColor="#F7A7A6",
|
|
collapsible=False
|
|
)
|
|
|
|
return_value = agraph(nodes=nodes, edges=edges, config=config)
|
|
|
|
if return_value:
|
|
if return_value != center_id:
|
|
st.session_state.graph_center_id = return_value
|
|
st.rerun()
|
|
else:
|
|
st.toast(f"Zentrum: {return_value}")
|
|
else:
|
|
st.info("👈 Bitte wähle links eine Notiz aus.") |