Skip to content

graph.html: clicking a neighbor link is a no-op when its community is filtered out #3378

Description

@evaldnet

Summary

In graph.html, clicking a neighbour link in the NODE INFO panel updates the panel but silently does nothing to the canvas when the target node's community is currently filtered out. Two of focusNode()'s three actions become no-ops, so the graph never moves and there is no feedback explaining why.

Reproduce

  1. Build a graph large enough to render the aggregated community view (mine is 28,959 nodes → 2,218 communities).
  2. Open graphify-out/graph.html.
  3. Untick Select All to hide every community.
  4. Tick a single community — I used MySQLWrapper.
  5. Click that node to populate the NODE INFO panel.
  6. Click any neighbour in the Neighbors (N) list whose community is still unticked.

Expected: the graph focuses the clicked node, or the UI indicates why it cannot.

Actual: the panel navigates to the new node and lists its neighbours, but the canvas is unchanged. The node stays invisible, nothing is selected, the camera does not move, and no message is shown. It reads as a dead click.

Cause

focusNode() has no notion that a node may currently be filtered out:

function focusNode(nodeId) {
  network.focus(nodeId, { scale: 1.4, animation: true });
  network.selectNodes([nodeId]);
  showInfo(nodeId);
}

The community filter hides nodes rather than removing them:

const updates = RAW_NODES.filter(n => n.community === c.cid)
                         .map(n => ({ id: n.id, hidden: !cb.checked }));
nodesDS.update(updates);

So for a node carrying hidden: true:

call result
network.focus(nodeId, …) no rendered position — silent no-op
network.selectNodes([nodeId]) selects something invisible
showInfo(nodeId) works, reads the underlying data

Only the panel updates. Both files: graphify/exporters/html.py.

Suggested fix

Reveal the target's community before focusing, by firing the legend checkbox's existing change handler rather than duplicating its body — that keeps hiddenCommunities, the dimmed styling, nodesDS and the Select-All tri-state consistent through one code path.

Register the checkboxes as the legend is built:

const CB_BY_CID = new Map();
// …in the LEGEND.forEach loop, after `cb.checked = true;`
CB_BY_CID.set(c.cid, cb);

Then:

function focusNode(nodeId) {
  const n = RAW_NODES.find(x => x.id === nodeId);
  if (n && hiddenCommunities.has(n.community)) {
    const cb = CB_BY_CID.get(n.community);
    if (cb) { cb.checked = true; cb.dispatchEvent(new Event('change')); }
  }
  network.focus(nodeId, { scale: 1.4, animation: true });
  network.selectNodes([nodeId]);
  showInfo(nodeId);
}

I have been running this patch locally against 0.9.55. With it, the repro above takes the checked-community count from 1 → 2 and visible nodes from 1 → 2, and the two nodes render with the edge between them.

If you would rather not have a click implicitly change the filter, an alternative is to leave the filter alone and surface the state instead — for example, disabling or annotating neighbour links whose community is hidden, so the click is visibly unavailable rather than inert. Happy to send a PR for whichever behaviour you prefer.

Notes

  • Reproduced on the aggregated community view. The same logic runs for the node-level view, so it should reproduce there too whenever a community is filtered out.
  • Regenerating after editing the exporter needs graphify cluster-only <path>; graphify update <path> --force re-extracts but stops at "No code-graph topology changes detected; outputs left untouched" and leaves the old graph.html in place. That was mildly confusing while testing, and may be worth a mention in the docs.

Environment

  • graphify 0.9.55 (uv tool install "graphifyy[sql,mcp,bedrock,leiden]")
  • macOS 15 (Darwin 25.6.0), arm64, Python 3.11
  • Corpus: 4,181 files, 28,959 nodes / 61,576 edges / 2,218 communities

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions