mcp-vector-search 0.12.1__py3-none-any.whl → 0.12.2__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 mcp-vector-search might be problematic. Click here for more details.
- mcp_vector_search/__init__.py +2 -2
- mcp_vector_search/cli/commands/visualize.py +22 -12
- mcp_vector_search/visualization/favicon-v1-1024.png +0 -0
- mcp_vector_search/visualization/favicon-v1-128.png +0 -0
- mcp_vector_search/visualization/favicon-v1-16.png +0 -0
- mcp_vector_search/visualization/favicon-v1-256.png +0 -0
- mcp_vector_search/visualization/favicon-v1-32.png +0 -0
- mcp_vector_search/visualization/favicon-v1-512.png +0 -0
- mcp_vector_search/visualization/favicon-v1-64.png +0 -0
- mcp_vector_search/visualization/favicon-v1.ico +0 -0
- mcp_vector_search/visualization/favicon-v2-1024.png +0 -0
- mcp_vector_search/visualization/favicon-v2-128.png +0 -0
- mcp_vector_search/visualization/favicon-v2-16.png +0 -0
- mcp_vector_search/visualization/favicon-v2-256.png +0 -0
- mcp_vector_search/visualization/favicon-v2-32.png +0 -0
- mcp_vector_search/visualization/favicon-v2-512.png +0 -0
- mcp_vector_search/visualization/favicon-v2-64.png +0 -0
- mcp_vector_search/visualization/favicon-v2.ico +0 -0
- mcp_vector_search/visualization/favicon-v3-1024.png +0 -0
- mcp_vector_search/visualization/favicon-v3-128.png +0 -0
- mcp_vector_search/visualization/favicon-v3-16.png +0 -0
- mcp_vector_search/visualization/favicon-v3-256.png +0 -0
- mcp_vector_search/visualization/favicon-v3-32.png +0 -0
- mcp_vector_search/visualization/favicon-v3-512.png +0 -0
- mcp_vector_search/visualization/favicon-v3-64.png +0 -0
- mcp_vector_search/visualization/favicon-v3.ico +0 -0
- mcp_vector_search/visualization/index.html +522 -172
- {mcp_vector_search-0.12.1.dist-info → mcp_vector_search-0.12.2.dist-info}/METADATA +62 -2
- {mcp_vector_search-0.12.1.dist-info → mcp_vector_search-0.12.2.dist-info}/RECORD +32 -8
- {mcp_vector_search-0.12.1.dist-info → mcp_vector_search-0.12.2.dist-info}/WHEEL +0 -0
- {mcp_vector_search-0.12.1.dist-info → mcp_vector_search-0.12.2.dist-info}/entry_points.txt +0 -0
- {mcp_vector_search-0.12.1.dist-info → mcp_vector_search-0.12.2.dist-info}/licenses/LICENSE +0 -0
mcp_vector_search/__init__.py
CHANGED
|
@@ -1069,12 +1069,18 @@ def _create_visualization_html(html_file: Path) -> None:
|
|
|
1069
1069
|
.style("pointer-events", "none")
|
|
1070
1070
|
.text(d => collapsedNodes.has(d.id) ? "+" : "−");
|
|
1071
1071
|
|
|
1072
|
-
// Add labels (show
|
|
1072
|
+
// Add labels (show actual import statement for L1 nodes)
|
|
1073
1073
|
node.append("text")
|
|
1074
1074
|
.text(d => {
|
|
1075
1075
|
// L1 (depth 1) nodes are imports
|
|
1076
1076
|
if (d.depth === 1 && d.type !== 'directory' && d.type !== 'file') {
|
|
1077
|
-
|
|
1077
|
+
if (d.content) {
|
|
1078
|
+
// Extract first line of import statement
|
|
1079
|
+
const importLine = d.content.split('\n')[0].trim();
|
|
1080
|
+
// Truncate if too long (max 60 chars)
|
|
1081
|
+
return importLine.length > 60 ? importLine.substring(0, 57) + '...' : importLine;
|
|
1082
|
+
}
|
|
1083
|
+
return d.name; // Fallback to name if no content
|
|
1078
1084
|
}
|
|
1079
1085
|
return d.name;
|
|
1080
1086
|
})
|
|
@@ -1230,9 +1236,14 @@ def _create_visualization_html(html_file: Path) -> None:
|
|
|
1230
1236
|
const meta = document.getElementById('pane-meta');
|
|
1231
1237
|
const content = document.getElementById('pane-content');
|
|
1232
1238
|
|
|
1233
|
-
// Set title with
|
|
1239
|
+
// Set title with actual import statement for L1 nodes
|
|
1234
1240
|
if (node.depth === 1 && node.type !== 'directory' && node.type !== 'file') {
|
|
1235
|
-
|
|
1241
|
+
if (node.content) {
|
|
1242
|
+
const importLine = node.content.split('\n')[0].trim();
|
|
1243
|
+
title.textContent = importLine;
|
|
1244
|
+
} else {
|
|
1245
|
+
title.textContent = `Import: ${node.name}`;
|
|
1246
|
+
}
|
|
1236
1247
|
} else {
|
|
1237
1248
|
title.textContent = node.name;
|
|
1238
1249
|
}
|
|
@@ -1360,10 +1371,15 @@ def _create_visualization_html(html_file: Path) -> None:
|
|
|
1360
1371
|
}
|
|
1361
1372
|
|
|
1362
1373
|
function showImportDetails(node, container) {
|
|
1363
|
-
// L1 nodes are import statements
|
|
1374
|
+
// L1 nodes are import statements - show import content prominently
|
|
1364
1375
|
const importHtml = `
|
|
1365
1376
|
<div class="import-details">
|
|
1366
|
-
|
|
1377
|
+
${node.content ? `
|
|
1378
|
+
<div style="margin-bottom: 16px;">
|
|
1379
|
+
<div class="detail-label" style="margin-bottom: 8px;">Import Statement:</div>
|
|
1380
|
+
<pre><code>${escapeHtml(node.content)}</code></pre>
|
|
1381
|
+
</div>
|
|
1382
|
+
` : '<p style="color: #8b949e;">No import content available</p>'}
|
|
1367
1383
|
<div class="detail-row">
|
|
1368
1384
|
<span class="detail-label">File:</span> ${node.file_path}
|
|
1369
1385
|
</div>
|
|
@@ -1377,12 +1393,6 @@ def _create_visualization_html(html_file: Path) -> None:
|
|
|
1377
1393
|
<span class="detail-label">Language:</span> ${node.language}
|
|
1378
1394
|
</div>
|
|
1379
1395
|
` : ''}
|
|
1380
|
-
${node.content ? `
|
|
1381
|
-
<div style="margin-top: 16px;">
|
|
1382
|
-
<div class="detail-label" style="margin-bottom: 8px;">Import Statement:</div>
|
|
1383
|
-
<pre><code>${escapeHtml(node.content)}</code></pre>
|
|
1384
|
-
</div>
|
|
1385
|
-
` : ''}
|
|
1386
1396
|
</div>
|
|
1387
1397
|
`;
|
|
1388
1398
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -2,10 +2,15 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
|
-
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
|
6
|
-
<meta http-equiv="Pragma" content="no-cache">
|
|
7
|
-
<meta http-equiv="Expires" content="0">
|
|
8
5
|
<title>Code Chunk Relationship Graph</title>
|
|
6
|
+
|
|
7
|
+
<!-- Favicon -->
|
|
8
|
+
<link rel="icon" type="image/x-icon" href="favicon-v1.ico">
|
|
9
|
+
<link rel="icon" type="image/png" sizes="16x16" href="favicon-v1-16.png">
|
|
10
|
+
<link rel="icon" type="image/png" sizes="32x32" href="favicon-v1-32.png">
|
|
11
|
+
<link rel="icon" type="image/png" sizes="64x64" href="favicon-v1-64.png">
|
|
12
|
+
<link rel="apple-touch-icon" sizes="180x180" href="favicon-v1-512.png">
|
|
13
|
+
|
|
9
14
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
|
10
15
|
<style>
|
|
11
16
|
body {
|
|
@@ -80,26 +85,43 @@
|
|
|
80
85
|
cursor: pointer;
|
|
81
86
|
stroke: #c9d1d9;
|
|
82
87
|
stroke-width: 1.5px;
|
|
88
|
+
transition: all 0.2s ease;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.node circle:hover,
|
|
92
|
+
.node rect:hover {
|
|
93
|
+
stroke-width: 3px !important;
|
|
94
|
+
filter: brightness(1.2);
|
|
95
|
+
cursor: pointer;
|
|
83
96
|
}
|
|
84
97
|
|
|
85
|
-
.node.directory circle { fill: #ffa657; }
|
|
86
|
-
.node.file circle { fill: #58a6ff; }
|
|
87
98
|
.node.module circle { fill: #238636; }
|
|
88
99
|
.node.class circle { fill: #1f6feb; }
|
|
89
100
|
.node.function circle { fill: #d29922; }
|
|
90
101
|
.node.method circle { fill: #8957e5; }
|
|
91
|
-
.node.imports circle { fill: #6e7681; }
|
|
92
|
-
.node.text circle { fill: #6e7681; }
|
|
93
102
|
.node.code circle { fill: #6e7681; }
|
|
103
|
+
.node.file circle {
|
|
104
|
+
fill: none;
|
|
105
|
+
stroke: #58a6ff;
|
|
106
|
+
stroke-width: 2px;
|
|
107
|
+
stroke-dasharray: 5,3;
|
|
108
|
+
opacity: 0.6;
|
|
109
|
+
}
|
|
110
|
+
.node.directory circle {
|
|
111
|
+
fill: none;
|
|
112
|
+
stroke: #79c0ff;
|
|
113
|
+
stroke-width: 2px;
|
|
114
|
+
stroke-dasharray: 3,3;
|
|
115
|
+
opacity: 0.5;
|
|
116
|
+
}
|
|
94
117
|
.node.subproject circle { fill: #da3633; stroke-width: 3px; }
|
|
95
118
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
font-weight: bold;
|
|
119
|
+
/* Non-code document nodes - squares */
|
|
120
|
+
.node.docstring rect { fill: #8b949e; }
|
|
121
|
+
.node.comment rect { fill: #6e7681; }
|
|
122
|
+
.node rect {
|
|
123
|
+
stroke: #c9d1d9;
|
|
124
|
+
stroke-width: 1.5px;
|
|
103
125
|
}
|
|
104
126
|
|
|
105
127
|
.node text {
|
|
@@ -110,10 +132,19 @@
|
|
|
110
132
|
user-select: none;
|
|
111
133
|
}
|
|
112
134
|
|
|
135
|
+
.node-icon {
|
|
136
|
+
font-size: 35px !important;
|
|
137
|
+
text-anchor: middle;
|
|
138
|
+
pointer-events: none;
|
|
139
|
+
user-select: none;
|
|
140
|
+
fill: #0d1117;
|
|
141
|
+
font-weight: bold;
|
|
142
|
+
}
|
|
143
|
+
|
|
113
144
|
.link {
|
|
114
|
-
stroke: #
|
|
115
|
-
stroke-opacity: 0.
|
|
116
|
-
stroke-width:
|
|
145
|
+
stroke: #30363d;
|
|
146
|
+
stroke-opacity: 0.6;
|
|
147
|
+
stroke-width: 1.5px;
|
|
117
148
|
}
|
|
118
149
|
|
|
119
150
|
.link.dependency {
|
|
@@ -144,73 +175,141 @@
|
|
|
144
175
|
color: #8b949e;
|
|
145
176
|
}
|
|
146
177
|
|
|
147
|
-
#
|
|
148
|
-
position:
|
|
149
|
-
top:
|
|
150
|
-
right:
|
|
151
|
-
width:
|
|
152
|
-
|
|
153
|
-
background: rgba(13, 17, 23, 0.
|
|
154
|
-
border: 1px solid #30363d;
|
|
155
|
-
border-radius: 6px;
|
|
156
|
-
padding: 16px;
|
|
178
|
+
#content-pane {
|
|
179
|
+
position: fixed;
|
|
180
|
+
top: 0;
|
|
181
|
+
right: 0;
|
|
182
|
+
width: 600px;
|
|
183
|
+
height: 100vh;
|
|
184
|
+
background: rgba(13, 17, 23, 0.98);
|
|
185
|
+
border-left: 1px solid #30363d;
|
|
157
186
|
overflow-y: auto;
|
|
158
|
-
box-shadow: 0
|
|
159
|
-
|
|
187
|
+
box-shadow: -4px 0 24px rgba(0, 0, 0, 0.5);
|
|
188
|
+
transform: translateX(100%);
|
|
189
|
+
transition: transform 0.3s ease-in-out;
|
|
190
|
+
z-index: 1000;
|
|
160
191
|
}
|
|
161
192
|
|
|
162
|
-
#
|
|
163
|
-
|
|
193
|
+
#content-pane.visible {
|
|
194
|
+
transform: translateX(0);
|
|
164
195
|
}
|
|
165
196
|
|
|
166
|
-
#
|
|
167
|
-
|
|
168
|
-
|
|
197
|
+
#content-pane .pane-header {
|
|
198
|
+
position: sticky;
|
|
199
|
+
top: 0;
|
|
200
|
+
background: rgba(13, 17, 23, 0.98);
|
|
201
|
+
padding: 20px;
|
|
169
202
|
border-bottom: 1px solid #30363d;
|
|
203
|
+
z-index: 1;
|
|
170
204
|
}
|
|
171
205
|
|
|
172
|
-
#
|
|
173
|
-
font-size:
|
|
206
|
+
#content-pane .pane-title {
|
|
207
|
+
font-size: 16px;
|
|
174
208
|
font-weight: bold;
|
|
175
209
|
color: #58a6ff;
|
|
176
|
-
margin-bottom:
|
|
210
|
+
margin-bottom: 8px;
|
|
211
|
+
padding-right: 30px;
|
|
177
212
|
}
|
|
178
213
|
|
|
179
|
-
#
|
|
180
|
-
font-size:
|
|
214
|
+
#content-pane .pane-meta {
|
|
215
|
+
font-size: 12px;
|
|
181
216
|
color: #8b949e;
|
|
182
217
|
}
|
|
183
218
|
|
|
184
|
-
#
|
|
185
|
-
|
|
219
|
+
#content-pane .collapse-btn {
|
|
220
|
+
position: absolute;
|
|
221
|
+
top: 20px;
|
|
222
|
+
right: 20px;
|
|
186
223
|
cursor: pointer;
|
|
187
224
|
color: #8b949e;
|
|
188
|
-
font-size:
|
|
225
|
+
font-size: 24px;
|
|
189
226
|
line-height: 1;
|
|
190
|
-
|
|
227
|
+
background: none;
|
|
228
|
+
border: none;
|
|
229
|
+
padding: 0;
|
|
230
|
+
transition: color 0.2s;
|
|
191
231
|
}
|
|
192
232
|
|
|
193
|
-
#
|
|
233
|
+
#content-pane .collapse-btn:hover {
|
|
194
234
|
color: #c9d1d9;
|
|
195
235
|
}
|
|
196
236
|
|
|
197
|
-
#
|
|
237
|
+
#content-pane .pane-content {
|
|
238
|
+
padding: 20px;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
#content-pane pre {
|
|
198
242
|
margin: 0;
|
|
199
|
-
padding:
|
|
243
|
+
padding: 16px;
|
|
200
244
|
background: #0d1117;
|
|
201
245
|
border: 1px solid #30363d;
|
|
202
246
|
border-radius: 6px;
|
|
203
247
|
overflow-x: auto;
|
|
204
|
-
font-size:
|
|
205
|
-
line-height: 1.
|
|
248
|
+
font-size: 12px;
|
|
249
|
+
line-height: 1.6;
|
|
206
250
|
}
|
|
207
251
|
|
|
208
|
-
#
|
|
252
|
+
#content-pane code {
|
|
209
253
|
color: #c9d1d9;
|
|
210
254
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
211
255
|
}
|
|
212
256
|
|
|
213
|
-
.
|
|
257
|
+
#content-pane .directory-list {
|
|
258
|
+
list-style: none;
|
|
259
|
+
padding: 0;
|
|
260
|
+
margin: 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
#content-pane .directory-list li {
|
|
264
|
+
padding: 8px 12px;
|
|
265
|
+
margin: 4px 0;
|
|
266
|
+
background: #161b22;
|
|
267
|
+
border: 1px solid #30363d;
|
|
268
|
+
border-radius: 4px;
|
|
269
|
+
font-size: 12px;
|
|
270
|
+
display: flex;
|
|
271
|
+
align-items: center;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
#content-pane .directory-list .item-icon {
|
|
275
|
+
margin-right: 8px;
|
|
276
|
+
font-size: 14px;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
#content-pane .directory-list .item-type {
|
|
280
|
+
margin-left: auto;
|
|
281
|
+
padding-left: 12px;
|
|
282
|
+
font-size: 10px;
|
|
283
|
+
color: #8b949e;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
#content-pane .import-details {
|
|
287
|
+
background: #161b22;
|
|
288
|
+
border: 1px solid #30363d;
|
|
289
|
+
border-radius: 6px;
|
|
290
|
+
padding: 16px;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
#content-pane .import-details .import-statement {
|
|
294
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
295
|
+
font-size: 12px;
|
|
296
|
+
color: #79c0ff;
|
|
297
|
+
margin-bottom: 12px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
#content-pane .import-details .detail-row {
|
|
301
|
+
font-size: 11px;
|
|
302
|
+
color: #8b949e;
|
|
303
|
+
margin: 4px 0;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
#content-pane .import-details .detail-label {
|
|
307
|
+
color: #c9d1d9;
|
|
308
|
+
font-weight: 600;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.node.highlighted circle,
|
|
312
|
+
.node.highlighted rect {
|
|
214
313
|
stroke: #f0e68c;
|
|
215
314
|
stroke-width: 3px;
|
|
216
315
|
filter: drop-shadow(0 0 8px #f0e68c);
|
|
@@ -228,25 +327,37 @@
|
|
|
228
327
|
<h3>Legend</h3>
|
|
229
328
|
<div class="legend">
|
|
230
329
|
<div class="legend-item">
|
|
231
|
-
|
|
330
|
+
<span class="legend-color" style="background: #da3633;"></span> Subproject
|
|
331
|
+
</div>
|
|
332
|
+
<div class="legend-item">
|
|
333
|
+
<span class="legend-color" style="border: 2px dashed #79c0ff; border-radius: 50%; background: transparent;"></span> Directory
|
|
334
|
+
</div>
|
|
335
|
+
<div class="legend-item">
|
|
336
|
+
<span class="legend-color" style="border: 2px dashed #58a6ff; border-radius: 50%; background: transparent;"></span> File
|
|
337
|
+
</div>
|
|
338
|
+
<div class="legend-item">
|
|
339
|
+
<span class="legend-color" style="background: #238636;"></span> Module
|
|
232
340
|
</div>
|
|
233
341
|
<div class="legend-item">
|
|
234
|
-
|
|
342
|
+
<span class="legend-color" style="background: #1f6feb;"></span> Class
|
|
235
343
|
</div>
|
|
236
344
|
<div class="legend-item">
|
|
237
|
-
|
|
345
|
+
<span class="legend-color" style="background: #d29922;"></span> Function
|
|
238
346
|
</div>
|
|
239
347
|
<div class="legend-item">
|
|
240
|
-
|
|
348
|
+
<span class="legend-color" style="background: #8957e5;"></span> Method
|
|
241
349
|
</div>
|
|
242
350
|
<div class="legend-item">
|
|
243
|
-
|
|
351
|
+
<span class="legend-color" style="background: #6e7681;"></span> Code
|
|
352
|
+
</div>
|
|
353
|
+
<div class="legend-item" style="font-style: italic; color: #79c0ff;">
|
|
354
|
+
<span class="legend-color" style="background: #6e7681;"></span> Import
|
|
244
355
|
</div>
|
|
245
356
|
<div class="legend-item">
|
|
246
|
-
|
|
357
|
+
<span class="legend-color" style="background: #8b949e; border-radius: 2px;"></span> Docstring ▢
|
|
247
358
|
</div>
|
|
248
359
|
<div class="legend-item">
|
|
249
|
-
|
|
360
|
+
<span class="legend-color" style="background: #6e7681; border-radius: 2px;"></span> Comment ▢
|
|
250
361
|
</div>
|
|
251
362
|
</div>
|
|
252
363
|
|
|
@@ -261,13 +372,13 @@
|
|
|
261
372
|
<svg id="graph"></svg>
|
|
262
373
|
<div id="tooltip" class="tooltip"></div>
|
|
263
374
|
|
|
264
|
-
<div id="
|
|
265
|
-
<div class="header">
|
|
266
|
-
<
|
|
267
|
-
<div class="title" id="
|
|
268
|
-
<div class="meta" id="
|
|
375
|
+
<div id="content-pane">
|
|
376
|
+
<div class="pane-header">
|
|
377
|
+
<button class="collapse-btn" onclick="closeContentPane()">×</button>
|
|
378
|
+
<div class="pane-title" id="pane-title"></div>
|
|
379
|
+
<div class="pane-meta" id="pane-meta"></div>
|
|
269
380
|
</div>
|
|
270
|
-
<
|
|
381
|
+
<div class="pane-content" id="pane-content"></div>
|
|
271
382
|
</div>
|
|
272
383
|
|
|
273
384
|
<script>
|
|
@@ -286,7 +397,6 @@
|
|
|
286
397
|
let simulation;
|
|
287
398
|
let allNodes = [];
|
|
288
399
|
let allLinks = [];
|
|
289
|
-
let allLinksOriginal = []; // Keep original link structure before D3 modifies it
|
|
290
400
|
let visibleNodes = new Set();
|
|
291
401
|
let collapsedNodes = new Set();
|
|
292
402
|
let highlightedNode = null;
|
|
@@ -296,8 +406,6 @@
|
|
|
296
406
|
|
|
297
407
|
allNodes = data.nodes;
|
|
298
408
|
allLinks = data.links;
|
|
299
|
-
// Deep copy links before D3 modifies them
|
|
300
|
-
allLinksOriginal = JSON.parse(JSON.stringify(data.links));
|
|
301
409
|
|
|
302
410
|
// Find root nodes - start with only top-level nodes
|
|
303
411
|
let rootNodes;
|
|
@@ -305,21 +413,36 @@
|
|
|
305
413
|
// In monorepos, subproject nodes are roots
|
|
306
414
|
rootNodes = allNodes.filter(n => n.type === 'subproject');
|
|
307
415
|
} else {
|
|
308
|
-
// Regular projects:
|
|
416
|
+
// Regular projects: Find root nodes by graph structure
|
|
309
417
|
const dirNodes = allNodes.filter(n => n.type === 'directory');
|
|
310
418
|
const fileNodes = allNodes.filter(n => n.type === 'file');
|
|
311
419
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
420
|
+
// Find minimum depth for directories and files (for fallback)
|
|
421
|
+
let minDirDepth = Infinity;
|
|
422
|
+
let minFileDepth = Infinity;
|
|
423
|
+
|
|
424
|
+
dirNodes.forEach(d => {
|
|
425
|
+
if (d.depth < minDirDepth) minDirDepth = d.depth;
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
fileNodes.forEach(f => {
|
|
429
|
+
if (f.depth < minFileDepth) minFileDepth = f.depth;
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// CORRECT: Find root nodes by checking graph structure
|
|
433
|
+
// Root nodes are nodes with no incoming links (no parent)
|
|
434
|
+
const targetIds = new Set(allLinks.map(link => link.target));
|
|
435
|
+
rootNodes = allNodes.filter(node => !targetIds.has(node.id));
|
|
436
|
+
|
|
437
|
+
console.log(`Root nodes found via links: ${rootNodes.length}`, rootNodes.map(n => n.name));
|
|
438
|
+
|
|
439
|
+
// Fallback only if we got 0 root nodes (shouldn't happen with correct data)
|
|
440
|
+
if (rootNodes.length === 0) {
|
|
441
|
+
console.warn('No root nodes found via links, falling back to depth-based detection');
|
|
442
|
+
rootNodes = [
|
|
443
|
+
...dirNodes.filter(n => n.depth === minDirDepth),
|
|
444
|
+
...fileNodes.filter(n => n.depth === minFileDepth)
|
|
445
|
+
];
|
|
323
446
|
}
|
|
324
447
|
}
|
|
325
448
|
|
|
@@ -327,58 +450,35 @@
|
|
|
327
450
|
visibleNodes = new Set(rootNodes.map(n => n.id));
|
|
328
451
|
collapsedNodes = new Set(rootNodes.map(n => n.id));
|
|
329
452
|
|
|
453
|
+
console.log('=== INITIAL STATE ===');
|
|
454
|
+
console.log(`Total nodes: ${allNodes.length}`);
|
|
455
|
+
console.log(`Total links: ${allLinks.length}`);
|
|
456
|
+
console.log(`Root nodes: ${rootNodes.length}`, rootNodes.map(n => n.name));
|
|
457
|
+
console.log(`Initial visibleNodes:`, Array.from(visibleNodes));
|
|
458
|
+
console.log(`Initial collapsedNodes:`, Array.from(collapsedNodes));
|
|
459
|
+
|
|
330
460
|
renderGraph();
|
|
331
461
|
}
|
|
332
462
|
|
|
333
463
|
function renderGraph() {
|
|
464
|
+
console.log('=== renderGraph() called ===');
|
|
465
|
+
console.log(`visibleNodes.size: ${visibleNodes.size}`);
|
|
466
|
+
console.log(`collapsedNodes.size: ${collapsedNodes.size}`);
|
|
467
|
+
|
|
334
468
|
const visibleNodesList = allNodes.filter(n => visibleNodes.has(n.id));
|
|
469
|
+
console.log(`Rendering ${visibleNodesList.length} visible nodes`);
|
|
470
|
+
|
|
335
471
|
const visibleLinks = allLinks.filter(l =>
|
|
336
472
|
visibleNodes.has(l.source.id || l.source) &&
|
|
337
473
|
visibleNodes.has(l.target.id || l.target)
|
|
338
474
|
);
|
|
339
|
-
|
|
340
|
-
// Dynamic collision radius based on node type
|
|
341
|
-
const getNodeRadius = (d) => {
|
|
342
|
-
if (d.type === 'directory') return 35;
|
|
343
|
-
if (d.type === 'file') return 32;
|
|
344
|
-
if (d.type === 'subproject') return 38;
|
|
345
|
-
return d.complexity ? Math.min(20 + d.complexity * 2, 35) : 25;
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
// Dynamic link distance based on node types
|
|
349
|
-
const getLinkDistance = (link) => {
|
|
350
|
-
const source = typeof link.source === 'object' ? link.source : visibleNodesList.find(n => n.id === link.source);
|
|
351
|
-
const target = typeof link.target === 'object' ? link.target : visibleNodesList.find(n => n.id === link.target);
|
|
352
|
-
|
|
353
|
-
if (!source || !target) return 150;
|
|
354
|
-
|
|
355
|
-
// Longer distances for directory relationships
|
|
356
|
-
if (source.type === 'directory' || target.type === 'directory') return 200;
|
|
357
|
-
if (source.type === 'file' || target.type === 'file') return 150;
|
|
358
|
-
return 100;
|
|
359
|
-
};
|
|
475
|
+
console.log(`Rendering ${visibleLinks.length} visible links`);
|
|
360
476
|
|
|
361
477
|
simulation = d3.forceSimulation(visibleNodesList)
|
|
362
|
-
.force("link", d3.forceLink(visibleLinks)
|
|
363
|
-
|
|
364
|
-
.distance(getLinkDistance)
|
|
365
|
-
.strength(0.5))
|
|
366
|
-
.force("charge", d3.forceManyBody()
|
|
367
|
-
.strength(d => {
|
|
368
|
-
// Stronger repulsion for directories and files
|
|
369
|
-
if (d.type === 'directory') return -800;
|
|
370
|
-
if (d.type === 'file') return -600;
|
|
371
|
-
return -400;
|
|
372
|
-
})
|
|
373
|
-
.distanceMax(500))
|
|
478
|
+
.force("link", d3.forceLink(visibleLinks).id(d => d.id).distance(100))
|
|
479
|
+
.force("charge", d3.forceManyBody().strength(-400))
|
|
374
480
|
.force("center", d3.forceCenter(width / 2, height / 2))
|
|
375
|
-
.force("collision", d3.forceCollide()
|
|
376
|
-
.radius(getNodeRadius)
|
|
377
|
-
.strength(0.9))
|
|
378
|
-
.force("x", d3.forceX(width / 2).strength(0.05))
|
|
379
|
-
.force("y", d3.forceY(height / 2).strength(0.05))
|
|
380
|
-
.alphaDecay(0.02)
|
|
381
|
-
.velocityDecay(0.3);
|
|
481
|
+
.force("collision", d3.forceCollide().radius(40));
|
|
382
482
|
|
|
383
483
|
g.selectAll("*").remove();
|
|
384
484
|
|
|
@@ -404,16 +504,59 @@
|
|
|
404
504
|
.on("mouseover", showTooltip)
|
|
405
505
|
.on("mouseout", hideTooltip);
|
|
406
506
|
|
|
407
|
-
// Add circles
|
|
408
|
-
|
|
507
|
+
// Add shapes based on node type (circles for code, squares for docs)
|
|
508
|
+
const isDocNode = d => ['docstring', 'comment'].includes(d.type);
|
|
509
|
+
|
|
510
|
+
// Add circles for code nodes
|
|
511
|
+
node.filter(d => !isDocNode(d))
|
|
512
|
+
.append("circle")
|
|
409
513
|
.attr("r", d => {
|
|
410
|
-
if (d.type === '
|
|
411
|
-
if (d.type === '
|
|
412
|
-
if (d.type === '
|
|
413
|
-
return d.complexity ? Math.min(
|
|
514
|
+
if (d.type === 'subproject') return 20;
|
|
515
|
+
if (d.type === 'directory') return 40; // Largest for directory containers
|
|
516
|
+
if (d.type === 'file') return 30; // Larger transparent circle for files
|
|
517
|
+
return d.complexity ? Math.min(8 + d.complexity * 2, 25) : 12;
|
|
518
|
+
})
|
|
519
|
+
.attr("stroke", d => hasChildren(d) ? "#ffffff" : "none")
|
|
520
|
+
.attr("stroke-width", d => hasChildren(d) ? 3 : 0)
|
|
521
|
+
.style("fill", d => d.color || null); // Use custom color if available
|
|
522
|
+
|
|
523
|
+
// Add rectangles for document nodes
|
|
524
|
+
node.filter(d => isDocNode(d))
|
|
525
|
+
.append("rect")
|
|
526
|
+
.attr("width", d => {
|
|
527
|
+
const size = d.complexity ? Math.min(8 + d.complexity * 2, 25) : 12;
|
|
528
|
+
return size * 2;
|
|
529
|
+
})
|
|
530
|
+
.attr("height", d => {
|
|
531
|
+
const size = d.complexity ? Math.min(8 + d.complexity * 2, 25) : 12;
|
|
532
|
+
return size * 2;
|
|
414
533
|
})
|
|
534
|
+
.attr("x", d => {
|
|
535
|
+
const size = d.complexity ? Math.min(8 + d.complexity * 2, 25) : 12;
|
|
536
|
+
return -size;
|
|
537
|
+
})
|
|
538
|
+
.attr("y", d => {
|
|
539
|
+
const size = d.complexity ? Math.min(8 + d.complexity * 2, 25) : 12;
|
|
540
|
+
return -size;
|
|
541
|
+
})
|
|
542
|
+
.attr("rx", 2) // Rounded corners
|
|
543
|
+
.attr("ry", 2)
|
|
544
|
+
.attr("stroke", d => hasChildren(d) ? "#ffffff" : "none")
|
|
545
|
+
.attr("stroke-width", d => hasChildren(d) ? 3 : 0)
|
|
415
546
|
.style("fill", d => d.color || null);
|
|
416
547
|
|
|
548
|
+
// Add expand/collapse indicator
|
|
549
|
+
node.filter(d => hasChildren(d))
|
|
550
|
+
.append("text")
|
|
551
|
+
.attr("class", "expand-indicator")
|
|
552
|
+
.attr("text-anchor", "middle")
|
|
553
|
+
.attr("dy", 5)
|
|
554
|
+
.style("font-size", "16px")
|
|
555
|
+
.style("font-weight", "bold")
|
|
556
|
+
.style("fill", "#ffffff")
|
|
557
|
+
.style("pointer-events", "none")
|
|
558
|
+
.text(d => collapsedNodes.has(d.id) ? "+" : "−");
|
|
559
|
+
|
|
417
560
|
// Add icons based on node type
|
|
418
561
|
node.append("text")
|
|
419
562
|
.attr("class", "node-icon")
|
|
@@ -426,24 +569,27 @@
|
|
|
426
569
|
if (d.type === 'method') return 'm';
|
|
427
570
|
if (d.type === 'module') return 'M';
|
|
428
571
|
if (d.type === 'imports') return '⇄';
|
|
572
|
+
if (d.type === 'docstring') return '📝';
|
|
573
|
+
if (d.type === 'comment') return '💬';
|
|
574
|
+
if (d.type === 'subproject') return '📦';
|
|
429
575
|
return '•';
|
|
430
576
|
});
|
|
431
577
|
|
|
432
|
-
// Add
|
|
433
|
-
node.filter(d => hasChildren(d))
|
|
434
|
-
.append("text")
|
|
435
|
-
.attr("class", "expand-indicator")
|
|
436
|
-
.attr("text-anchor", "middle")
|
|
437
|
-
.attr("dy", -20)
|
|
438
|
-
.style("font-size", "14px")
|
|
439
|
-
.style("font-weight", "bold")
|
|
440
|
-
.style("fill", "#58a6ff")
|
|
441
|
-
.style("pointer-events", "none")
|
|
442
|
-
.text(d => collapsedNodes.has(d.id) ? "+" : "−");
|
|
443
|
-
|
|
444
|
-
// Add labels
|
|
578
|
+
// Add labels (show actual import statement for import nodes)
|
|
445
579
|
node.append("text")
|
|
446
|
-
.text(d =>
|
|
580
|
+
.text(d => {
|
|
581
|
+
// Import nodes have type === 'imports'
|
|
582
|
+
if (d.type === 'imports') {
|
|
583
|
+
if (d.content) {
|
|
584
|
+
// Extract first line of import statement
|
|
585
|
+
const importLine = d.content.split('\n')[0].trim();
|
|
586
|
+
// Truncate if too long (max 60 chars)
|
|
587
|
+
return importLine.length > 60 ? importLine.substring(0, 57) + '...' : importLine;
|
|
588
|
+
}
|
|
589
|
+
return d.name; // Fallback to name if no content
|
|
590
|
+
}
|
|
591
|
+
return d.name;
|
|
592
|
+
})
|
|
447
593
|
.attr("dy", 30);
|
|
448
594
|
|
|
449
595
|
simulation.on("tick", () => {
|
|
@@ -460,50 +606,87 @@
|
|
|
460
606
|
}
|
|
461
607
|
|
|
462
608
|
function hasChildren(node) {
|
|
463
|
-
//
|
|
464
|
-
|
|
609
|
+
// Handle both pre-mutation (string IDs) and post-mutation (object references)
|
|
610
|
+
const result = allLinks.some(l => {
|
|
611
|
+
const sourceId = typeof l.source === 'object' ? l.source.id : l.source;
|
|
612
|
+
return sourceId === node.id;
|
|
613
|
+
});
|
|
614
|
+
console.log(`hasChildren(${node.name}):`, result,
|
|
615
|
+
`Checking ${allLinks.length} links for node ID: ${node.id}`);
|
|
616
|
+
return result;
|
|
465
617
|
}
|
|
466
618
|
|
|
467
619
|
function handleNodeClick(event, d) {
|
|
620
|
+
console.log('=== NODE CLICKED ===');
|
|
621
|
+
console.log('Node:', d.name, 'Type:', d.type, 'ID:', d.id);
|
|
622
|
+
console.log('Event:', event);
|
|
623
|
+
|
|
468
624
|
event.stopPropagation();
|
|
469
625
|
|
|
470
|
-
//
|
|
471
|
-
|
|
472
|
-
|
|
626
|
+
// Always show content pane when clicking any node
|
|
627
|
+
showContentPane(d);
|
|
628
|
+
|
|
629
|
+
// If node has children, also toggle expansion
|
|
630
|
+
const nodeHasChildren = hasChildren(d);
|
|
631
|
+
console.log('Node has children:', nodeHasChildren);
|
|
632
|
+
|
|
633
|
+
if (nodeHasChildren) {
|
|
634
|
+
const isCollapsed = collapsedNodes.has(d.id);
|
|
635
|
+
console.log('Node is collapsed:', isCollapsed);
|
|
636
|
+
|
|
637
|
+
if (isCollapsed) {
|
|
638
|
+
console.log('Expanding node...');
|
|
473
639
|
expandNode(d);
|
|
474
640
|
} else {
|
|
641
|
+
console.log('Collapsing node...');
|
|
475
642
|
collapseNode(d);
|
|
476
643
|
}
|
|
644
|
+
console.log('Calling renderGraph()...');
|
|
477
645
|
renderGraph();
|
|
646
|
+
console.log('renderGraph() complete');
|
|
478
647
|
} else {
|
|
479
|
-
|
|
480
|
-
showCodeViewer(d);
|
|
648
|
+
console.log('Node has no children, skipping expansion');
|
|
481
649
|
}
|
|
482
650
|
}
|
|
483
651
|
|
|
484
652
|
function expandNode(node) {
|
|
653
|
+
console.log(`expandNode(${node.name}):`, 'Removing from collapsedNodes');
|
|
485
654
|
collapsedNodes.delete(node.id);
|
|
486
655
|
|
|
487
|
-
// Find direct children
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
656
|
+
// Find direct children
|
|
657
|
+
const childLinks = allLinks.filter(l => (l.source.id || l.source) === node.id);
|
|
658
|
+
console.log(` Found ${childLinks.length} child links for node ${node.id}`);
|
|
659
|
+
|
|
660
|
+
const children = childLinks
|
|
661
|
+
.map(l => {
|
|
662
|
+
const targetId = l.target.id || l.target;
|
|
663
|
+
const child = allNodes.find(n => n.id === targetId);
|
|
664
|
+
if (!child) {
|
|
665
|
+
console.warn(` Could not find child node with ID: ${targetId}`);
|
|
666
|
+
}
|
|
667
|
+
return child;
|
|
668
|
+
})
|
|
491
669
|
.filter(n => n);
|
|
492
670
|
|
|
671
|
+
console.log(` Found ${children.length} child nodes:`, children.map(c => c.name));
|
|
672
|
+
|
|
493
673
|
children.forEach(child => {
|
|
674
|
+
console.log(` Adding child to visibleNodes: ${child.name} (${child.id})`);
|
|
494
675
|
visibleNodes.add(child.id);
|
|
495
676
|
collapsedNodes.add(child.id); // Children start collapsed
|
|
496
677
|
});
|
|
678
|
+
|
|
679
|
+
console.log(` visibleNodes now has ${visibleNodes.size} items`);
|
|
497
680
|
}
|
|
498
681
|
|
|
499
682
|
function collapseNode(node) {
|
|
500
683
|
collapsedNodes.add(node.id);
|
|
501
684
|
|
|
502
|
-
// Hide all descendants recursively
|
|
685
|
+
// Hide all descendants recursively
|
|
503
686
|
function hideDescendants(parentId) {
|
|
504
|
-
const children =
|
|
505
|
-
.filter(l => l.source === parentId)
|
|
506
|
-
.map(l => l.target);
|
|
687
|
+
const children = allLinks
|
|
688
|
+
.filter(l => (l.source.id || l.source) === parentId)
|
|
689
|
+
.map(l => l.target.id || l.target);
|
|
507
690
|
|
|
508
691
|
children.forEach(childId => {
|
|
509
692
|
visibleNodes.delete(childId);
|
|
@@ -586,19 +769,30 @@
|
|
|
586
769
|
}
|
|
587
770
|
}
|
|
588
771
|
|
|
589
|
-
function
|
|
772
|
+
function showContentPane(node) {
|
|
590
773
|
// Highlight the node
|
|
591
774
|
highlightedNode = node;
|
|
592
775
|
renderGraph();
|
|
593
776
|
|
|
594
|
-
// Populate
|
|
595
|
-
const
|
|
596
|
-
const title = document.getElementById('
|
|
597
|
-
const meta = document.getElementById('
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
title
|
|
777
|
+
// Populate content pane
|
|
778
|
+
const pane = document.getElementById('content-pane');
|
|
779
|
+
const title = document.getElementById('pane-title');
|
|
780
|
+
const meta = document.getElementById('pane-meta');
|
|
781
|
+
const content = document.getElementById('pane-content');
|
|
782
|
+
|
|
783
|
+
// Set title with actual import statement for import nodes
|
|
784
|
+
if (node.type === 'imports') {
|
|
785
|
+
if (node.content) {
|
|
786
|
+
const importLine = node.content.split('\n')[0].trim();
|
|
787
|
+
title.textContent = importLine;
|
|
788
|
+
} else {
|
|
789
|
+
title.textContent = `Import: ${node.name}`;
|
|
790
|
+
}
|
|
791
|
+
} else {
|
|
792
|
+
title.textContent = node.name;
|
|
793
|
+
}
|
|
601
794
|
|
|
795
|
+
// Set metadata
|
|
602
796
|
let metaText = `${node.type} • ${node.file_path}`;
|
|
603
797
|
if (node.start_line) {
|
|
604
798
|
metaText += ` • Lines ${node.start_line}-${node.end_line}`;
|
|
@@ -608,22 +802,178 @@
|
|
|
608
802
|
}
|
|
609
803
|
meta.textContent = metaText;
|
|
610
804
|
|
|
611
|
-
//
|
|
805
|
+
// Display content based on node type
|
|
806
|
+
if (node.type === 'directory') {
|
|
807
|
+
showDirectoryContents(node, content);
|
|
808
|
+
} else if (node.type === 'file') {
|
|
809
|
+
showFileContents(node, content);
|
|
810
|
+
} else if (node.type === 'imports') {
|
|
811
|
+
// Import nodes show import details
|
|
812
|
+
showImportDetails(node, content);
|
|
813
|
+
} else {
|
|
814
|
+
// Class, function, method, code nodes
|
|
815
|
+
showCodeContent(node, content);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
pane.classList.add('visible');
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function showDirectoryContents(node, container) {
|
|
822
|
+
// Find all direct children of this directory
|
|
823
|
+
const children = allLinks
|
|
824
|
+
.filter(l => (l.source.id || l.source) === node.id)
|
|
825
|
+
.map(l => allNodes.find(n => n.id === (l.target.id || l.target)))
|
|
826
|
+
.filter(n => n);
|
|
827
|
+
|
|
828
|
+
if (children.length === 0) {
|
|
829
|
+
container.innerHTML = '<p style="color: #8b949e;">Empty directory</p>';
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// Group by type
|
|
834
|
+
const files = children.filter(n => n.type === 'file');
|
|
835
|
+
const subdirs = children.filter(n => n.type === 'directory');
|
|
836
|
+
const chunks = children.filter(n => n.type !== 'file' && n.type !== 'directory');
|
|
837
|
+
|
|
838
|
+
let html = '<ul class="directory-list">';
|
|
839
|
+
|
|
840
|
+
// Show subdirectories first
|
|
841
|
+
subdirs.forEach(child => {
|
|
842
|
+
html += `
|
|
843
|
+
<li>
|
|
844
|
+
<span class="item-icon">📁</span>
|
|
845
|
+
${child.name}
|
|
846
|
+
<span class="item-type">directory</span>
|
|
847
|
+
</li>
|
|
848
|
+
`;
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
// Then files
|
|
852
|
+
files.forEach(child => {
|
|
853
|
+
html += `
|
|
854
|
+
<li>
|
|
855
|
+
<span class="item-icon">📄</span>
|
|
856
|
+
${child.name}
|
|
857
|
+
<span class="item-type">file</span>
|
|
858
|
+
</li>
|
|
859
|
+
`;
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
// Then code chunks
|
|
863
|
+
chunks.forEach(child => {
|
|
864
|
+
const icon = child.type === 'class' ? '🔷' : child.type === 'function' ? '⚡' : '📝';
|
|
865
|
+
html += `
|
|
866
|
+
<li>
|
|
867
|
+
<span class="item-icon">${icon}</span>
|
|
868
|
+
${child.name}
|
|
869
|
+
<span class="item-type">${child.type}</span>
|
|
870
|
+
</li>
|
|
871
|
+
`;
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
html += '</ul>';
|
|
875
|
+
|
|
876
|
+
// Add summary
|
|
877
|
+
const summary = `<p style="color: #8b949e; font-size: 11px; margin-top: 16px;">
|
|
878
|
+
Total: ${children.length} items (${subdirs.length} directories, ${files.length} files, ${chunks.length} code chunks)
|
|
879
|
+
</p>`;
|
|
880
|
+
|
|
881
|
+
container.innerHTML = html + summary;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
function showFileContents(node, container) {
|
|
885
|
+
// Find all chunks in this file
|
|
886
|
+
const fileChunks = allLinks
|
|
887
|
+
.filter(l => (l.source.id || l.source) === node.id)
|
|
888
|
+
.map(l => allNodes.find(n => n.id === (l.target.id || l.target)))
|
|
889
|
+
.filter(n => n);
|
|
890
|
+
|
|
891
|
+
if (fileChunks.length === 0) {
|
|
892
|
+
container.innerHTML = '<p style="color: #8b949e;">No code chunks found in this file</p>';
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Collect all content from chunks and sort by line number
|
|
897
|
+
const sortedChunks = fileChunks
|
|
898
|
+
.filter(c => c.content)
|
|
899
|
+
.sort((a, b) => a.start_line - b.start_line);
|
|
900
|
+
|
|
901
|
+
if (sortedChunks.length === 0) {
|
|
902
|
+
container.innerHTML = '<p style="color: #8b949e;">File content not available</p>';
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// Combine all chunks to show full file
|
|
907
|
+
const fullContent = sortedChunks.map(c => c.content).join('\n\n');
|
|
908
|
+
|
|
909
|
+
container.innerHTML = `
|
|
910
|
+
<p style="color: #8b949e; font-size: 11px; margin-bottom: 12px;">
|
|
911
|
+
Contains ${fileChunks.length} code chunks
|
|
912
|
+
</p>
|
|
913
|
+
<pre><code>${escapeHtml(fullContent)}</code></pre>
|
|
914
|
+
`;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
function showImportDetails(node, container) {
|
|
918
|
+
// Import nodes (type === 'imports') - show import content prominently
|
|
919
|
+
const importHtml = `
|
|
920
|
+
<div class="import-details">
|
|
921
|
+
${node.content ? `
|
|
922
|
+
<div style="margin-bottom: 16px;">
|
|
923
|
+
<div class="detail-label" style="margin-bottom: 8px;">Import Statement:</div>
|
|
924
|
+
<pre><code>${escapeHtml(node.content)}</code></pre>
|
|
925
|
+
</div>
|
|
926
|
+
` : '<p style="color: #8b949e;">No import content available</p>'}
|
|
927
|
+
<div class="detail-row">
|
|
928
|
+
<span class="detail-label">File:</span> ${node.file_path}
|
|
929
|
+
</div>
|
|
930
|
+
${node.start_line ? `
|
|
931
|
+
<div class="detail-row">
|
|
932
|
+
<span class="detail-label">Location:</span> Lines ${node.start_line}-${node.end_line}
|
|
933
|
+
</div>
|
|
934
|
+
` : ''}
|
|
935
|
+
${node.language ? `
|
|
936
|
+
<div class="detail-row">
|
|
937
|
+
<span class="detail-label">Language:</span> ${node.language}
|
|
938
|
+
</div>
|
|
939
|
+
` : ''}
|
|
940
|
+
</div>
|
|
941
|
+
`;
|
|
942
|
+
|
|
943
|
+
container.innerHTML = importHtml;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
function showCodeContent(node, container) {
|
|
947
|
+
// Show code for function, class, method, or code chunks
|
|
948
|
+
let html = '';
|
|
949
|
+
|
|
950
|
+
if (node.docstring) {
|
|
951
|
+
html += `
|
|
952
|
+
<div style="margin-bottom: 16px; padding: 12px; background: #161b22; border: 1px solid #30363d; border-radius: 6px;">
|
|
953
|
+
<div style="font-size: 11px; color: #8b949e; margin-bottom: 8px; font-weight: 600;">DOCSTRING</div>
|
|
954
|
+
<pre style="margin: 0; padding: 0; background: transparent; border: none;"><code>${escapeHtml(node.docstring)}</code></pre>
|
|
955
|
+
</div>
|
|
956
|
+
`;
|
|
957
|
+
}
|
|
958
|
+
|
|
612
959
|
if (node.content) {
|
|
613
|
-
|
|
614
|
-
} else if (node.docstring) {
|
|
615
|
-
code.textContent = `// Docstring:
|
|
616
|
-
${node.docstring}`;
|
|
960
|
+
html += `<pre><code>${escapeHtml(node.content)}</code></pre>`;
|
|
617
961
|
} else {
|
|
618
|
-
|
|
962
|
+
html += '<p style="color: #8b949e;">No content available</p>';
|
|
619
963
|
}
|
|
620
964
|
|
|
621
|
-
|
|
965
|
+
container.innerHTML = html;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function escapeHtml(text) {
|
|
969
|
+
const div = document.createElement('div');
|
|
970
|
+
div.textContent = text;
|
|
971
|
+
return div.innerHTML;
|
|
622
972
|
}
|
|
623
973
|
|
|
624
|
-
function
|
|
625
|
-
const
|
|
626
|
-
|
|
974
|
+
function closeContentPane() {
|
|
975
|
+
const pane = document.getElementById('content-pane');
|
|
976
|
+
pane.classList.remove('visible');
|
|
627
977
|
|
|
628
978
|
// Remove highlight
|
|
629
979
|
highlightedNode = null;
|
|
@@ -634,7 +984,7 @@ ${node.docstring}`;
|
|
|
634
984
|
window.addEventListener('DOMContentLoaded', () => {
|
|
635
985
|
const loadingEl = document.getElementById('loading');
|
|
636
986
|
|
|
637
|
-
fetch(
|
|
987
|
+
fetch("chunk-graph.json")
|
|
638
988
|
.then(response => {
|
|
639
989
|
if (!response.ok) {
|
|
640
990
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-vector-search
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.2
|
|
4
4
|
Summary: CLI-first semantic code search with MCP integration
|
|
5
5
|
Project-URL: Homepage, https://github.com/bobmatnyc/mcp-vector-search
|
|
6
6
|
Project-URL: Documentation, https://mcp-vector-search.readthedocs.io
|
|
@@ -490,6 +490,10 @@ uv sync
|
|
|
490
490
|
# Install in development mode
|
|
491
491
|
uv pip install -e .
|
|
492
492
|
|
|
493
|
+
# Test CLI from source (recommended during development)
|
|
494
|
+
./dev-mcp version # Shows [DEV] indicator
|
|
495
|
+
./dev-mcp search "test" # No reinstall needed after code changes
|
|
496
|
+
|
|
493
497
|
# Run tests
|
|
494
498
|
uv run pytest
|
|
495
499
|
|
|
@@ -498,6 +502,8 @@ uv run ruff check
|
|
|
498
502
|
uv run mypy src/
|
|
499
503
|
```
|
|
500
504
|
|
|
505
|
+
For detailed development workflow and `dev-mcp` usage, see the [Development](#-development) section below.
|
|
506
|
+
|
|
501
507
|
### Adding Language Support
|
|
502
508
|
|
|
503
509
|
1. Create a new parser in `src/mcp_vector_search/parsers/`
|
|
@@ -570,10 +576,64 @@ uv sync && uv pip install -e .
|
|
|
570
576
|
# Run development tests
|
|
571
577
|
./scripts/dev-test.sh
|
|
572
578
|
|
|
573
|
-
#
|
|
579
|
+
# Run CLI from source (recommended during development)
|
|
580
|
+
./dev-mcp version # Visual [DEV] indicator
|
|
581
|
+
./dev-mcp status # Any command works
|
|
582
|
+
./dev-mcp search "auth" # Immediate feedback on changes
|
|
583
|
+
|
|
584
|
+
# Alternative: use uv run directly
|
|
574
585
|
uv run mcp-vector-search version
|
|
575
586
|
```
|
|
576
587
|
|
|
588
|
+
#### Using the `dev-mcp` Development Helper
|
|
589
|
+
|
|
590
|
+
The `./dev-mcp` script provides a streamlined way to run the CLI from source code during development, eliminating the need for repeated installations.
|
|
591
|
+
|
|
592
|
+
**Key Features:**
|
|
593
|
+
- **Visual [DEV] Indicator**: Shows `[DEV]` prefix to distinguish from installed version
|
|
594
|
+
- **No Reinstall Required**: Reflects code changes immediately
|
|
595
|
+
- **Complete Argument Forwarding**: Works with all CLI commands and options
|
|
596
|
+
- **Verbose Mode**: Debug output with `--verbose` flag
|
|
597
|
+
- **Built-in Help**: Script usage with `--help`
|
|
598
|
+
|
|
599
|
+
**Usage Examples:**
|
|
600
|
+
```bash
|
|
601
|
+
# Basic commands (note the [DEV] prefix in output)
|
|
602
|
+
./dev-mcp version
|
|
603
|
+
./dev-mcp status
|
|
604
|
+
./dev-mcp index
|
|
605
|
+
./dev-mcp search "authentication logic"
|
|
606
|
+
|
|
607
|
+
# With CLI options
|
|
608
|
+
./dev-mcp search "error handling" --limit 10
|
|
609
|
+
./dev-mcp index --force
|
|
610
|
+
|
|
611
|
+
# Script verbose mode (shows Python interpreter, paths)
|
|
612
|
+
./dev-mcp --verbose search "database"
|
|
613
|
+
|
|
614
|
+
# Script help (shows dev-mcp usage, not CLI help)
|
|
615
|
+
./dev-mcp --help
|
|
616
|
+
|
|
617
|
+
# CLI command help (forwards --help to the CLI)
|
|
618
|
+
./dev-mcp search --help
|
|
619
|
+
./dev-mcp index --help
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
**When to Use:**
|
|
623
|
+
- **`./dev-mcp`** → Development workflow (runs from source code)
|
|
624
|
+
- **`mcp-vector-search`** → Production usage (runs installed version via pipx/pip)
|
|
625
|
+
|
|
626
|
+
**Benefits:**
|
|
627
|
+
- **Instant Feedback**: Changes to source code are reflected immediately
|
|
628
|
+
- **No Build Step**: Skip the reinstall cycle during active development
|
|
629
|
+
- **Clear Context**: Visual `[DEV]` indicator prevents confusion about which version is running
|
|
630
|
+
- **Error Handling**: Built-in checks for uv installation and project structure
|
|
631
|
+
|
|
632
|
+
**Requirements:**
|
|
633
|
+
- Must have `uv` installed (`pip install uv`)
|
|
634
|
+
- Must run from project root directory
|
|
635
|
+
- Requires `pyproject.toml` in current directory
|
|
636
|
+
|
|
577
637
|
**Stage B: Local Deployment Testing**
|
|
578
638
|
```bash
|
|
579
639
|
# Build and test clean deployment
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
mcp_vector_search/__init__.py,sha256=
|
|
1
|
+
mcp_vector_search/__init__.py,sha256=VI7aQii3CQwhLiBXYs4cQi58X3uMRZ9oKNT2G7olYd4,300
|
|
2
2
|
mcp_vector_search/py.typed,sha256=lCKeV9Qcn9sGtbRsgg-LJO2ZwWRuknnnlmomq3bJFH0,43
|
|
3
3
|
mcp_vector_search/cli/__init__.py,sha256=TNB7CaOASz8u3yHWLbNmo8-GtHF0qwUjVKWAuNphKgo,40
|
|
4
4
|
mcp_vector_search/cli/didyoumean.py,sha256=8yF15w74xq9uUT46ww-XNidBs7cSPVYV7mKiMS4bRqY,16318
|
|
@@ -21,7 +21,7 @@ mcp_vector_search/cli/commands/reset.py,sha256=bsIT6zjDf6gsvIkVaRaUClYzlTyNe--8t
|
|
|
21
21
|
mcp_vector_search/cli/commands/search.py,sha256=yyou7wO9qZ_w2oiKdyOrk2WUxvkFpc-Up8hpflxYlyw,24802
|
|
22
22
|
mcp_vector_search/cli/commands/status.py,sha256=-Ke58000-bi74T0J-dy1zaD3C2TuZBSdERm9mIm3kXI,19814
|
|
23
23
|
mcp_vector_search/cli/commands/uninstall.py,sha256=XjFoSJlQkM1GPg7zaK65jOvb-hb87F2Ciyldbn8y2jc,14953
|
|
24
|
-
mcp_vector_search/cli/commands/visualize.py,sha256=
|
|
24
|
+
mcp_vector_search/cli/commands/visualize.py,sha256=VwzrMCOTfdjzDOrP3ry3SwW68YKYNRFKV4vzlbxfqRM,52331
|
|
25
25
|
mcp_vector_search/cli/commands/watch.py,sha256=2pyWRoo4fIppFnyQ4sW4IBLHmpb_IwnTjRnzHkVBPcQ,8927
|
|
26
26
|
mcp_vector_search/config/__init__.py,sha256=r_qAQkU5gc0EQ2pv8EQARACe4klhrR_WRJqCb9lfGc0,54
|
|
27
27
|
mcp_vector_search/config/constants.py,sha256=afXR6SvLLd8QYY4MG4s1vq-hCJiQsE5PhnE-XG9lvb4,1092
|
|
@@ -61,9 +61,33 @@ mcp_vector_search/utils/gitignore.py,sha256=hJHt5YsfEvLIfweaa968tGTavcbxqh3X5nSa
|
|
|
61
61
|
mcp_vector_search/utils/monorepo.py,sha256=leTYx4ffN4IO0wDg7OWYfXMWMPp2Q_uEHl5WQFNk5Hs,8657
|
|
62
62
|
mcp_vector_search/utils/timing.py,sha256=THC7mfbTYnUpnnDcblgQacYMzbEkfFoIShx6plmhCgg,11285
|
|
63
63
|
mcp_vector_search/utils/version.py,sha256=d7fS-CLemxb8UzZ9j18zH0Y0Ud097ljKKYYOPulnGPE,1138
|
|
64
|
-
mcp_vector_search/visualization/
|
|
65
|
-
mcp_vector_search-
|
|
66
|
-
mcp_vector_search-
|
|
67
|
-
mcp_vector_search-
|
|
68
|
-
mcp_vector_search-
|
|
69
|
-
mcp_vector_search-
|
|
64
|
+
mcp_vector_search/visualization/favicon-v1-1024.png,sha256=1pESJk22wsG29ryTC31884wnpP6tG1AAJZJlxUBcZu8,3129
|
|
65
|
+
mcp_vector_search/visualization/favicon-v1-128.png,sha256=29jmI1odupCm1eUZnbQhtunwN9VmQ9Ld7AGxivt65ko,127
|
|
66
|
+
mcp_vector_search/visualization/favicon-v1-16.png,sha256=WEitLws7EWu9zvgmYV0JNklwtRJNZmzhq_nuixD5_TY,74
|
|
67
|
+
mcp_vector_search/visualization/favicon-v1-256.png,sha256=o2hBl9H7pjH2Ovp5HRUF5uQS_BxQXTE2vyZLiiX-cRg,270
|
|
68
|
+
mcp_vector_search/visualization/favicon-v1-32.png,sha256=_t-DLHfL_pBXbYtKJLi02wOeGP_HH11QWdElS3fc9uU,83
|
|
69
|
+
mcp_vector_search/visualization/favicon-v1-512.png,sha256=Fwfv5FcwfQpeL1oewc_1WypEKG88TY3aMr85S-iWO4s,843
|
|
70
|
+
mcp_vector_search/visualization/favicon-v1-64.png,sha256=nD-ibvLuxrrmDjhiIf1hIWMsyOIkozk2W7fakDCmlF0,91
|
|
71
|
+
mcp_vector_search/visualization/favicon-v1.ico,sha256=VYru2inbgzlru_ZLpqOO_rMrGxCGU7_eFcMuLx2Bx58,96
|
|
72
|
+
mcp_vector_search/visualization/favicon-v2-1024.png,sha256=1pESJk22wsG29ryTC31884wnpP6tG1AAJZJlxUBcZu8,3129
|
|
73
|
+
mcp_vector_search/visualization/favicon-v2-128.png,sha256=29jmI1odupCm1eUZnbQhtunwN9VmQ9Ld7AGxivt65ko,127
|
|
74
|
+
mcp_vector_search/visualization/favicon-v2-16.png,sha256=WEitLws7EWu9zvgmYV0JNklwtRJNZmzhq_nuixD5_TY,74
|
|
75
|
+
mcp_vector_search/visualization/favicon-v2-256.png,sha256=o2hBl9H7pjH2Ovp5HRUF5uQS_BxQXTE2vyZLiiX-cRg,270
|
|
76
|
+
mcp_vector_search/visualization/favicon-v2-32.png,sha256=_t-DLHfL_pBXbYtKJLi02wOeGP_HH11QWdElS3fc9uU,83
|
|
77
|
+
mcp_vector_search/visualization/favicon-v2-512.png,sha256=Fwfv5FcwfQpeL1oewc_1WypEKG88TY3aMr85S-iWO4s,843
|
|
78
|
+
mcp_vector_search/visualization/favicon-v2-64.png,sha256=nD-ibvLuxrrmDjhiIf1hIWMsyOIkozk2W7fakDCmlF0,91
|
|
79
|
+
mcp_vector_search/visualization/favicon-v2.ico,sha256=VYru2inbgzlru_ZLpqOO_rMrGxCGU7_eFcMuLx2Bx58,96
|
|
80
|
+
mcp_vector_search/visualization/favicon-v3-1024.png,sha256=1pESJk22wsG29ryTC31884wnpP6tG1AAJZJlxUBcZu8,3129
|
|
81
|
+
mcp_vector_search/visualization/favicon-v3-128.png,sha256=29jmI1odupCm1eUZnbQhtunwN9VmQ9Ld7AGxivt65ko,127
|
|
82
|
+
mcp_vector_search/visualization/favicon-v3-16.png,sha256=WEitLws7EWu9zvgmYV0JNklwtRJNZmzhq_nuixD5_TY,74
|
|
83
|
+
mcp_vector_search/visualization/favicon-v3-256.png,sha256=o2hBl9H7pjH2Ovp5HRUF5uQS_BxQXTE2vyZLiiX-cRg,270
|
|
84
|
+
mcp_vector_search/visualization/favicon-v3-32.png,sha256=_t-DLHfL_pBXbYtKJLi02wOeGP_HH11QWdElS3fc9uU,83
|
|
85
|
+
mcp_vector_search/visualization/favicon-v3-512.png,sha256=Fwfv5FcwfQpeL1oewc_1WypEKG88TY3aMr85S-iWO4s,843
|
|
86
|
+
mcp_vector_search/visualization/favicon-v3-64.png,sha256=nD-ibvLuxrrmDjhiIf1hIWMsyOIkozk2W7fakDCmlF0,91
|
|
87
|
+
mcp_vector_search/visualization/favicon-v3.ico,sha256=VYru2inbgzlru_ZLpqOO_rMrGxCGU7_eFcMuLx2Bx58,96
|
|
88
|
+
mcp_vector_search/visualization/index.html,sha256=10dKa2itpjqxw9aHMs-HWpw5WWTe5EB1NqYRs5fnxxw,36830
|
|
89
|
+
mcp_vector_search-0.12.2.dist-info/METADATA,sha256=zZzHr70_Z_MueRsrzCSCrUJU5WcXl97fs7WrA6zrIhQ,22839
|
|
90
|
+
mcp_vector_search-0.12.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
91
|
+
mcp_vector_search-0.12.2.dist-info/entry_points.txt,sha256=y3Ygtc_JiBchNEIL-tPABo7EbzBExGAxwGdkkeP5D2I,86
|
|
92
|
+
mcp_vector_search-0.12.2.dist-info/licenses/LICENSE,sha256=FqZUgGJH_tZKZLQsMCpXaLawRyLmyFKRVfMwYyEcyTs,1072
|
|
93
|
+
mcp_vector_search-0.12.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|