mvn-tree-visualizer 1.3.0__py3-none-any.whl → 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mvn-tree-visualizer might be problematic. Click here for more details.

@@ -19,6 +19,7 @@ def generate_diagram(
19
19
  keep_tree: bool,
20
20
  output_format: str,
21
21
  show_versions: bool,
22
+ theme: str = "minimal",
22
23
  ) -> None:
23
24
  """Generate the dependency diagram with comprehensive error handling."""
24
25
  timestamp = time.strftime("%H:%M:%S")
@@ -86,7 +87,7 @@ def generate_diagram(
86
87
  # Generate output
87
88
  try:
88
89
  if output_format == "html":
89
- create_html_diagram(dependency_tree, output_file, show_versions)
90
+ create_html_diagram(dependency_tree, output_file, show_versions, theme)
90
91
  elif output_format == "json":
91
92
  create_json_output(dependency_tree, output_file, show_versions)
92
93
  else:
@@ -179,6 +180,14 @@ def cli() -> NoReturn:
179
180
  help="Watch for changes in Maven dependency files and automatically regenerate the diagram.",
180
181
  )
181
182
 
183
+ parser.add_argument(
184
+ "--theme",
185
+ type=str,
186
+ default="minimal",
187
+ choices=["minimal", "dark"],
188
+ help="Theme for the diagram visualization. Default is 'minimal'.",
189
+ )
190
+
182
191
  args = parser.parse_args()
183
192
  directory: str = args.directory
184
193
  output_file: str = args.output
@@ -187,10 +196,11 @@ def cli() -> NoReturn:
187
196
  output_format: str = args.format
188
197
  show_versions: bool = args.show_versions
189
198
  watch_mode: bool = args.watch
199
+ theme: str = args.theme
190
200
 
191
201
  # Generate initial diagram
192
202
  print("Generating initial diagram...")
193
- generate_diagram(directory, output_file, filename, keep_tree, output_format, show_versions)
203
+ generate_diagram(directory, output_file, filename, keep_tree, output_format, show_versions, theme)
194
204
 
195
205
  if not watch_mode:
196
206
  print("You can open it in your browser to view the dependency tree.")
@@ -200,7 +210,7 @@ def cli() -> NoReturn:
200
210
  # Watch mode
201
211
  def regenerate_callback():
202
212
  """Callback function for file watcher."""
203
- generate_diagram(directory, output_file, filename, keep_tree, output_format, show_versions)
213
+ generate_diagram(directory, output_file, filename, keep_tree, output_format, show_versions, theme)
204
214
 
205
215
  watcher = FileWatcher(directory, filename, regenerate_callback)
206
216
  watcher.start()
@@ -0,0 +1,218 @@
1
+ """Enhanced HTML templates with the interactive features."""
2
+
3
+ from typing import Any, Dict
4
+
5
+ from .themes import STANDARD_COLORS, Theme
6
+
7
+
8
+ def get_html_template(theme: Theme) -> str:
9
+ """Generate HTML template with theme-specific styling and interactive features."""
10
+
11
+ # Build Mermaid configuration
12
+ mermaid_config = {
13
+ "startOnLoad": True,
14
+ "sequence": {"useMaxWidth": False},
15
+ "theme": theme.mermaid_theme,
16
+ **theme.mermaid_config,
17
+ }
18
+
19
+ # Convert config to JavaScript object
20
+ mermaid_config_js = _dict_to_js_object(mermaid_config)
21
+
22
+ return f"""<!DOCTYPE html>
23
+ <html lang="en">
24
+ <head>
25
+ <meta charset="UTF-8">
26
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
27
+ <title>Maven Dependency Diagram - {theme.name.title()} Theme</title>
28
+ <style>
29
+ #mySvgId {{
30
+ height: 100%;
31
+ width: 100%;
32
+ }}
33
+
34
+ /* Theme-specific styles */
35
+ {theme.custom_css}
36
+
37
+ /* Dark theme text visibility fixes */
38
+ {
39
+ ""
40
+ if theme.name != "dark"
41
+ else '''
42
+ /* Force white text for all mermaid elements in dark theme */
43
+ .node text, .edgeLabel text, text, .label text {
44
+ fill: #ffffff !important;
45
+ color: #ffffff !important;
46
+ }
47
+
48
+ /* Ensure node backgrounds are visible */
49
+ .node rect, .node circle, .node ellipse, .node polygon {
50
+ fill: #4a5568 !important;
51
+ stroke: #e2e8f0 !important;
52
+ stroke-width: 1px !important;
53
+ }
54
+
55
+ /* Edge styling for dark theme */
56
+ .edge path, .flowchart-link {
57
+ stroke: #a0aec0 !important;
58
+ stroke-width: 2px !important;
59
+ }
60
+
61
+ /* Arrow styling */
62
+ .arrowheadPath {
63
+ fill: #a0aec0 !important;
64
+ stroke: #a0aec0 !important;
65
+ }
66
+ '''
67
+ }
68
+
69
+ /* Improved node styling */
70
+ .node {{
71
+ cursor: pointer;
72
+ transition: opacity 0.2s ease;
73
+ }}
74
+
75
+ .node:hover {{
76
+ opacity: 0.8;
77
+ }}
78
+
79
+ /* Highlighting styles */
80
+ .highlighted {{
81
+ opacity: 1 !important;
82
+ filter: drop-shadow(0 0 8px {STANDARD_COLORS["root_node"]});
83
+ }}
84
+
85
+ .dimmed {{
86
+ opacity: 0.3;
87
+ }}
88
+ </style>
89
+ </head>
90
+ <body>
91
+ <div class="controls">
92
+ <div class="control-group">
93
+ <button id="downloadButton" class="toggle-btn">Download SVG</button>
94
+ <!-- Note: PNG download feature to be implemented in future version -->
95
+ </div>
96
+ </div>
97
+
98
+ <div id="graphDiv"></div>
99
+
100
+ <script src="https://cdn.jsdelivr.net/npm/svg-pan-zoom@3.5.0/dist/svg-pan-zoom.min.js"></script>
101
+ <script type="module">
102
+ import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11.9.0/dist/mermaid.esm.min.mjs';
103
+
104
+ // Initialize mermaid with theme configuration
105
+ mermaid.initialize({mermaid_config_js});
106
+
107
+ // Global variables
108
+ let panZoomInstance = null;
109
+
110
+ const drawDiagram = async function () {{
111
+ const element = document.querySelector('#graphDiv');
112
+ const graphDefinition = `{{{{diagram_definition}}}}`;
113
+
114
+ try {{
115
+ const {{ svg }} = await mermaid.render('mySvgId', graphDefinition);
116
+ element.innerHTML = svg.replace(/[ ]*max-width:[ 0-9\\.]*px;/i , '');
117
+
118
+ // Initialize pan & zoom
119
+ panZoomInstance = svgPanZoom('#mySvgId', {{
120
+ zoomEnabled: true,
121
+ controlIconsEnabled: true,
122
+ fit: true,
123
+ center: true,
124
+ minZoom: 0.1,
125
+ maxZoom: 10
126
+ }});
127
+
128
+ // Setup node interactions
129
+ setupNodeInteractions();
130
+
131
+ }} catch (error) {{
132
+ console.error('Error rendering diagram:', error);
133
+ element.innerHTML = `<p style="color: red; padding: 20px;">Error rendering diagram: ${{error.message}}</p>`;
134
+ }}
135
+ }};
136
+
137
+ const setupNodeInteractions = function() {{
138
+ const nodes = document.querySelectorAll('#mySvgId .node');
139
+
140
+ nodes.forEach(node => {{
141
+ node.style.cursor = 'pointer';
142
+ }});
143
+ }};
144
+
145
+ // Download functionality
146
+ document.getElementById('downloadButton').addEventListener('click', function() {{
147
+ downloadSVG();
148
+ }});
149
+
150
+ const downloadSVG = function() {{
151
+ const svg = document.querySelector('#mySvgId');
152
+ let svgData = new XMLSerializer().serializeToString(svg);
153
+
154
+ // Clean up pan & zoom controls
155
+ svgData = svgData.replace(/<g\\b[^>]*\\bclass="svg-pan-zoom-.*?".*?>.*?<\\/g>/g, '');
156
+ svgData = svgData.replace(/<\\/g><\\/svg>/, '</svg>');
157
+
158
+ const svgBlob = new Blob([svgData], {{type: 'image/svg+xml;charset=utf-8'}});
159
+ const svgUrl = URL.createObjectURL(svgBlob);
160
+ const downloadLink = document.createElement('a');
161
+ downloadLink.href = svgUrl;
162
+ downloadLink.download = 'dependency-diagram.svg';
163
+ document.body.appendChild(downloadLink);
164
+ downloadLink.click();
165
+ document.body.removeChild(downloadLink);
166
+ URL.revokeObjectURL(svgUrl);
167
+ }};
168
+
169
+ // Initialize the diagram
170
+ await drawDiagram();
171
+
172
+ // Keyboard shortcuts
173
+ document.addEventListener('keydown', (e) => {{
174
+ if (e.ctrlKey || e.metaKey) {{
175
+ switch(e.key) {{
176
+ case 's':
177
+ e.preventDefault();
178
+ downloadSVG();
179
+ break;
180
+ case 'r':
181
+ e.preventDefault();
182
+ if (panZoomInstance) {{
183
+ panZoomInstance.reset();
184
+ }}
185
+ break;
186
+ }}
187
+ }}
188
+ }});
189
+
190
+ </script>
191
+ </body>
192
+ </html>"""
193
+
194
+
195
+ def _dict_to_js_object(d: Dict[str, Any], indent: int = 0) -> str:
196
+ """Convert Python dict to JavaScript object string."""
197
+ if not isinstance(d, dict):
198
+ if isinstance(d, str):
199
+ return f'"{d}"'
200
+ elif isinstance(d, bool):
201
+ return str(d).lower()
202
+ else:
203
+ return str(d)
204
+
205
+ items = []
206
+ for key, value in d.items():
207
+ if isinstance(value, dict):
208
+ value_str = _dict_to_js_object(value, indent + 1)
209
+ elif isinstance(value, str):
210
+ value_str = f'"{value}"'
211
+ elif isinstance(value, bool):
212
+ value_str = str(value).lower()
213
+ else:
214
+ value_str = str(value)
215
+
216
+ items.append(f'"{key}": {value_str}')
217
+
218
+ return "{" + ", ".join(items) + "}"
@@ -3,25 +3,40 @@ from typing import List, Set, Tuple
3
3
 
4
4
  from jinja2 import BaseLoader, Environment
5
5
 
6
- from ..TEMPLATE import HTML_TEMPLATE
6
+ from ..enhanced_template import get_html_template
7
+ from ..themes import STANDARD_COLORS, get_theme
7
8
 
8
9
 
9
- def create_html_diagram(dependency_tree: str, output_filename: str, show_versions: bool = False) -> None:
10
+ def create_html_diagram(dependency_tree: str, output_filename: str, show_versions: bool = False, theme: str = "minimal") -> None:
11
+ """Create HTML diagram with theme support."""
12
+ theme_obj = get_theme(theme)
10
13
  mermaid_diagram: str = _convert_to_mermaid(dependency_tree, show_versions)
11
- template = Environment(loader=BaseLoader).from_string(HTML_TEMPLATE)
14
+
15
+ # Use enhanced template with theme
16
+ html_template = get_html_template(theme_obj)
17
+ template = Environment(loader=BaseLoader).from_string(html_template)
12
18
  rendered: str = template.render(diagram_definition=mermaid_diagram)
19
+
13
20
  parent_dir: Path = Path(output_filename).parent
14
21
  if not parent_dir.exists():
15
22
  parent_dir.mkdir(parents=True, exist_ok=True)
16
- with open(output_filename, "w") as f:
23
+ with open(output_filename, "w", encoding="utf-8") as f:
17
24
  f.write(rendered)
18
25
 
19
26
 
20
27
  def _convert_to_mermaid(dependency_tree: str, show_versions: bool = False) -> str:
21
- # generate a `graph LR` format for Mermaid
28
+ """Convert dependency tree to enhanced Mermaid diagram with styling."""
22
29
  lines: List[str] = dependency_tree.strip().split("\n")
23
- mermaid_lines: Set[str] = set()
30
+ relationships: Set[str] = set()
31
+ node_styles: Set[str] = set()
24
32
  previous_dependency: List[Tuple[str, int]] = []
33
+
34
+ # Track root and leaf nodes for styling, and store all node declarations
35
+ all_nodes: Set[str] = set()
36
+ parent_nodes: Set[str] = set()
37
+ child_nodes: Set[str] = set()
38
+ node_declarations: Set[str] = set()
39
+
25
40
  for line in lines:
26
41
  if not line:
27
42
  continue
@@ -30,17 +45,21 @@ def _convert_to_mermaid(dependency_tree: str, show_versions: bool = False) -> st
30
45
  parts: List[str] = line.split(":")
31
46
  if len(parts) < 3:
32
47
  continue
48
+
33
49
  if len(parts) == 4:
34
50
  group_id, artifact_id, app, version = parts
35
51
  if show_versions:
36
52
  node_label: str = f"{artifact_id}:{version}"
37
- mermaid_lines.add(f"\t{node_label};")
53
+ safe_node_id: str = _sanitize_node_id(f"{artifact_id}_{version}")
38
54
  else:
39
55
  node_label: str = artifact_id
40
- mermaid_lines.add(f"\t{artifact_id};")
56
+ safe_node_id: str = _sanitize_node_id(artifact_id)
57
+
58
+ node_declarations.add(f'\t{safe_node_id}["{node_label}"];')
59
+ all_nodes.add(safe_node_id)
41
60
  if previous_dependency: # Re initialize the list if it wasn't empty
42
61
  previous_dependency = []
43
- previous_dependency.append((node_label, 0)) # The second element is the depth
62
+ previous_dependency.append((safe_node_id, 0)) # The second element is the depth
44
63
  else:
45
64
  depth: int = len(parts[0].split(" ")) - 1
46
65
  if len(parts) == 6:
@@ -50,16 +69,64 @@ def _convert_to_mermaid(dependency_tree: str, show_versions: bool = False) -> st
50
69
 
51
70
  if show_versions:
52
71
  node_label: str = f"{artifact_id}:{version}"
72
+ safe_node_id: str = _sanitize_node_id(f"{artifact_id}_{version}")
53
73
  else:
54
74
  node_label: str = artifact_id
75
+ safe_node_id: str = _sanitize_node_id(artifact_id)
76
+
77
+ node_declarations.add(f'\t{safe_node_id}["{node_label}"];')
78
+ all_nodes.add(safe_node_id)
79
+ child_nodes.add(safe_node_id)
55
80
 
56
81
  if previous_dependency[-1][1] < depth:
57
- mermaid_lines.add(f"\t{previous_dependency[-1][0]} --> {node_label};")
58
- previous_dependency.append((node_label, depth))
82
+ parent_id = previous_dependency[-1][0]
83
+ parent_nodes.add(parent_id)
84
+ relationships.add(f"\t{parent_id} --> {safe_node_id};")
85
+ previous_dependency.append((safe_node_id, depth))
59
86
  else:
60
87
  # remove all dependencies that are deeper or equal to the current depth
61
88
  while previous_dependency and previous_dependency[-1][1] >= depth:
62
89
  previous_dependency.pop()
63
- mermaid_lines.add(f"\t{previous_dependency[-1][0]} --> {node_label};")
64
- previous_dependency.append((node_label, depth))
65
- return "graph LR\n" + "\n".join(mermaid_lines)
90
+ parent_id = previous_dependency[-1][0]
91
+ parent_nodes.add(parent_id)
92
+ relationships.add(f"\t{parent_id} --> {safe_node_id};")
93
+ previous_dependency.append((safe_node_id, depth))
94
+
95
+ # Add styling classes
96
+ root_nodes = all_nodes - child_nodes
97
+ leaf_nodes = all_nodes - parent_nodes
98
+
99
+ # Add node styling
100
+ for node in root_nodes:
101
+ node_styles.add(f"\tclass {node} rootNode;")
102
+ for node in leaf_nodes:
103
+ node_styles.add(f"\tclass {node} leafNode;")
104
+ for node in parent_nodes.intersection(child_nodes):
105
+ node_styles.add(f"\tclass {node} intermediateNode;")
106
+
107
+ # Build the complete diagram with standardized colors
108
+ diagram_parts = [
109
+ "graph LR",
110
+ *sorted(node_declarations),
111
+ *sorted(relationships),
112
+ "",
113
+ f"classDef rootNode fill:{STANDARD_COLORS['root_node']}20,stroke:{STANDARD_COLORS['root_node']},stroke-width:3px,color:#000;",
114
+ f"classDef leafNode fill:{STANDARD_COLORS['leaf_node']}20,stroke:{STANDARD_COLORS['leaf_node']},stroke-width:2px,color:#000;",
115
+ f"classDef intermediateNode fill:{STANDARD_COLORS['intermediate_node']}20,stroke:{STANDARD_COLORS['intermediate_node']},stroke-width:2px,color:#000;",
116
+ "",
117
+ *sorted(node_styles),
118
+ ]
119
+
120
+ return "\n".join(diagram_parts)
121
+
122
+
123
+ def _sanitize_node_id(node_id: str) -> str:
124
+ """Sanitize node ID for Mermaid compatibility."""
125
+ # Replace special characters that could break Mermaid syntax
126
+ import re
127
+
128
+ sanitized = re.sub(r"[^a-zA-Z0-9_]", "_", node_id)
129
+ # Ensure it starts with a letter or underscore
130
+ if sanitized and not sanitized[0].isalpha() and sanitized[0] != "_":
131
+ sanitized = "_" + sanitized
132
+ return sanitized or "node"
@@ -0,0 +1,189 @@
1
+ """Theme configurations for mvn-tree-visualizer diagrams."""
2
+
3
+ from typing import Any, Dict
4
+
5
+
6
+ class Theme:
7
+ """Base theme configuration class."""
8
+
9
+ def __init__(
10
+ self,
11
+ name: str,
12
+ mermaid_theme: str = "default",
13
+ background_color: str = "#ffffff",
14
+ custom_css: str = "",
15
+ mermaid_config: Dict[str, Any] = None,
16
+ ):
17
+ self.name = name
18
+ self.mermaid_theme = mermaid_theme
19
+ self.background_color = background_color
20
+ self.custom_css = custom_css
21
+ self.mermaid_config = mermaid_config or {}
22
+
23
+
24
+ # Standard color scheme for consistency across themes
25
+ # Root nodes: Blue shades, Intermediate nodes: Orange shades, Leaf nodes: Green shades
26
+ STANDARD_COLORS = {
27
+ "root_node": "#3B82F6", # Blue for root nodes
28
+ "intermediate_node": "#F59E0B", # Orange for intermediate nodes
29
+ "leaf_node": "#10B981", # Green for leaf nodes
30
+ }
31
+
32
+ # Predefined themes
33
+ THEMES = {
34
+ "minimal": Theme(
35
+ name="minimal",
36
+ mermaid_theme="neutral",
37
+ background_color="#ffffff",
38
+ custom_css="""
39
+ body {
40
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
41
+ margin: 0;
42
+ padding: 20px;
43
+ background-color: #ffffff;
44
+ color: #000000;
45
+ line-height: 1.6;
46
+ height: 100vh;
47
+ box-sizing: border-box;
48
+ }
49
+ #graphDiv {
50
+ background-color: #ffffff;
51
+ border: 1px solid #000000;
52
+ padding: 20px;
53
+ margin-bottom: 20px;
54
+ height: calc(100vh - 120px);
55
+ overflow: hidden;
56
+ }
57
+ .controls {
58
+ margin-bottom: 20px;
59
+ display: flex;
60
+ gap: 10px;
61
+ flex-wrap: wrap;
62
+ align-items: center;
63
+ }
64
+ .control-group {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: 8px;
68
+ }
69
+ .control-label {
70
+ font-size: 12px;
71
+ font-weight: normal;
72
+ color: #000000;
73
+ text-transform: uppercase;
74
+ }
75
+ .toggle-btn {
76
+ background-color: #000000;
77
+ color: white;
78
+ border: none;
79
+ padding: 8px 16px;
80
+ cursor: pointer;
81
+ font-family: inherit;
82
+ font-size: 12px;
83
+ text-transform: uppercase;
84
+ letter-spacing: 1px;
85
+ transition: opacity 0.2s;
86
+ }
87
+ .toggle-btn:hover {
88
+ opacity: 0.8;
89
+ }
90
+ """,
91
+ mermaid_config={
92
+ "theme": "neutral",
93
+ "themeVariables": {
94
+ "primaryColor": STANDARD_COLORS["root_node"],
95
+ "primaryTextColor": "#000000",
96
+ "primaryBorderColor": "#000000",
97
+ "lineColor": "#000000",
98
+ "secondaryColor": "#f5f5f5",
99
+ "tertiaryColor": "#ffffff",
100
+ },
101
+ },
102
+ ),
103
+ "dark": Theme(
104
+ name="dark",
105
+ mermaid_theme="forest",
106
+ background_color="#2d3748",
107
+ custom_css="""
108
+ body {
109
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
110
+ margin: 0;
111
+ padding: 20px;
112
+ background-color: #2d3748;
113
+ color: #f7fafc;
114
+ line-height: 1.6;
115
+ height: 100vh;
116
+ box-sizing: border-box;
117
+ }
118
+ #graphDiv {
119
+ background-color: #2d3748;
120
+ border: 1px solid #e2e8f0;
121
+ padding: 20px;
122
+ margin-bottom: 20px;
123
+ height: calc(100vh - 120px);
124
+ overflow: hidden;
125
+ }
126
+ .controls {
127
+ margin-bottom: 20px;
128
+ display: flex;
129
+ gap: 10px;
130
+ flex-wrap: wrap;
131
+ align-items: center;
132
+ }
133
+ .control-group {
134
+ display: flex;
135
+ align-items: center;
136
+ gap: 8px;
137
+ }
138
+ .control-label {
139
+ font-size: 12px;
140
+ font-weight: normal;
141
+ color: #f7fafc;
142
+ text-transform: uppercase;
143
+ }
144
+ .toggle-btn {
145
+ background-color: #f7fafc;
146
+ color: #2d3748;
147
+ border: none;
148
+ padding: 8px 16px;
149
+ cursor: pointer;
150
+ font-family: inherit;
151
+ font-size: 12px;
152
+ text-transform: uppercase;
153
+ letter-spacing: 1px;
154
+ transition: opacity 0.2s;
155
+ }
156
+ .toggle-btn:hover {
157
+ opacity: 0.8;
158
+ }
159
+ """,
160
+ mermaid_config={
161
+ "theme": "forest",
162
+ "themeVariables": {
163
+ "primaryColor": STANDARD_COLORS["root_node"],
164
+ "primaryTextColor": "#ffffff",
165
+ "primaryBorderColor": "#e2e8f0",
166
+ "lineColor": "#a0aec0",
167
+ "secondaryColor": "#4a5568",
168
+ "tertiaryColor": "#2d3748",
169
+ "background": "#2d3748",
170
+ "mainBkg": "#4a5568",
171
+ "nodeBkg": "#4a5568",
172
+ "clusterBkg": "#2d3748",
173
+ "edgeLabelBackground": "#2d3748",
174
+ "nodeTextColor": "#ffffff",
175
+ "textColor": "#ffffff",
176
+ },
177
+ },
178
+ ),
179
+ }
180
+
181
+
182
+ def get_theme(theme_name: str) -> Theme:
183
+ """Get a theme by name, fallback to minimal if not found."""
184
+ return THEMES.get(theme_name, THEMES["minimal"])
185
+
186
+
187
+ def get_available_themes() -> list[str]:
188
+ """Get list of available theme names."""
189
+ return list(THEMES.keys())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mvn-tree-visualizer
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: A simple command line tool to visualize the dependency tree of a Maven project in a graphical format.
5
5
  Project-URL: source, https://github.com/dyka3773/mvn-tree-visualizer
6
6
  Author-email: Iraklis Konsoulas <dyka3773@gmail.com>
@@ -55,12 +55,14 @@ pip install mvn-tree-visualizer
55
55
  - **🌐 Multiple Output Formats:**
56
56
  - **HTML:** Generates an interactive HTML diagram of your dependency tree using Mermaid.js.
57
57
  - **JSON:** Creates a structured JSON representation of the dependency tree, perfect for scripting or integration with other tools.
58
+ - **🎨 Theme System:** Choose from 2 built-in themes (minimal, dark) for clean and consistent diagram styling.
58
59
  - **🔄 Watch Mode:** Automatically regenerates diagrams when Maven dependency files change using the `--watch` flag.
59
60
  - **📋 Version Display:** Show or hide dependency versions in both HTML and JSON outputs using the `--show-versions` flag.
60
61
  - **⚡ Easy to Use:** A simple command-line interface that gets the job done with minimal configuration.
61
62
  - **📂 File Merging:** Automatically finds and merges multiple `maven_dependency_file` files from different subdirectories.
62
63
  - **🎨 Customizable Output:** Specify the output file name and location.
63
- - **💾 SVG Export:** Download the generated diagram as an SVG file directly from the HTML page.
64
+ - **💾 Enhanced Downloads:** Download diagrams as SVG or high-resolution PNG directly from the HTML page.
65
+ - **🖱️ Interactive Features:** Hover tooltips, click-to-highlight connections, pan/zoom controls, and keyboard shortcuts.
64
66
 
65
67
  ## How to Use
66
68
 
@@ -93,6 +95,15 @@ mvn_tree_visualizer --filename "maven_dependency_file" --output "dependencies.js
93
95
  mvn_tree_visualizer --filename "maven_dependency_file" --output "diagram.html" --show-versions
94
96
  ```
95
97
 
98
+ #### With Custom Themes
99
+ ```bash
100
+ # Dark theme for low-light environments
101
+ mvn_tree_visualizer --filename "maven_dependency_file" --output "diagram.html" --theme dark
102
+
103
+ # Default minimal theme (clean monospace design)
104
+ mvn_tree_visualizer --filename "maven_dependency_file" --output "diagram.html"
105
+ ```
106
+
96
107
  #### JSON Output with Versions
97
108
  ```bash
98
109
  mvn_tree_visualizer --filename "maven_dependency_file" --output "dependencies.json" --format json --show-versions
@@ -129,12 +140,20 @@ Each example includes:
129
140
  | `--filename` | The name of the file containing the Maven dependency tree | `maven_dependency_file` |
130
141
  | `--output` | The name of the output file | `diagram.html` |
131
142
  | `--format` | The output format (`html` or `json`) | `html` |
143
+ | `--theme` | Theme for HTML diagrams (`minimal`, `dark`) | `minimal` |
132
144
  | `--show-versions` | Show dependency versions in the diagram | `False` |
133
145
  | `--watch` | Watch for file changes and auto-regenerate diagram | `False` |
134
146
  | `--directory` | The directory to scan for the Maven dependency file(s) | current directory |
135
147
  | `--keep-tree` | Keep the intermediate `dependency_tree.txt` file | `False` |
136
148
  | `--help` | Show the help message and exit | - |
137
149
 
150
+ ### Theme Options
151
+
152
+ - **`minimal`**: Clean monospace design with simple black borders (default)
153
+ - **`dark`**: Same minimal styling but with white text on black background
154
+
155
+ 📖 **See the complete [Theme Documentation](docs/THEMES.md) for detailed information about themes and interactive features.**
156
+
138
157
  ## Performance
139
158
 
140
159
  **For Large Projects:**
@@ -1,16 +1,17 @@
1
- mvn_tree_visualizer/TEMPLATE.py,sha256=WIQfSNBygUZVkBrERq7QzqouGURA0NYVqUUm-11wMvo,2499
2
1
  mvn_tree_visualizer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
2
  mvn_tree_visualizer/__main__.py,sha256=yIQFAdWjthKAFbSzzRuz5_YGlK0c6BnR2ypjNRDq180,82
4
- mvn_tree_visualizer/cli.py,sha256=33K5ujgB85xejcSUKMNAwirpLfnGMGfrnhqyLFOy69k,8219
3
+ mvn_tree_visualizer/cli.py,sha256=Gufk14zH74lqSTz2o2KPPEmNLvOHKHLyno-eSyXqahw,8504
5
4
  mvn_tree_visualizer/diagram.py,sha256=UfvP_J4Im4JQLe3EWlY3TsP4tua3oYk5NiCGbZNQwoA,933
5
+ mvn_tree_visualizer/enhanced_template.py,sha256=I35fNkZrlA5jYdyjPW0jU4FH4FF2HEagtwcUX6fcmMc,7050
6
6
  mvn_tree_visualizer/exceptions.py,sha256=R4nnJ0xrOpd84GfPD9rFSDk40etDLoph7iZpj1CCR0c,543
7
7
  mvn_tree_visualizer/file_watcher.py,sha256=JtmV1KW08_Az-XqpKhcd342WpxV1vUW-Dge9lodjjJY,2284
8
8
  mvn_tree_visualizer/get_dependencies_in_one_file.py,sha256=nXhEhU-dI7tXa3TpoW1pv2t86t1K0yppSw8FYDtmTlQ,1973
9
+ mvn_tree_visualizer/themes.py,sha256=91asg9VIqa7q2sUmgRD-Fw5wJ6kKsVWlPJ-bX9kGZhw,5469
9
10
  mvn_tree_visualizer/validation.py,sha256=UQ194gHiVS8UnJpp90sCM-Vjn3aeXT6scdwOplAoaSE,3689
10
- mvn_tree_visualizer/outputs/html_output.py,sha256=QwUZRzNUCKrpdOq6BHadIJSU47W_1Kat-ouzwue-olA,2857
11
+ mvn_tree_visualizer/outputs/html_output.py,sha256=Y0IY-UF0UMTa5w8mVFoLidgcW6BUBTxASO0iRo26hH4,5531
11
12
  mvn_tree_visualizer/outputs/json_output.py,sha256=cXntw9ndE_BcrmFnuV61cEwZaRMp9Ev0SxaK1SUedlw,2037
12
- mvn_tree_visualizer-1.3.0.dist-info/METADATA,sha256=YHhhNT8CjZUB_rSbdOip-7anjiGD7NxmoYPdl4bZw50,7834
13
- mvn_tree_visualizer-1.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- mvn_tree_visualizer-1.3.0.dist-info/entry_points.txt,sha256=Mu3QZhrlvbYuCxqmluVGi2efgKjkQY6T8Opf-vdb7hU,68
15
- mvn_tree_visualizer-1.3.0.dist-info/licenses/LICENSE,sha256=4zi6unpe17RUDMBu7ebh14jdbyvyeT-UA3n8Zl7aW74,1075
16
- mvn_tree_visualizer-1.3.0.dist-info/RECORD,,
13
+ mvn_tree_visualizer-1.4.0.dist-info/METADATA,sha256=AIAeZ3goQMeRVtZDxJLIRnlw37nBL1kCMHHRnjk2XuM,8756
14
+ mvn_tree_visualizer-1.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
+ mvn_tree_visualizer-1.4.0.dist-info/entry_points.txt,sha256=Mu3QZhrlvbYuCxqmluVGi2efgKjkQY6T8Opf-vdb7hU,68
16
+ mvn_tree_visualizer-1.4.0.dist-info/licenses/LICENSE,sha256=4zi6unpe17RUDMBu7ebh14jdbyvyeT-UA3n8Zl7aW74,1075
17
+ mvn_tree_visualizer-1.4.0.dist-info/RECORD,,
@@ -1,61 +0,0 @@
1
- HTML_TEMPLATE = r"""<html></html>
2
- <head>
3
- <style type="text/css">
4
- #mySvgId {
5
- height: 90%;
6
- width: 90%;
7
- }
8
- </style>
9
- <title>Dependency Diagram</title>
10
- </head>
11
- <body>
12
- <div id="graphDiv"></div>
13
- <button id="downloadButton">Download SVG</button>
14
- <script src="https://cdn.jsdelivr.net/npm/svg-pan-zoom@3.5.0/dist/svg-pan-zoom.min.js"></script>
15
- <script type="module">
16
- import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10.9.0/dist/mermaid.esm.min.mjs';
17
- mermaid.initialize({
18
- startOnLoad:true,
19
- sequence:{
20
- useMaxWidth:false
21
- }
22
- });
23
-
24
- const drawDiagram = async function () {
25
- const element = document.querySelector('#graphDiv');
26
- const graphDefinition = `
27
- {{diagram_definition}}
28
- `;
29
- const { svg } = await mermaid.render('mySvgId', graphDefinition);
30
- element.innerHTML = svg.replace(/[ ]*max-width:[ 0-9\.]*px;/i , '');
31
- var panZoomTiger = svgPanZoom('#mySvgId', {
32
- zoomEnabled: true,
33
- controlIconsEnabled: true,
34
- fit: true,
35
- center: true
36
- })
37
- };
38
- await drawDiagram();
39
-
40
- // Add event listener to the download button to download the SVG without the pan & zoom buttons
41
- document.getElementById('downloadButton').addEventListener('click', function() {
42
- const svg = document.querySelector('#mySvgId');
43
- let svgData = new XMLSerializer().serializeToString(svg);
44
-
45
- // To remove the pan & zoom buttons of the diagram, any element whose class contains the string 'svg-pan-zoom-*' should be removed
46
- svgData = svgData.replace(/<g\b[^>]*\bclass="svg-pan-zoom-.*?".*?>.*?<\/g>/g, '');
47
- // The above leaves out a closing </g> tag before the final </svg> tag, so we need to remove it
48
- svgData = svgData.replace(/<\/g><\/svg>/, '</svg>');
49
-
50
- const svgBlob = new Blob([svgData], {type: 'image/svg+xml;charset=utf-8'});
51
- const svgUrl = URL.createObjectURL(svgBlob);
52
- const downloadLink = document.createElement('a');
53
- downloadLink.href = svgUrl;
54
- downloadLink.download = 'diagram.svg';
55
- document.body.appendChild(downloadLink);
56
- downloadLink.click();
57
- document.body.removeChild(downloadLink);
58
- });
59
- </script>
60
- </body>
61
- </html>"""