mcp-vector-search 0.12.6__py3-none-any.whl → 1.0.3__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.
Files changed (65) hide show
  1. mcp_vector_search/__init__.py +2 -2
  2. mcp_vector_search/analysis/__init__.py +64 -0
  3. mcp_vector_search/analysis/collectors/__init__.py +39 -0
  4. mcp_vector_search/analysis/collectors/base.py +164 -0
  5. mcp_vector_search/analysis/collectors/complexity.py +743 -0
  6. mcp_vector_search/analysis/metrics.py +341 -0
  7. mcp_vector_search/analysis/reporters/__init__.py +5 -0
  8. mcp_vector_search/analysis/reporters/console.py +222 -0
  9. mcp_vector_search/cli/commands/analyze.py +408 -0
  10. mcp_vector_search/cli/commands/chat.py +1262 -0
  11. mcp_vector_search/cli/commands/index.py +21 -3
  12. mcp_vector_search/cli/commands/init.py +13 -0
  13. mcp_vector_search/cli/commands/install.py +597 -335
  14. mcp_vector_search/cli/commands/install_old.py +8 -4
  15. mcp_vector_search/cli/commands/mcp.py +78 -6
  16. mcp_vector_search/cli/commands/reset.py +68 -26
  17. mcp_vector_search/cli/commands/search.py +30 -7
  18. mcp_vector_search/cli/commands/setup.py +1133 -0
  19. mcp_vector_search/cli/commands/status.py +37 -2
  20. mcp_vector_search/cli/commands/uninstall.py +276 -357
  21. mcp_vector_search/cli/commands/visualize/__init__.py +39 -0
  22. mcp_vector_search/cli/commands/visualize/cli.py +276 -0
  23. mcp_vector_search/cli/commands/visualize/exporters/__init__.py +12 -0
  24. mcp_vector_search/cli/commands/visualize/exporters/html_exporter.py +33 -0
  25. mcp_vector_search/cli/commands/visualize/exporters/json_exporter.py +29 -0
  26. mcp_vector_search/cli/commands/visualize/graph_builder.py +714 -0
  27. mcp_vector_search/cli/commands/visualize/layout_engine.py +469 -0
  28. mcp_vector_search/cli/commands/visualize/server.py +311 -0
  29. mcp_vector_search/cli/commands/visualize/state_manager.py +428 -0
  30. mcp_vector_search/cli/commands/visualize/templates/__init__.py +16 -0
  31. mcp_vector_search/cli/commands/visualize/templates/base.py +180 -0
  32. mcp_vector_search/cli/commands/visualize/templates/scripts.py +2507 -0
  33. mcp_vector_search/cli/commands/visualize/templates/styles.py +1313 -0
  34. mcp_vector_search/cli/commands/visualize.py.original +2536 -0
  35. mcp_vector_search/cli/didyoumean.py +22 -2
  36. mcp_vector_search/cli/main.py +115 -159
  37. mcp_vector_search/cli/output.py +24 -8
  38. mcp_vector_search/config/__init__.py +4 -0
  39. mcp_vector_search/config/default_thresholds.yaml +52 -0
  40. mcp_vector_search/config/settings.py +12 -0
  41. mcp_vector_search/config/thresholds.py +185 -0
  42. mcp_vector_search/core/auto_indexer.py +3 -3
  43. mcp_vector_search/core/boilerplate.py +186 -0
  44. mcp_vector_search/core/config_utils.py +394 -0
  45. mcp_vector_search/core/database.py +369 -94
  46. mcp_vector_search/core/exceptions.py +11 -0
  47. mcp_vector_search/core/git_hooks.py +4 -4
  48. mcp_vector_search/core/indexer.py +221 -4
  49. mcp_vector_search/core/llm_client.py +751 -0
  50. mcp_vector_search/core/models.py +3 -0
  51. mcp_vector_search/core/project.py +17 -0
  52. mcp_vector_search/core/scheduler.py +11 -11
  53. mcp_vector_search/core/search.py +179 -29
  54. mcp_vector_search/mcp/server.py +24 -5
  55. mcp_vector_search/utils/__init__.py +2 -0
  56. mcp_vector_search/utils/gitignore_updater.py +212 -0
  57. mcp_vector_search/utils/monorepo.py +66 -4
  58. mcp_vector_search/utils/timing.py +10 -6
  59. {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.0.3.dist-info}/METADATA +182 -52
  60. mcp_vector_search-1.0.3.dist-info/RECORD +97 -0
  61. {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.0.3.dist-info}/WHEEL +1 -1
  62. {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.0.3.dist-info}/entry_points.txt +1 -0
  63. mcp_vector_search/cli/commands/visualize.py +0 -1467
  64. mcp_vector_search-0.12.6.dist-info/RECORD +0 -68
  65. {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.0.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1313 @@
1
+ """CSS styles for the visualization interface.
2
+
3
+ This module contains all CSS styling for the D3.js code graph visualization,
4
+ organized into logical sections for maintainability.
5
+ """
6
+
7
+
8
+ def get_base_styles() -> str:
9
+ """Get base styles for body and core layout.
10
+
11
+ Returns:
12
+ CSS string for base styling
13
+ """
14
+ return """
15
+ body {
16
+ margin: 0;
17
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
18
+ background: #0d1117;
19
+ color: #c9d1d9;
20
+ overflow: hidden;
21
+ }
22
+
23
+ h1 { margin: 0 0 16px 0; font-size: 18px; }
24
+ h3 { margin: 16px 0 8px 0; font-size: 14px; color: #8b949e; }
25
+ """
26
+
27
+
28
+ def get_controls_styles() -> str:
29
+ """Get styles for the control panel.
30
+
31
+ Returns:
32
+ CSS string for control panel styling
33
+ """
34
+ return """
35
+ #controls {
36
+ position: absolute;
37
+ top: 20px;
38
+ left: 20px;
39
+ background: rgba(13, 17, 23, 0.95);
40
+ border: 1px solid #30363d;
41
+ border-radius: 6px;
42
+ padding: 16px;
43
+ min-width: 250px;
44
+ max-height: 80vh;
45
+ overflow-y: auto;
46
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
47
+ z-index: 500;
48
+ }
49
+
50
+ .control-group {
51
+ margin-bottom: 12px;
52
+ }
53
+
54
+ label {
55
+ display: block;
56
+ margin-bottom: 4px;
57
+ font-size: 12px;
58
+ color: #8b949e;
59
+ }
60
+
61
+ input[type="file"] {
62
+ width: 100%;
63
+ padding: 6px;
64
+ background: #161b22;
65
+ border: 1px solid #30363d;
66
+ border-radius: 6px;
67
+ color: #c9d1d9;
68
+ font-size: 12px;
69
+ }
70
+
71
+ .legend {
72
+ background: #161b22;
73
+ border: 1px solid #30363d;
74
+ border-radius: 6px;
75
+ padding: 12px;
76
+ font-size: 13px;
77
+ max-width: 300px;
78
+ margin-top: 16px;
79
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
80
+ }
81
+
82
+ .legend-category {
83
+ margin-bottom: 12px;
84
+ padding-bottom: 8px;
85
+ border-bottom: 1px solid #21262d;
86
+ }
87
+
88
+ .legend-category:last-child {
89
+ margin-bottom: 0;
90
+ padding-bottom: 0;
91
+ border-bottom: none;
92
+ }
93
+
94
+ .legend-title {
95
+ font-weight: 600;
96
+ color: #c9d1d9;
97
+ margin-bottom: 8px;
98
+ font-size: 12px;
99
+ text-transform: uppercase;
100
+ letter-spacing: 0.5px;
101
+ }
102
+
103
+ .legend-item {
104
+ display: flex;
105
+ align-items: center;
106
+ margin-bottom: 6px;
107
+ padding-left: 8px;
108
+ }
109
+
110
+ .legend-item:last-child {
111
+ margin-bottom: 0;
112
+ }
113
+
114
+ .legend-color {
115
+ width: 12px;
116
+ height: 12px;
117
+ border-radius: 50%;
118
+ margin-right: 8px;
119
+ }
120
+
121
+ .stats {
122
+ margin-top: 16px;
123
+ padding-top: 16px;
124
+ border-top: 1px solid #30363d;
125
+ font-size: 12px;
126
+ color: #8b949e;
127
+ }
128
+
129
+ .toggle-switch-container {
130
+ display: flex;
131
+ align-items: center;
132
+ justify-content: center;
133
+ gap: 12px;
134
+ padding: 10px;
135
+ background: #161b22;
136
+ border: 1px solid #30363d;
137
+ border-radius: 6px;
138
+ }
139
+
140
+ .toggle-label {
141
+ font-size: 13px;
142
+ color: #8b949e;
143
+ font-weight: 500;
144
+ transition: color 0.2s ease;
145
+ }
146
+
147
+ .toggle-label.active {
148
+ color: #58a6ff;
149
+ }
150
+
151
+ .toggle-switch {
152
+ position: relative;
153
+ display: inline-block;
154
+ width: 48px;
155
+ height: 24px;
156
+ margin: 0;
157
+ }
158
+
159
+ .toggle-switch input {
160
+ opacity: 0;
161
+ width: 100%;
162
+ height: 100%;
163
+ position: absolute;
164
+ top: 0;
165
+ left: 0;
166
+ cursor: pointer;
167
+ z-index: 2;
168
+ }
169
+
170
+ .toggle-slider {
171
+ position: absolute;
172
+ cursor: pointer;
173
+ top: 0;
174
+ left: 0;
175
+ right: 0;
176
+ bottom: 0;
177
+ background-color: #30363d;
178
+ transition: 0.3s;
179
+ border-radius: 24px;
180
+ border: 1px solid #30363d;
181
+ }
182
+
183
+ .toggle-slider:before {
184
+ position: absolute;
185
+ content: "";
186
+ height: 16px;
187
+ width: 16px;
188
+ left: 3px;
189
+ bottom: 3px;
190
+ background-color: #8b949e;
191
+ transition: 0.3s;
192
+ border-radius: 50%;
193
+ }
194
+
195
+ .toggle-switch input:checked + .toggle-slider {
196
+ background-color: #238636;
197
+ border-color: #2ea043;
198
+ }
199
+
200
+ .toggle-switch input:checked + .toggle-slider:before {
201
+ transform: translateX(24px);
202
+ background-color: #ffffff;
203
+ }
204
+
205
+ .toggle-slider:hover {
206
+ background-color: #3a424d;
207
+ }
208
+
209
+ .toggle-switch input:checked + .toggle-slider:hover {
210
+ background-color: #2ea043;
211
+ }
212
+ """
213
+
214
+
215
+ def get_graph_styles() -> str:
216
+ """Get styles for the graph SVG element.
217
+
218
+ Returns:
219
+ CSS string for graph styling
220
+ """
221
+ return """
222
+ #main-container {
223
+ position: fixed;
224
+ left: 0;
225
+ top: 0;
226
+ right: 0;
227
+ bottom: 0;
228
+ transition: right 0.3s ease-in-out;
229
+ }
230
+
231
+ #main-container.viewer-open {
232
+ right: 450px;
233
+ }
234
+
235
+ /* When viewer is expanded, handled via JS style.right */
236
+
237
+ #graph {
238
+ width: 100%;
239
+ height: 100%;
240
+ }
241
+ """
242
+
243
+
244
+ def get_node_styles() -> str:
245
+ """Get styles for graph nodes.
246
+
247
+ Returns:
248
+ CSS string for node styling including different node types
249
+ """
250
+ return """
251
+ .node circle {
252
+ cursor: pointer;
253
+ stroke: #c9d1d9;
254
+ stroke-width: 2px;
255
+ pointer-events: all;
256
+ }
257
+
258
+ .node.module circle { fill: #238636; }
259
+ .node.class circle { fill: #1f6feb; }
260
+ .node.function circle { fill: #d29922; }
261
+ .node.method circle { fill: #8957e5; }
262
+ .node.code circle { fill: #6e7681; }
263
+ .node.file circle {
264
+ fill: none;
265
+ stroke: #58a6ff;
266
+ stroke-width: 2px;
267
+ stroke-dasharray: 5,3;
268
+ opacity: 0.6;
269
+ }
270
+ .node.directory circle {
271
+ fill: none;
272
+ stroke: #79c0ff;
273
+ stroke-width: 2px;
274
+ stroke-dasharray: 3,3;
275
+ opacity: 0.5;
276
+ }
277
+ .node.subproject circle { fill: #da3633; stroke-width: 3px; }
278
+
279
+ /* Non-code document nodes - squares */
280
+ .node.docstring rect:not(.hit-area) { fill: #8b949e; }
281
+ .node.comment rect:not(.hit-area) { fill: #6e7681; }
282
+ .node rect:not(.hit-area) {
283
+ cursor: pointer;
284
+ stroke: #c9d1d9;
285
+ stroke-width: 2px;
286
+ pointer-events: all;
287
+ }
288
+
289
+ /* Hit area for file/directory nodes - transparent clickable rectangle */
290
+ .node rect.hit-area {
291
+ fill: transparent;
292
+ stroke: none;
293
+ pointer-events: all;
294
+ cursor: pointer;
295
+ }
296
+
297
+ /* Debug mode: uncomment to visualize hit areas */
298
+ /* .node rect.hit-area { fill: rgba(255, 0, 0, 0.1); stroke: red; stroke-width: 1; } */
299
+
300
+ /* File type icon styling */
301
+ .node path.file-icon {
302
+ fill: currentColor;
303
+ stroke: none;
304
+ pointer-events: none;
305
+ cursor: pointer;
306
+ }
307
+
308
+ .node text {
309
+ font-size: 14px;
310
+ fill: #c9d1d9;
311
+ /* text-anchor set by JS based on layout */
312
+ pointer-events: none;
313
+ user-select: none;
314
+ }
315
+
316
+ .node.highlighted circle,
317
+ .node.highlighted rect {
318
+ stroke: #f0e68c;
319
+ stroke-width: 3px;
320
+ filter: drop-shadow(0 0 8px #f0e68c);
321
+ }
322
+
323
+ /* Node loading spinner */
324
+ .node-loading {
325
+ stroke: #2196F3;
326
+ stroke-width: 3;
327
+ fill: none;
328
+ animation: spin 1s linear infinite;
329
+ }
330
+
331
+ .node-loading-overlay {
332
+ fill: rgba(255, 255, 255, 0.8);
333
+ pointer-events: none;
334
+ }
335
+ """
336
+
337
+
338
+ def get_link_styles() -> str:
339
+ """Get styles for graph links (edges).
340
+
341
+ Returns:
342
+ CSS string for link styling including semantic similarity and cycles
343
+ """
344
+ return """
345
+ .link {
346
+ fill: none;
347
+ stroke: #c9d1d9;
348
+ stroke-opacity: 0.8;
349
+ stroke-width: 1.5px;
350
+ }
351
+
352
+ .link.dependency {
353
+ stroke: #d29922;
354
+ stroke-opacity: 0.8;
355
+ stroke-width: 2px;
356
+ stroke-dasharray: 5,5;
357
+ }
358
+
359
+ /* Semantic relationship links - colored by similarity */
360
+ .link.semantic {
361
+ stroke-opacity: 0.7;
362
+ stroke-dasharray: 4,4;
363
+ }
364
+
365
+ .link.semantic.sim-high { stroke: #00ff00; stroke-width: 4px; }
366
+ .link.semantic.sim-medium-high { stroke: #88ff00; stroke-width: 3px; }
367
+ .link.semantic.sim-medium { stroke: #ffff00; stroke-width: 2.5px; }
368
+ .link.semantic.sim-low { stroke: #ffaa00; stroke-width: 2px; }
369
+ .link.semantic.sim-very-low { stroke: #ff0000; stroke-width: 1.5px; }
370
+
371
+ /* Circular dependency links - highest visual priority */
372
+ .link.cycle {
373
+ stroke: #ff4444 !important;
374
+ stroke-width: 3px !important;
375
+ stroke-dasharray: 8, 4;
376
+ stroke-opacity: 0.8;
377
+ animation: pulse-cycle 2s infinite;
378
+ }
379
+
380
+ @keyframes pulse-cycle {
381
+ 0%, 100% { stroke-opacity: 0.8; }
382
+ 50% { stroke-opacity: 1.0; }
383
+ }
384
+ """
385
+
386
+
387
+ def get_tooltip_styles() -> str:
388
+ """Get styles for tooltips.
389
+
390
+ Returns:
391
+ CSS string for tooltip styling
392
+ """
393
+ return """
394
+ .tooltip {
395
+ position: absolute;
396
+ padding: 12px;
397
+ background: rgba(13, 17, 23, 0.95);
398
+ border: 1px solid #30363d;
399
+ border-radius: 6px;
400
+ pointer-events: none;
401
+ display: none;
402
+ font-size: 12px;
403
+ max-width: 300px;
404
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
405
+ }
406
+
407
+ .caller-link {
408
+ color: #58a6ff;
409
+ text-decoration: none;
410
+ cursor: pointer;
411
+ transition: color 0.2s;
412
+ }
413
+
414
+ .caller-link:hover {
415
+ color: #79c0ff;
416
+ text-decoration: underline;
417
+ }
418
+ """
419
+
420
+
421
+ def get_breadcrumb_styles() -> str:
422
+ """Get styles for breadcrumb navigation.
423
+
424
+ Returns:
425
+ CSS string for breadcrumb styling
426
+ """
427
+ return """
428
+ /* Breadcrumb navigation */
429
+ .breadcrumb-nav {
430
+ margin: 0 0 10px 0;
431
+ padding: 8px 12px;
432
+ background: #161b22;
433
+ border: 1px solid #30363d;
434
+ border-radius: 4px;
435
+ font-size: 12px;
436
+ line-height: 1.6;
437
+ overflow-x: auto;
438
+ white-space: nowrap;
439
+ }
440
+
441
+ .breadcrumb-root {
442
+ color: #58a6ff;
443
+ cursor: pointer;
444
+ font-weight: 500;
445
+ transition: color 0.2s;
446
+ }
447
+
448
+ .breadcrumb-root:hover {
449
+ color: #79c0ff;
450
+ text-decoration: underline;
451
+ }
452
+
453
+ .breadcrumb-link {
454
+ color: #58a6ff;
455
+ cursor: pointer;
456
+ transition: color 0.2s;
457
+ }
458
+
459
+ .breadcrumb-link:hover {
460
+ color: #79c0ff;
461
+ text-decoration: underline;
462
+ }
463
+
464
+ .breadcrumb-separator {
465
+ color: #6e7681;
466
+ margin: 0 6px;
467
+ }
468
+
469
+ .breadcrumb-current {
470
+ color: #c9d1d9;
471
+ font-weight: 600;
472
+ }
473
+ """
474
+
475
+
476
+ def get_content_pane_styles() -> str:
477
+ """Get styles for the viewer panel (code/file/directory viewer).
478
+
479
+ Returns:
480
+ CSS string for viewer panel styling
481
+ """
482
+ return """
483
+ .viewer-panel {
484
+ position: fixed;
485
+ top: 0;
486
+ right: 0;
487
+ width: 450px;
488
+ height: 100vh;
489
+ background: rgba(13, 17, 23, 0.98);
490
+ border-left: 1px solid #30363d;
491
+ overflow-y: auto;
492
+ box-shadow: -4px 0 24px rgba(0, 0, 0, 0.5);
493
+ transform: translateX(100%);
494
+ transition: transform 0.3s ease-in-out;
495
+ z-index: 1000;
496
+ }
497
+
498
+ .viewer-panel.open {
499
+ transform: translateX(0);
500
+ }
501
+
502
+ .viewer-panel.expanded {
503
+ width: 70vw;
504
+ max-width: 1200px;
505
+ }
506
+
507
+ .viewer-header {
508
+ position: sticky;
509
+ top: 0;
510
+ background: rgba(13, 17, 23, 0.98);
511
+ padding: 16px 20px;
512
+ border-bottom: 1px solid #30363d;
513
+ z-index: 1;
514
+ }
515
+
516
+ .viewer-header-buttons {
517
+ position: absolute;
518
+ top: 12px;
519
+ right: 12px;
520
+ display: flex;
521
+ gap: 8px;
522
+ }
523
+
524
+ .viewer-expand-btn {
525
+ cursor: pointer;
526
+ color: #c9d1d9;
527
+ font-size: 16px;
528
+ line-height: 1;
529
+ background: #21262d;
530
+ border: 1px solid #30363d;
531
+ padding: 6px 8px;
532
+ transition: color 0.2s, background 0.2s, border-color 0.2s;
533
+ border-radius: 4px;
534
+ display: flex;
535
+ align-items: center;
536
+ justify-content: center;
537
+ }
538
+
539
+ .viewer-expand-btn:hover {
540
+ color: #58a6ff;
541
+ background: #30363d;
542
+ border-color: #58a6ff;
543
+ }
544
+
545
+ .viewer-title {
546
+ font-size: 16px;
547
+ font-weight: bold;
548
+ color: #58a6ff;
549
+ margin: 0 0 8px 0;
550
+ padding-right: 80px;
551
+ }
552
+
553
+ .section-nav {
554
+ margin-top: 8px;
555
+ }
556
+
557
+ .section-nav select {
558
+ background: #21262d;
559
+ color: #c9d1d9;
560
+ border: 1px solid #30363d;
561
+ border-radius: 6px;
562
+ padding: 6px 10px;
563
+ font-size: 12px;
564
+ cursor: pointer;
565
+ width: 100%;
566
+ max-width: 250px;
567
+ }
568
+
569
+ .section-nav select:hover {
570
+ border-color: #58a6ff;
571
+ }
572
+
573
+ .section-nav select:focus {
574
+ outline: none;
575
+ border-color: #58a6ff;
576
+ box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.2);
577
+ }
578
+
579
+ .viewer-close-btn {
580
+ cursor: pointer;
581
+ color: #c9d1d9;
582
+ font-size: 18px;
583
+ line-height: 1;
584
+ background: #21262d;
585
+ border: 1px solid #30363d;
586
+ padding: 6px 10px;
587
+ transition: color 0.2s, background 0.2s, border-color 0.2s;
588
+ border-radius: 4px;
589
+ display: flex;
590
+ align-items: center;
591
+ justify-content: center;
592
+ }
593
+
594
+ .viewer-close-btn:hover {
595
+ color: #f85149;
596
+ background: #30363d;
597
+ border-color: #f85149;
598
+ }
599
+
600
+ .viewer-content {
601
+ padding: 20px;
602
+ }
603
+
604
+ .viewer-section {
605
+ margin-bottom: 24px;
606
+ }
607
+
608
+ .viewer-section-title {
609
+ font-size: 13px;
610
+ font-weight: 600;
611
+ color: #8b949e;
612
+ text-transform: uppercase;
613
+ letter-spacing: 0.5px;
614
+ margin-bottom: 12px;
615
+ }
616
+
617
+ .viewer-info-grid {
618
+ display: grid;
619
+ gap: 8px;
620
+ }
621
+
622
+ .viewer-info-row {
623
+ display: flex;
624
+ font-size: 13px;
625
+ }
626
+
627
+ .viewer-info-label {
628
+ color: #8b949e;
629
+ min-width: 100px;
630
+ font-weight: 500;
631
+ }
632
+
633
+ .viewer-info-value {
634
+ color: #c9d1d9;
635
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
636
+ }
637
+
638
+ .viewer-info-value.clickable {
639
+ color: #58a6ff;
640
+ cursor: pointer;
641
+ text-decoration: underline;
642
+ text-decoration-style: dotted;
643
+ text-underline-offset: 2px;
644
+ }
645
+
646
+ .viewer-info-value.clickable:hover {
647
+ color: #79c0ff;
648
+ text-decoration-style: solid;
649
+ }
650
+
651
+ .viewer-content pre {
652
+ margin: 0;
653
+ padding: 16px;
654
+ background: #0d1117;
655
+ border: 1px solid #30363d;
656
+ border-radius: 6px;
657
+ overflow-x: auto;
658
+ font-size: 12px;
659
+ line-height: 1.6;
660
+ }
661
+
662
+ .viewer-content code {
663
+ color: #c9d1d9;
664
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
665
+ }
666
+
667
+ .chunk-list {
668
+ display: flex;
669
+ flex-direction: column;
670
+ gap: 8px;
671
+ }
672
+
673
+ .chunk-list-item {
674
+ display: flex;
675
+ align-items: center;
676
+ gap: 10px;
677
+ padding: 10px 12px;
678
+ background: #161b22;
679
+ border: 1px solid #30363d;
680
+ border-radius: 6px;
681
+ cursor: pointer;
682
+ transition: all 0.2s ease;
683
+ }
684
+
685
+ .chunk-list-item:hover {
686
+ background: #21262d;
687
+ border-color: #58a6ff;
688
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
689
+ }
690
+
691
+ /* Relationship tags for callers/callees */
692
+ .relationship-tag {
693
+ display: inline-block;
694
+ padding: 2px 8px;
695
+ border-radius: 12px;
696
+ font-size: 11px;
697
+ cursor: pointer;
698
+ transition: all 0.2s ease;
699
+ }
700
+
701
+ .relationship-tag.caller {
702
+ background: rgba(88, 166, 255, 0.15);
703
+ color: #58a6ff;
704
+ border: 1px solid rgba(88, 166, 255, 0.3);
705
+ }
706
+
707
+ .relationship-tag.caller:hover {
708
+ background: rgba(88, 166, 255, 0.3);
709
+ border-color: #58a6ff;
710
+ }
711
+
712
+ .relationship-tag.callee {
713
+ background: rgba(240, 136, 62, 0.15);
714
+ color: #f0883e;
715
+ border: 1px solid rgba(240, 136, 62, 0.3);
716
+ }
717
+
718
+ .relationship-tag.callee:hover {
719
+ background: rgba(240, 136, 62, 0.3);
720
+ border-color: #f0883e;
721
+ }
722
+
723
+ /* Semantic similarity items */
724
+ .semantic-item {
725
+ display: flex;
726
+ align-items: center;
727
+ gap: 8px;
728
+ padding: 6px 10px;
729
+ background: #161b22;
730
+ border: 1px solid #30363d;
731
+ border-radius: 4px;
732
+ cursor: pointer;
733
+ transition: all 0.2s ease;
734
+ }
735
+
736
+ .semantic-item:hover {
737
+ background: #21262d;
738
+ border-color: #a371f7;
739
+ }
740
+
741
+ .semantic-score {
742
+ font-size: 11px;
743
+ font-weight: 600;
744
+ color: #a371f7;
745
+ background: rgba(163, 113, 247, 0.15);
746
+ padding: 2px 6px;
747
+ border-radius: 4px;
748
+ min-width: 36px;
749
+ text-align: center;
750
+ }
751
+
752
+ .semantic-name {
753
+ flex: 1;
754
+ color: #c9d1d9;
755
+ font-size: 12px;
756
+ overflow: hidden;
757
+ text-overflow: ellipsis;
758
+ white-space: nowrap;
759
+ }
760
+
761
+ .semantic-type {
762
+ font-size: 10px;
763
+ color: #8b949e;
764
+ text-transform: uppercase;
765
+ }
766
+
767
+ /* External Calls/Callers Styles */
768
+ .external-call-item {
769
+ display: flex;
770
+ align-items: center;
771
+ gap: 10px;
772
+ padding: 8px 12px;
773
+ background: #161b22;
774
+ border: 1px solid #30363d;
775
+ border-radius: 6px;
776
+ cursor: pointer;
777
+ transition: all 0.2s ease;
778
+ }
779
+
780
+ .external-call-item:hover {
781
+ background: #21262d;
782
+ border-color: #58a6ff;
783
+ transform: translateX(4px);
784
+ }
785
+
786
+ .external-call-icon {
787
+ font-size: 14px;
788
+ font-weight: bold;
789
+ color: #58a6ff;
790
+ width: 20px;
791
+ text-align: center;
792
+ }
793
+
794
+ .external-call-name {
795
+ font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
796
+ font-size: 12px;
797
+ font-weight: 600;
798
+ color: #58a6ff;
799
+ flex-shrink: 0;
800
+ }
801
+
802
+ .external-call-path {
803
+ font-size: 11px;
804
+ color: #8b949e;
805
+ flex: 1;
806
+ overflow: hidden;
807
+ text-overflow: ellipsis;
808
+ white-space: nowrap;
809
+ text-align: right;
810
+ }
811
+
812
+ .chunk-icon {
813
+ font-size: 16px;
814
+ flex-shrink: 0;
815
+ }
816
+
817
+ .chunk-info {
818
+ flex: 1;
819
+ min-width: 0;
820
+ }
821
+
822
+ .chunk-name {
823
+ font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
824
+ font-size: 13px;
825
+ color: #c9d1d9;
826
+ font-weight: 500;
827
+ overflow: hidden;
828
+ text-overflow: ellipsis;
829
+ white-space: nowrap;
830
+ }
831
+
832
+ .chunk-meta {
833
+ font-size: 11px;
834
+ color: #8b949e;
835
+ margin-top: 2px;
836
+ }
837
+
838
+ .dir-list {
839
+ display: flex;
840
+ flex-direction: column;
841
+ gap: 6px;
842
+ }
843
+
844
+ .dir-list-item {
845
+ display: flex;
846
+ align-items: center;
847
+ gap: 10px;
848
+ padding: 10px 12px;
849
+ background: #161b22;
850
+ border: 1px solid #30363d;
851
+ border-radius: 6px;
852
+ cursor: pointer;
853
+ transition: all 0.2s ease;
854
+ }
855
+
856
+ .dir-list-item:hover {
857
+ background: #21262d;
858
+ border-color: #58a6ff;
859
+ }
860
+
861
+ .dir-icon {
862
+ font-size: 16px;
863
+ flex-shrink: 0;
864
+ }
865
+
866
+ .dir-name {
867
+ flex: 1;
868
+ font-size: 13px;
869
+ color: #c9d1d9;
870
+ overflow: hidden;
871
+ text-overflow: ellipsis;
872
+ white-space: nowrap;
873
+ }
874
+
875
+ .dir-type {
876
+ font-size: 11px;
877
+ color: #8b949e;
878
+ text-transform: uppercase;
879
+ }
880
+
881
+ .dir-arrow {
882
+ color: #58a6ff;
883
+ font-size: 14px;
884
+ opacity: 0;
885
+ transition: opacity 0.2s ease;
886
+ }
887
+
888
+ .dir-list-item:hover .dir-arrow {
889
+ opacity: 1;
890
+ }
891
+
892
+ .dir-list-item.clickable {
893
+ cursor: pointer;
894
+ }
895
+
896
+ /* Navigation bar styles */
897
+ .navigation-bar {
898
+ display: flex;
899
+ align-items: center;
900
+ gap: 8px;
901
+ padding: 8px 0;
902
+ margin-bottom: 12px;
903
+ border-bottom: 1px solid #30363d;
904
+ }
905
+
906
+ .nav-btn {
907
+ width: 28px;
908
+ height: 28px;
909
+ display: flex;
910
+ align-items: center;
911
+ justify-content: center;
912
+ background: #21262d;
913
+ border: 1px solid #30363d;
914
+ border-radius: 4px;
915
+ color: #c9d1d9;
916
+ cursor: pointer;
917
+ font-size: 14px;
918
+ transition: all 0.2s ease;
919
+ }
920
+
921
+ .nav-btn:hover:not(.disabled) {
922
+ background: #30363d;
923
+ border-color: #58a6ff;
924
+ }
925
+
926
+ .nav-btn.disabled {
927
+ opacity: 0.4;
928
+ cursor: not-allowed;
929
+ }
930
+
931
+ .breadcrumb-trail {
932
+ display: flex;
933
+ align-items: center;
934
+ flex-wrap: wrap;
935
+ gap: 4px;
936
+ flex: 1;
937
+ min-width: 0;
938
+ overflow: hidden;
939
+ }
940
+
941
+ .breadcrumb-separator {
942
+ color: #484f58;
943
+ font-size: 12px;
944
+ }
945
+
946
+ .breadcrumb-item {
947
+ font-size: 12px;
948
+ color: #8b949e;
949
+ max-width: 120px;
950
+ overflow: hidden;
951
+ text-overflow: ellipsis;
952
+ white-space: nowrap;
953
+ }
954
+
955
+ .breadcrumb-item.clickable {
956
+ color: #58a6ff;
957
+ cursor: pointer;
958
+ }
959
+
960
+ .breadcrumb-item.clickable:hover {
961
+ text-decoration: underline;
962
+ }
963
+
964
+ .breadcrumb-item.current {
965
+ color: #c9d1d9;
966
+ font-weight: 500;
967
+ }
968
+
969
+ /* Node highlight animation */
970
+ .node-highlight circle {
971
+ stroke: #58a6ff !important;
972
+ stroke-width: 3px !important;
973
+ filter: drop-shadow(0 0 6px rgba(88, 166, 255, 0.6));
974
+ }
975
+
976
+ .node-highlight text {
977
+ fill: #58a6ff !important;
978
+ font-weight: bold !important;
979
+ }
980
+ """
981
+
982
+
983
+ def get_code_chunks_styles() -> str:
984
+ """Get styles for code chunks section in file viewer.
985
+
986
+ Returns:
987
+ CSS string for code chunks styling
988
+ """
989
+ return """
990
+ /* Code chunks section */
991
+ .code-chunks-section {
992
+ margin: 0 0 20px 0;
993
+ padding: 15px;
994
+ background: #161b22;
995
+ border-radius: 6px;
996
+ border: 1px solid #30363d;
997
+ }
998
+
999
+ .section-header {
1000
+ margin: 0 0 12px 0;
1001
+ font-size: 13px;
1002
+ font-weight: 600;
1003
+ color: #c9d1d9;
1004
+ text-transform: uppercase;
1005
+ letter-spacing: 0.5px;
1006
+ }
1007
+
1008
+ .code-chunks-list {
1009
+ display: flex;
1010
+ flex-direction: column;
1011
+ gap: 6px;
1012
+ }
1013
+
1014
+ .code-chunk-item {
1015
+ display: flex;
1016
+ align-items: center;
1017
+ gap: 8px;
1018
+ padding: 8px 12px;
1019
+ background: #0d1117;
1020
+ border: 1px solid #30363d;
1021
+ border-radius: 4px;
1022
+ cursor: pointer;
1023
+ transition: all 0.2s ease;
1024
+ }
1025
+
1026
+ .code-chunk-item:hover {
1027
+ background: #21262d;
1028
+ border-color: #58a6ff;
1029
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
1030
+ }
1031
+
1032
+ .chunk-icon {
1033
+ font-size: 16px;
1034
+ flex-shrink: 0;
1035
+ }
1036
+
1037
+ .chunk-name {
1038
+ flex: 1;
1039
+ font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
1040
+ font-size: 13px;
1041
+ color: #c9d1d9;
1042
+ font-weight: 500;
1043
+ overflow: hidden;
1044
+ text-overflow: ellipsis;
1045
+ white-space: nowrap;
1046
+ }
1047
+
1048
+ .line-range {
1049
+ font-size: 11px;
1050
+ color: #8b949e;
1051
+ font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
1052
+ background: #161b22;
1053
+ padding: 2px 6px;
1054
+ border-radius: 3px;
1055
+ flex-shrink: 0;
1056
+ }
1057
+
1058
+ .chunk-type {
1059
+ font-size: 11px;
1060
+ color: #ffffff;
1061
+ background: #6e7681;
1062
+ padding: 2px 8px;
1063
+ border-radius: 12px;
1064
+ text-transform: lowercase;
1065
+ flex-shrink: 0;
1066
+ }
1067
+
1068
+ /* Type-specific colors for chunk badges */
1069
+ .code-chunk-item[data-type="function"] .chunk-type {
1070
+ background: #d29922;
1071
+ }
1072
+
1073
+ .code-chunk-item[data-type="class"] .chunk-type {
1074
+ background: #1f6feb;
1075
+ }
1076
+
1077
+ .code-chunk-item[data-type="method"] .chunk-type {
1078
+ background: #8957e5;
1079
+ }
1080
+
1081
+ .code-chunk-item[data-type="code"] .chunk-type {
1082
+ background: #6e7681;
1083
+ }
1084
+ """
1085
+
1086
+
1087
+ def get_reset_button_styles() -> str:
1088
+ """Get styles for the reset view button.
1089
+
1090
+ Returns:
1091
+ CSS string for reset button styling
1092
+ """
1093
+ return """
1094
+ #reset-view-btn {
1095
+ position: fixed;
1096
+ top: 20px;
1097
+ right: 460px;
1098
+ padding: 8px 16px;
1099
+ background: #21262d;
1100
+ border: 1px solid #30363d;
1101
+ border-radius: 6px;
1102
+ color: #c9d1d9;
1103
+ font-size: 14px;
1104
+ cursor: pointer;
1105
+ display: flex;
1106
+ align-items: center;
1107
+ gap: 8px;
1108
+ z-index: 100;
1109
+ transition: all 0.2s;
1110
+ }
1111
+
1112
+ #reset-view-btn:hover {
1113
+ background: #30363d;
1114
+ border-color: #58a6ff;
1115
+ transform: translateY(-1px);
1116
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
1117
+ }
1118
+ """
1119
+
1120
+
1121
+ def get_spinner_styles() -> str:
1122
+ """Get styles for the loading spinner animation.
1123
+
1124
+ Returns:
1125
+ CSS string for spinner styling and animation
1126
+ """
1127
+ return """
1128
+ /* Loading spinner animation */
1129
+ @keyframes spin {
1130
+ 0% { transform: rotate(0deg); }
1131
+ 100% { transform: rotate(360deg); }
1132
+ }
1133
+
1134
+ .spinner {
1135
+ display: inline-block;
1136
+ width: 20px;
1137
+ height: 20px;
1138
+ border: 3px solid #30363d;
1139
+ border-top-color: #58a6ff;
1140
+ border-radius: 50%;
1141
+ animation: spin 0.8s linear infinite;
1142
+ margin-right: 8px;
1143
+ vertical-align: middle;
1144
+ }
1145
+ """
1146
+
1147
+
1148
+ def get_search_styles() -> str:
1149
+ """Get styles for the search box and results dropdown.
1150
+
1151
+ Returns:
1152
+ CSS string for search styling
1153
+ """
1154
+ return """
1155
+ /* Search box styles */
1156
+ .search-container {
1157
+ position: relative;
1158
+ margin-bottom: 16px;
1159
+ z-index: 100;
1160
+ }
1161
+
1162
+ #search-input {
1163
+ width: 100%;
1164
+ padding: 10px 12px;
1165
+ background: #161b22;
1166
+ border: 1px solid #30363d;
1167
+ border-radius: 6px;
1168
+ color: #c9d1d9;
1169
+ font-size: 13px;
1170
+ outline: none;
1171
+ transition: border-color 0.2s, box-shadow 0.2s;
1172
+ box-sizing: border-box;
1173
+ position: relative;
1174
+ z-index: 101;
1175
+ cursor: text;
1176
+ }
1177
+
1178
+ #search-input:focus {
1179
+ border-color: #58a6ff;
1180
+ box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.2);
1181
+ }
1182
+
1183
+ #search-input::placeholder {
1184
+ color: #8b949e;
1185
+ }
1186
+
1187
+ /* Search results dropdown */
1188
+ .search-results {
1189
+ position: absolute;
1190
+ top: 100%;
1191
+ left: 0;
1192
+ right: 0;
1193
+ max-height: 300px;
1194
+ overflow-y: auto;
1195
+ background: #161b22;
1196
+ border: 1px solid #30363d;
1197
+ border-top: none;
1198
+ border-radius: 0 0 6px 6px;
1199
+ z-index: 1000;
1200
+ display: none;
1201
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
1202
+ }
1203
+
1204
+ .search-results.visible {
1205
+ display: block;
1206
+ }
1207
+
1208
+ .search-result-item {
1209
+ display: flex;
1210
+ align-items: center;
1211
+ gap: 10px;
1212
+ padding: 10px 12px;
1213
+ cursor: pointer;
1214
+ border-bottom: 1px solid #21262d;
1215
+ transition: background 0.15s;
1216
+ }
1217
+
1218
+ .search-result-item:last-child {
1219
+ border-bottom: none;
1220
+ }
1221
+
1222
+ .search-result-item:hover,
1223
+ .search-result-item.selected {
1224
+ background: #21262d;
1225
+ }
1226
+
1227
+ .search-result-icon {
1228
+ font-size: 14px;
1229
+ flex-shrink: 0;
1230
+ width: 20px;
1231
+ text-align: center;
1232
+ }
1233
+
1234
+ .search-result-info {
1235
+ flex: 1;
1236
+ min-width: 0;
1237
+ overflow: hidden;
1238
+ }
1239
+
1240
+ .search-result-name {
1241
+ font-size: 13px;
1242
+ color: #c9d1d9;
1243
+ font-weight: 500;
1244
+ overflow: hidden;
1245
+ text-overflow: ellipsis;
1246
+ white-space: nowrap;
1247
+ }
1248
+
1249
+ .search-result-name mark {
1250
+ background: rgba(88, 166, 255, 0.3);
1251
+ color: #58a6ff;
1252
+ border-radius: 2px;
1253
+ padding: 0 2px;
1254
+ }
1255
+
1256
+ .search-result-path {
1257
+ font-size: 11px;
1258
+ color: #8b949e;
1259
+ overflow: hidden;
1260
+ text-overflow: ellipsis;
1261
+ white-space: nowrap;
1262
+ margin-top: 2px;
1263
+ }
1264
+
1265
+ .search-result-type {
1266
+ font-size: 10px;
1267
+ color: #8b949e;
1268
+ background: #21262d;
1269
+ padding: 2px 6px;
1270
+ border-radius: 10px;
1271
+ text-transform: uppercase;
1272
+ flex-shrink: 0;
1273
+ }
1274
+
1275
+ .search-no-results {
1276
+ padding: 20px;
1277
+ text-align: center;
1278
+ color: #8b949e;
1279
+ font-size: 13px;
1280
+ }
1281
+
1282
+ .search-hint {
1283
+ padding: 8px 12px;
1284
+ font-size: 11px;
1285
+ color: #6e7681;
1286
+ background: #0d1117;
1287
+ border-top: 1px solid #21262d;
1288
+ }
1289
+ """
1290
+
1291
+
1292
+ def get_all_styles() -> str:
1293
+ """Get all CSS styles combined.
1294
+
1295
+ Returns:
1296
+ Complete CSS string for the visualization
1297
+ """
1298
+ return "".join(
1299
+ [
1300
+ get_base_styles(),
1301
+ get_controls_styles(),
1302
+ get_graph_styles(),
1303
+ get_node_styles(),
1304
+ get_link_styles(),
1305
+ get_tooltip_styles(),
1306
+ get_breadcrumb_styles(),
1307
+ get_content_pane_styles(),
1308
+ get_code_chunks_styles(),
1309
+ get_reset_button_styles(),
1310
+ get_spinner_styles(),
1311
+ get_search_styles(),
1312
+ ]
1313
+ )