Generate a self-contained interactive HTML visualization. Args: store: The GraphStore to read graph data from. output_path: Path for the output HTML file. mode: Rendering mode — ``"auto"``, ``"full"``, ``"community"``, or ``"file"``. ``"auto"`` switches to
(
store: GraphStore,
output_path: str | Path,
mode: str = "auto",
max_full_nodes: int = 3000,
)
| 358 | |
| 359 | |
| 360 | def generate_html( |
| 361 | store: GraphStore, |
| 362 | output_path: str | Path, |
| 363 | mode: str = "auto", |
| 364 | max_full_nodes: int = 3000, |
| 365 | ) -> Path: |
| 366 | """Generate a self-contained interactive HTML visualization. |
| 367 | |
| 368 | Args: |
| 369 | store: The GraphStore to read graph data from. |
| 370 | output_path: Path for the output HTML file. |
| 371 | mode: Rendering mode — ``"auto"``, ``"full"``, ``"community"``, |
| 372 | or ``"file"``. ``"auto"`` switches to ``"community"`` when |
| 373 | the node count exceeds *max_full_nodes*. |
| 374 | max_full_nodes: Threshold for auto-switching to community mode. |
| 375 | |
| 376 | Writes the HTML file to *output_path* and returns the resolved Path. |
| 377 | """ |
| 378 | output_path = Path(output_path) |
| 379 | stats = store.get_stats() |
| 380 | if stats.total_nodes > 50000: |
| 381 | logger.warning( |
| 382 | "Graph has %d nodes — visualization may be slow. " |
| 383 | "Consider filtering by file pattern.", stats.total_nodes, |
| 384 | ) |
| 385 | data = export_graph_data(store) |
| 386 | |
| 387 | # Determine effective mode |
| 388 | effective_mode = mode |
| 389 | if effective_mode == "auto": |
| 390 | effective_mode = ( |
| 391 | "community" if stats.total_nodes > max_full_nodes else "full" |
| 392 | ) |
| 393 | |
| 394 | if effective_mode == "community": |
| 395 | # Keep full data available for drill-down; aggregate for top-level |
| 396 | agg = _aggregate_community(data) |
| 397 | # Escape </script> inside JSON to prevent premature tag closure |
| 398 | data_json = json.dumps(agg, default=str).replace("</", "<\\/") |
| 399 | html = _AGGREGATED_HTML_TEMPLATE.replace("__GRAPH_DATA__", data_json) |
| 400 | elif effective_mode == "file": |
| 401 | agg = _aggregate_file(data) |
| 402 | data_json = json.dumps(agg, default=str).replace("</", "<\\/") |
| 403 | html = _AGGREGATED_HTML_TEMPLATE.replace("__GRAPH_DATA__", data_json) |
| 404 | else: |
| 405 | # full mode — original behavior |
| 406 | data_json = json.dumps(data, default=str).replace("</", "<\\/") |
| 407 | html = _HTML_TEMPLATE.replace("__GRAPH_DATA__", data_json) |
| 408 | |
| 409 | output_path.write_text(html, encoding="utf-8") |
| 410 | return output_path |
| 411 | |
| 412 | |
| 413 | # --------------------------------------------------------------------------- |