Skip to content

graph.html: no way to tell calls from imports/contains at a glance #2088

Description

@elias-codenfun

What I ran into

Reading graph.html for a 721-node TypeScript project, I could not tell the call graph apart from the file structure. Every edge is drawn the same, so the only way to learn an edge's type is to hover it.

The breakdown for that graph:

relation edges
calls 910
imports + imports_from 926
contains 603
references 38
other 36

contains and imports are ~62% of what is drawn and carry no behavioural information, so they crowd out the edges I was actually looking for. The data is already there — each edge carries its relation as label and it shows in the tooltip — but _html_script blanks the label and gives every edge one colour, so nothing surfaces it at a glance.

What would help

A colour per relation plus a way to hide relations, in the shape of the community legend that already exists. Unticking everything except calls leaves the call graph on its own, which was exactly what I needed.

Would you take a PR?

I have this working locally and would rather ask before opening it, since the README asks for worked examples and extraction bug reports rather than features, and every changelog entry I can see is an issue-linked fix.

What I have, if it is useful:

  • A "Connection Types" panel next to the community legend: colour, count and checkbox per relation.
  • Off by default. The graph opens with edge colours untouched and nothing repainted at load, so it costs nothing to anyone who ignores it. A checkbox turns it on and the state persists in localStorage, so it is the reader's choice rather than something baked into the file by whoever generated it. to_html(edge_types=...) and GRAPHIFY_EDGE_TYPES=1 set the initial state, mirroring node_limit / GRAPHIFY_VIZ_NODE_LIMIT.
  • Relation strings go through sanitize_label on the way out and esc() on the way into the DOM, and the lookup maps are null-prototype (a relation named constructor or __proto__ would otherwise corrupt the legend).
  • Colouring preserves each edge's own opacity, so the EXTRACTED/inferred distinction is not flattened.
  • A click updates only that relation's edges, not the whole array, to avoid a physics rebuild on large graphs.
  • Unknown relations get a stable hashed hue, so new extractor relation kinds stay distinguishable with no change needed there.
  • Skipped for the aggregated (>node-limit) view, whose meta-edges carry "<n> cross-community edges" as their relation; a legend row per distinct weight would be noise.
  • Tests drive the generated panel's own JavaScript through a small DOM/vis stub (skipped when node is unavailable), so they fail if the behaviour breaks rather than only if a string disappears.

One thing worth fixing regardless

While testing I hit a separate bug in the existing viewer. toggleAllCommunities queries .legend-item and .legend-cb unscoped:

document.querySelectorAll('.legend-item').forEach(...)
document.querySelectorAll('.legend-cb').forEach(cb => { cb.checked = !hide; })

Today nothing else in the page uses those classes, so it is latent, but any second panel reusing the legend styles gets its checkboxes rewritten without its change handlers firing, leaving the sidebar showing a state the graph is not in. Scoping both to #legend fixes it. Happy to send that as a standalone one-line PR whatever you decide about the feature.

Environment: graphify 0.9.22, Python 3.12, Linux.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions