Skip to content

Feature: Interactive graph [as html page]#333

Open
dzianis-dashkevich wants to merge 7 commits intopahen:masterfrom
dzianis-dashkevich:feature/interactive-graph-html-page
Open

Feature: Interactive graph [as html page]#333
dzianis-dashkevich wants to merge 7 commits intopahen:masterfrom
dzianis-dashkevich:feature/interactive-graph-html-page

Conversation

@dzianis-dashkevich
Copy link

Dear contributors,

Thank you for madge, this is a nice and easy-to-use library.
Recently, I forked madge and added an interactive feature for my personal needs.
I decided that this might be helpful for others as well. So here I am.

This feature is highly inspired by: https://github.com/sverweij/dependency-cruiser/blob/develop/src/cli/tools/wrap-stream-in-html.js

Please, check this deployed example based on the video.js open-source project (https://github.com/videojs/video.js):
https://dzianis-dashkevich.github.io/madge-interactive-examples/video-js-graph.html

Controls features (I added it to the README):

Left click selects node(with edges) or edge. Other edges will be dimmed.
Left click out of context(node or edge) resets everything to the initial state.
Right click selects an additional node(with edges) or edge. Has no effect if there are no previously selected items.
Previously selected nodes or edges will be slightly dimmed(but will stay colored).
Escape resets everything to the initial state.

This example was generated by running the following command at the root of the project:
npx madge --interactive interactive-graph.html src/js/video.js

Currently, it is pretty opinionated (eg: colors, controls, etc..), we can discuss potential changes, if you are interested.
Please, just close this PR, If you are not interested in this feature.

Regards,
Dzianis

@PabloLION PabloLION mentioned this pull request Jan 30, 2023
@PabloLION PabloLION requested a review from Copilot September 25, 2025 04:10
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds an interactive HTML graph feature to madge, allowing users to generate dependency graphs as interactive web pages instead of static images. The feature provides visual controls for selecting and highlighting nodes and edges in the dependency graph.

  • Adds new interactive HTML output format with click and keyboard controls
  • Implements CSS styling and JavaScript functionality for graph interaction
  • Integrates the interactive feature into the existing API and CLI

Reviewed Changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
lib/interactive/templates/styles.css CSS styles for interactive graph visualization with selection states
lib/interactive/templates/scripts.js JavaScript functionality for node/edge selection and interaction
lib/interactive/index.js Core module for generating interactive HTML from SVG graphs
lib/graph.js Adds interactive function and refactors svg function for reuse
lib/api.js Adds interactive method to the main Madge API class
README.md Documentation for the new interactive feature and controls

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +36 to +40
svgEl.insertAdjacentHTML('afterbegin', `<linearGradient id="dualDirection">
<stop offset="0%" stop-color="var(--color-from)" />
<stop offset="100%" stop-color="var(--color-to)" />
</linearGradient>`);

Copy link

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linear gradient is inserted as raw HTML string. Consider creating the gradient elements using DOM methods for better maintainability and to avoid potential XSS issues.

Suggested change
svgEl.insertAdjacentHTML('afterbegin', `<linearGradient id="dualDirection">
<stop offset="0%" stop-color="var(--color-from)" />
<stop offset="100%" stop-color="var(--color-to)" />
</linearGradient>`);
const SVG_NS = "http://www.w3.org/2000/svg";
const linearGradient = document.createElementNS(SVG_NS, "linearGradient");
linearGradient.setAttribute("id", "dualDirection");
const stop1 = document.createElementNS(SVG_NS, "stop");
stop1.setAttribute("offset", "0%");
stop1.setAttribute("stop-color", "var(--color-from)");
linearGradient.appendChild(stop1);
const stop2 = document.createElementNS(SVG_NS, "stop");
stop2.setAttribute("offset", "100%");
stop2.setAttribute("stop-color", "var(--color-to)");
linearGradient.appendChild(stop2);
// Insert as the first child of the SVG (after <defs> if present, or create <defs>)
let defs = svgEl.querySelector('defs');
if (!defs) {
defs = document.createElementNS(SVG_NS, "defs");
svgEl.insertBefore(defs, svgEl.firstChild);
}
defs.appendChild(linearGradient);

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +66
let nodeList = [nodeTitleToNodeMap.get(from), nodeTitleToNodeMap.get(to)];
edgesMap.set(title, nodeList);

nodeList = edgesMap.get(from) || [];
nodeList.push(edge);
edgesMap.set(from, nodeList);

nodeList = edgesMap.get(to) || [];
nodeList.push(edge);
edgesMap.set(to, nodeList);
Copy link

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable nodeList is being reused for different purposes which makes the code confusing. Use different variable names like connectedNodes, fromEdges, and toEdges to clarify intent.

Suggested change
let nodeList = [nodeTitleToNodeMap.get(from), nodeTitleToNodeMap.get(to)];
edgesMap.set(title, nodeList);
nodeList = edgesMap.get(from) || [];
nodeList.push(edge);
edgesMap.set(from, nodeList);
nodeList = edgesMap.get(to) || [];
nodeList.push(edge);
edgesMap.set(to, nodeList);
const connectedNodes = [nodeTitleToNodeMap.get(from), nodeTitleToNodeMap.get(to)];
edgesMap.set(title, connectedNodes);
const fromEdges = edgesMap.get(from) || [];
fromEdges.push(edge);
edgesMap.set(from, fromEdges);
const toEdges = edgesMap.get(to) || [];
toEdges.push(edge);
edgesMap.set(to, toEdges);

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +25
function toHtml(styles, content, scripts) {
return `<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8" />
<title>Interactive Graph</title>
<style>
${styles}
</style>
</head>
<body>
${content}
<script>
${scripts}
</script>
</body>
</html>
`;
}
Copy link

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Direct template interpolation of styles, content, and scripts parameters could lead to XSS vulnerabilities if the input contains malicious content. Consider sanitizing or validating these inputs before insertion.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant