neu Strategie zum Node Select

This commit is contained in:
Lars 2025-12-14 16:22:48 +01:00
parent 8772271648
commit 48de6aed8c

View File

@ -11,7 +11,6 @@ def render_graph_explorer_cytoscape(graph_service):
if "graph_center_id" not in st.session_state:
st.session_state.graph_center_id = None
# Getrennter State für Inspektion (Gelber Rahmen) vs. Navigation (Roter Rahmen)
if "graph_inspected_id" not in st.session_state:
st.session_state.graph_inspected_id = None
@ -22,7 +21,7 @@ def render_graph_explorer_cytoscape(graph_service):
col_ctrl, col_graph = st.columns([1, 4])
# --- LINKES PANEL ---
# --- LINKES PANEL: SUCHE & SETTINGS ---
with col_ctrl:
st.subheader("Fokus")
search_term = st.text_input("Suche Notiz", placeholder="Titel eingeben...", key="cy_search")
@ -58,7 +57,7 @@ def render_graph_explorer_cytoscape(graph_service):
for k, v in list(GRAPH_COLORS.items())[:8]:
st.markdown(f"<span style='color:{v}'>●</span> {k}", unsafe_allow_html=True)
# --- RECHTES PANEL ---
# --- RECHTES PANEL: GRAPH & INSPECTOR ---
with col_graph:
center_id = st.session_state.graph_center_id
@ -68,6 +67,7 @@ def render_graph_explorer_cytoscape(graph_service):
st.session_state.graph_center_id = center_id
if center_id:
# Sync: Wenn Inspection None ist, setze auf Center
if not st.session_state.graph_inspected_id:
st.session_state.graph_inspected_id = center_id
@ -75,11 +75,12 @@ def render_graph_explorer_cytoscape(graph_service):
# --- DATEN LADEN ---
with st.spinner(f"Lade Graph (Tiefe {st.session_state.cy_depth})..."):
# 1. Graph Daten
nodes_data, edges_data = graph_service.get_ego_graph(
center_id,
depth=st.session_state.cy_depth
)
# Daten für den INSPECTOR laden
# 2. Detail Daten (nur für den Inspector)
inspected_data = graph_service.get_note_with_full_content(inspected_id)
# --- ACTION BAR ---
@ -89,7 +90,7 @@ def render_graph_explorer_cytoscape(graph_service):
with c1:
title_show = inspected_data.get('title', inspected_id) if inspected_data else inspected_id
st.info(f"**Info:** {title_show}")
st.info(f"**Ausgewählt:** {title_show}")
with c2:
# NAVIGATION
@ -98,7 +99,7 @@ def render_graph_explorer_cytoscape(graph_service):
st.session_state.graph_center_id = inspected_id
st.rerun()
else:
st.caption("_(Zentrum)_")
st.caption("_(Ist aktuelles Zentrum)_")
with c3:
# EDITIEREN
@ -128,9 +129,10 @@ def render_graph_explorer_cytoscape(graph_service):
cy_elements = []
for n in nodes_data:
is_center = (n.id == center_id)
# Selektion wird visuell über Stylesheet gesteuert
is_inspected = (n.id == inspected_id)
# Logik: Wir vergeben Klassen statt Selection-State
classes = []
if n.id == center_id: classes.append("center")
if n.id == inspected_id: classes.append("inspected")
tooltip_text = n.title if n.title else n.label
display_label = n.label
@ -144,8 +146,8 @@ def render_graph_explorer_cytoscape(graph_service):
"bg_color": n.color,
"tooltip": tooltip_text
},
"selected": is_inspected,
"classes": "center" if is_center else ""
"classes": " ".join(classes),
"selected": False # Wir deaktivieren die interne Selektion beim Init
}
cy_elements.append(cy_node)
@ -162,7 +164,7 @@ def render_graph_explorer_cytoscape(graph_service):
}
cy_elements.append(cy_edge)
# --- STYLESHEET ---
# --- STYLESHEET (Klassen-basiert) ---
stylesheet = [
{
"selector": "node",
@ -177,23 +179,44 @@ def render_graph_explorer_cytoscape(graph_service):
"title": "data(tooltip)"
}
},
# Klasse .inspected = Gelber Rahmen (Ersetzt :selected)
{
"selector": "node:selected",
"selector": ".inspected",
"style": {
"border-width": 6,
"border-color": "#FFC300",
"border-color": "#FFC300", # Gelb/Gold
"width": "50px", "height": "50px",
"font-weight": "bold"
"font-weight": "bold",
"z-index": 999
}
},
# Klasse .center = Roter Rahmen
{
"selector": ".center",
"style": {
"border-width": 4,
"border-color": "#FF5733",
"border-color": "#FF5733", # Rot
"width": "40px", "height": "40px"
}
},
# Wenn beides zutrifft (Zentrum ist inspiziert)
{
"selector": ".center.inspected",
"style": {
"border-width": 6,
"border-color": "#FF5733", # Rot gewinnt oder Mix
"width": "55px", "height": "55px"
}
},
# Interne Selektion unsichtbar machen oder angleichen
{
"selector": "node:selected",
"style": {
"overlay-opacity": 0, # Kein blauer Schleier
"border-width": 6,
"border-color": "#FFC300" # Feedback beim Klick
}
},
{
"selector": "edge",
"style": {
@ -210,12 +233,14 @@ def render_graph_explorer_cytoscape(graph_service):
]
# --- RENDER ---
# KEY-STRATEGIE:
# Der Key hängt NUR vom Zentrum und den Settings ab.
# Wenn sich inspected_id ändert, bleibt der Key GLEICH -> Kein Re-Layout, nur Style-Update.
graph_key = f"cy_{center_id}_{st.session_state.cy_depth}_{st.session_state.cy_ideal_edge_len}"
clicked_elements = cytoscape(
elements=cy_elements,
stylesheet=stylesheet,
# Layout Config (config Argument entfernt!)
layout={
"name": "cose",
"idealEdgeLength": st.session_state.cy_ideal_edge_len,
@ -223,7 +248,7 @@ def render_graph_explorer_cytoscape(graph_service):
"refresh": 20,
"fit": True,
"padding": 50,
"randomize": False,
"randomize": False, # WICHTIG gegen Springen
"componentSpacing": 100,
"nodeRepulsion": st.session_state.cy_node_repulsion,
"edgeElasticity": 100,
@ -239,15 +264,27 @@ def render_graph_explorer_cytoscape(graph_service):
height="700px"
)
# --- INTERACTION ---
# --- EVENT HANDLING ---
if clicked_elements:
clicked_nodes = clicked_elements.get("nodes", [])
# Wir suchen nach der Node, die neu angeklickt wurde
if clicked_nodes:
clicked_id = clicked_nodes[0]
# Wir nehmen die letzte Node in der Liste (meistens die neu geklickte)
# oder filtern nach der, die nicht schon inspected ist
candidate_id = None
# Wenn neuer Knoten geklickt wurde -> Inspektion ändern
if clicked_id != st.session_state.graph_inspected_id:
st.session_state.graph_inspected_id = clicked_id
# Strategie: Wenn Liste > 1, nimm die ID die NICHT inspected_id ist
if len(clicked_nodes) > 1:
for nid in clicked_nodes:
if nid != st.session_state.graph_inspected_id:
candidate_id = nid
break
else:
candidate_id = clicked_nodes[0]
if candidate_id and candidate_id != st.session_state.graph_inspected_id:
st.session_state.graph_inspected_id = candidate_id
st.rerun()
else: