rsdeps

Cargo.lock visualizer (mirror)
Log | Files | Refs | README | LICENSE

commit 1e86c03288c62d75f2d4b51ec524a71c109bdec3
parent e072873bed67c1c3e212f916c04ce428c2494832
Author: Andy Khramtsov <>
Date:   Thu, 28 May 2026 03:03:44 +0300

feat: add selected node details

Diffstat:
Msrc/deps/pages/home.py | 63+++++++++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 43 insertions(+), 20 deletions(-)

diff --git a/src/deps/pages/home.py b/src/deps/pages/home.py @@ -27,11 +27,12 @@ class ids: collapse_tables: str = "collapse-tables" cargo_lock_table: str = "cargo-lock-table" cargo_toml_table: str = "cargo-toml-table" - recalculate_button: str = "recalculate-button" + generate_button: str = "generate-button" dependency_graph: str = "dependency-graph" reset_highlight_button: str = "reset-highlight-button" + selected_node_details: str = "selected-node-details" cache_store: str = "cache-store" @@ -106,13 +107,20 @@ def layout(): dcc.Graph( id=ids.dependency_graph, figure={}, - style={"height": 800}, + style={"height": 1000}, ), dcc.Button(id=ids.reset_highlight_button, children="Reset highlight"), + html.Div(id=ids.selected_node_details), ], ) +def parse_node_details(details: dict) -> str: + details = {key: val for key, val in details.items()} + details["dependencies"] = "[" + ",".join([f"<br> {dep}" for dep in details["dependencies"] or []]) + "<br>]" + return "<br>".join([f"{key}: {val}" for key, val in details.items()]) + + def parse_cargo_toml(contents: str) -> pl.DataFrame: toml = tomllib.loads(contents) dependencies = toml.get("dependencies", {}) @@ -269,17 +277,20 @@ def draw_graph_figure( edge_x.extend([x0, x1, None]) edge_y.extend([y0, y1, None]) - edge_trace = go.Scatter(x=edge_x, y=edge_y, line=dict(width=1.0, color="#B0BEC0"), hoverinfo="skip", mode="lines") + edge_trace = ( + go.Scatter(x=edge_x, y=edge_y, line=dict(width=1.0, color="#A0A0A0"), hoverinfo="skip", mode="lines") + if not selected_node + else go.Scatter(x=edge_x, y=edge_y, line=dict(width=1.0, color="#D0D0D0"), hoverinfo="skip", mode="lines") + ) edge_trace_highlight = go.Scatter( - x=edge_x_highlight, y=edge_y_highlight, line=dict(width=1.0, color="#707EF0"), hoverinfo="skip", mode="lines" + x=edge_x_highlight, y=edge_y_highlight, line=dict(width=1.5, color="#8080F0"), hoverinfo="skip", mode="lines" ) node_x = [] node_y = [] short_labels = [] hover_details = [] - colors = [] - sizes = [] + marker_colors = [] text_colors = [] custom_data = [] for row in df_graph["nodes"].iter_rows(named=True): @@ -289,34 +300,31 @@ def draw_graph_figure( node_x.append(x) node_y.append(y) short_labels.append(row["short_label"]) - hover_details.append("<br>".join([f"<b>{key}:</b> {val}" for key, val in row["full_details"].items()])) + hover_details.append(parse_node_details(row["full_details"])) custom_data.append(node_id) - base_color = "#C1829E" if row["full_details"]["explicit"] else "#3182CE" + base_color = "#F0A0A0" if row["full_details"]["explicit"] else "#A0A0F0" if not selected_node: - colors.append(base_color) - sizes.append(28) - text_colors.append("#2D3748") + marker_colors.append(base_color) + text_colors.append("#303030") elif node_id in highlight_nodes: - colors.append("#ED8936" if node_id != selected_node else "#3182CE") - sizes.append(34) + marker_colors.append(base_color if node_id != selected_node else "#A0F0A0") text_colors.append("#000000") else: - colors.append("#E2E8F0") - sizes.append(20) - text_colors.append("#A0AEC0") + marker_colors.append("#E0E0E0") + text_colors.append("#A0A0A0") node_trace = go.Scatter( x=node_x, y=node_y, mode="markers+text", text=short_labels, - textposition="top center", + textposition="middle left", hovertext=hover_details, hoverinfo="text", customdata=custom_data, - marker=dict(showscale=False, color=colors, size=16, line=dict(width=2, color="white")), - textfont=dict(size=11, color=text_colors), + marker=dict(showscale=False, color=marker_colors, size=16, line=dict(width=1, color="white")), + textfont=dict(size=10, color=text_colors), ) fig = go.Figure( @@ -432,7 +440,11 @@ def display_cargo_lock_packages(inputs, state): @callback( - dict(graph=Output(ids.dependency_graph, "figure"), cache=Output(ids.cache_store, "data")), + dict( + graph=Output(ids.dependency_graph, "figure"), + cache=Output(ids.cache_store, "data"), + details=Output(ids.selected_node_details, "children"), + ), dict( generate=Input(ids.generate_button, "n_clicks"), click_data=Input(ids.dependency_graph, "clickData"), @@ -488,9 +500,20 @@ def visualize(inputs, state): ) cache = dash.no_update + details = [ + item + for part in ( + parse_node_details(df_graph["nodes"].filter(pl.col("node_id") == selected).to_dicts()[0]["full_details"]) + if selected + else "" + ).split("<br>") + for item in (part, html.Br()) + ] + fig = draw_graph_figure(df_graph, positions, selected) return dict( graph=fig, cache=cache, + details=details, )