From a7350482706a7f4bc9e6cff8d094151de43b4145 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Sun, 17 May 2026 01:27:05 +0300 Subject: [PATCH] Make HTML5 flowchart nodes navigable --- services/api-server/src/api_server/html5.py | 37 ++++++++++++++++----- services/api-server/tests/test_api.py | 2 ++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/services/api-server/src/api_server/html5.py b/services/api-server/src/api_server/html5.py index 33ea7ea..3838def 100644 --- a/services/api-server/src/api_server/html5.py +++ b/services/api-server/src/api_server/html5.py @@ -275,9 +275,9 @@ def render_html5_flowchart( nodes = getattr(flowchart, "nodes", []) or [] edges = getattr(flowchart, "edges", []) or [] mode = str(getattr(flowchart, "mode", "overview")) - body = "".join(_flowchart_edge_item(item, nodes) for item in edges[:10]) + body = "".join(_flowchart_edge_item(project_id, item, nodes, normalized_depth) for item in edges[:10]) if not body: - body = "".join(_flowchart_node_item(item) for item in nodes[:10]) + body = "".join(_flowchart_node_item(project_id, item, normalized_depth) for item in nodes[:10]) if not body: body = '

Связи проекта не найдены

' return f""" @@ -482,7 +482,7 @@ def render_html5_object_context( compact_body += ''.join(_named_node_item("read", item) for item in query_tables[:8]) compact_body += ''.join(_named_node_item("write", item) for item in writes[:8]) compact_body += ''.join(_named_node_item("call", item) for item in callees[:8]) - compact_body += ''.join(_flowchart_edge_item(item, flow_nodes) for item in flow_edges[:8]) + compact_body += ''.join(_flowchart_edge_item(project_id, item, flow_nodes, 1) for item in flow_edges[:8]) compact_body += ''.join(_named_node_item("routine", item) for item in routines[:8]) compact_body += ''.join(_named_node_item("job", item) for item in jobs[:6]) compact_body = compact_body or '

Impact-связи не найдены

' @@ -503,7 +503,7 @@ def render_html5_object_context( compact_body += ''.join(_named_node_item("read", item) for item in query_tables[:4]) compact_body += ''.join(_named_node_item("write", item) for item in writes[:4]) compact_body += ''.join(_named_node_item("call", item) for item in callees[:6]) - compact_body += ''.join(_flowchart_edge_item(item, flow_nodes) for item in flow_edges[:8]) + compact_body += ''.join(_flowchart_edge_item(project_id, item, flow_nodes, 1) for item in flow_edges[:8]) compact_body += ''.join(_runtime_summary_item(item) for item in runtime_items[:6]) compact_body += ''.join(_knowledge_record_item(item) for item in knowledge_items[:6]) compact_body += ''.join(_privacy_marker_item(item) for item in privacy_markers[:6]) @@ -1734,7 +1734,20 @@ def _integration_endpoint_item(endpoint: object) -> str: """ -def _flowchart_edge_item(edge: object, nodes: Iterable[object]) -> str: +def _flowchart_focus_link(project_id: str, name: str, depth: int) -> str: + url = _flowchart_url(project_id, name, depth) + return f""" + {escape(name)} + """ + + +def _flowchart_edge_item(project_id: str, edge: object, nodes: Iterable[object], depth: int) -> str: node_names = { str(getattr(node, "id", "")): str(getattr(node, "qualified_name", "") or getattr(node, "label", "")) for node in nodes @@ -1746,7 +1759,7 @@ def _flowchart_edge_item(edge: object, nodes: Iterable[object]) -> str: return f"""
{escape(label)} - {escape(source)} -> {escape(target)} · {escape(kind)} + {_flowchart_focus_link(project_id, source, depth)} -> {_flowchart_focus_link(project_id, target, depth)} · {escape(kind)}
""" @@ -1779,13 +1792,21 @@ def _flowchart_depth_actions(project_id: str, focus: str | None, active_depth: i return f'' -def _flowchart_node_item(node: object) -> str: +def _flowchart_node_item(project_id: str, node: object, depth: int) -> str: name = str(getattr(node, "qualified_name", "") or getattr(node, "label", "") or "node") kind = str(getattr(node, "kind", "") or "NODE") level = getattr(node, "level", 0) count = getattr(node, "count", 1) + url = _flowchart_url(project_id, name, depth) return f""" -
+
{escape(name)} {escape(kind)} · level {escape(str(level))} · count {escape(str(count))}
diff --git a/services/api-server/tests/test_api.py b/services/api-server/tests/test_api.py index f8b726e..30e70ca 100644 --- a/services/api-server/tests/test_api.py +++ b/services/api-server/tests/test_api.py @@ -341,6 +341,7 @@ def test_html5_object_context_fragment(tmp_path: Path): assert "https://api.example.local/orders" in context.text assert "OUTBOUND" in context.text assert "data-html5-object-context-item=\"flow-edge\"" in context.text + assert "data-html5-flowchart-focus" in context.text assert "data-html5-flowchart" in context.text assert 'hx-swap-oob="outerHTML"' in context.text assert "Карта связей · focus" in context.text @@ -427,6 +428,7 @@ def test_html5_flowchart_fragment(tmp_path: Path): assert "Depth 3" in flowchart.text assert "depth=2" in flowchart.text assert 'hx-target="[data-html5-flowchart]"' in flowchart.text + assert "data-html5-flowchart-focus" in flowchart.text assert 'hx-swap-oob="outerHTML"' not in flowchart.text assert "Карта связей" in flowchart.text assert "Nodes" in flowchart.text