claude-mpm 4.1.11__py3-none-any.whl → 4.1.13__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/INSTRUCTIONS.md +8 -0
- claude_mpm/cli/__init__.py +1 -1
- claude_mpm/cli/commands/monitor.py +88 -627
- claude_mpm/cli/commands/mpm_init.py +127 -107
- claude_mpm/cli/commands/mpm_init_handler.py +24 -23
- claude_mpm/cli/parsers/mpm_init_parser.py +34 -28
- claude_mpm/core/config.py +18 -0
- claude_mpm/core/instruction_reinforcement_hook.py +266 -0
- claude_mpm/core/pm_hook_interceptor.py +105 -8
- claude_mpm/dashboard/static/built/components/activity-tree.js +1 -1
- claude_mpm/dashboard/static/built/components/code-tree.js +1 -1
- claude_mpm/dashboard/static/built/dashboard.js +1 -1
- claude_mpm/dashboard/static/built/socket-client.js +1 -1
- claude_mpm/dashboard/static/css/activity.css +1239 -267
- claude_mpm/dashboard/static/css/dashboard.css +511 -0
- claude_mpm/dashboard/static/dist/components/activity-tree.js +1 -1
- claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
- claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/js/components/activity-tree.js +1193 -892
- claude_mpm/dashboard/static/js/components/build-tracker.js +15 -13
- claude_mpm/dashboard/static/js/components/code-tree.js +534 -143
- claude_mpm/dashboard/static/js/components/module-viewer.js +21 -7
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +1066 -0
- claude_mpm/dashboard/static/js/connection-manager.js +1 -1
- claude_mpm/dashboard/static/js/dashboard.js +227 -84
- claude_mpm/dashboard/static/js/socket-client.js +2 -2
- claude_mpm/dashboard/templates/index.html +100 -23
- claude_mpm/services/agents/deployment/agent_template_builder.py +11 -7
- claude_mpm/services/cli/socketio_manager.py +39 -8
- claude_mpm/services/infrastructure/monitoring.py +1 -1
- claude_mpm/services/socketio/handlers/code_analysis.py +83 -136
- claude_mpm/tools/code_tree_analyzer.py +290 -202
- {claude_mpm-4.1.11.dist-info → claude_mpm-4.1.13.dist-info}/METADATA +1 -1
- {claude_mpm-4.1.11.dist-info → claude_mpm-4.1.13.dist-info}/RECORD +41 -39
- {claude_mpm-4.1.11.dist-info → claude_mpm-4.1.13.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.11.dist-info → claude_mpm-4.1.13.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.11.dist-info → claude_mpm-4.1.13.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.11.dist-info → claude_mpm-4.1.13.dist-info}/top_level.txt +0 -0
|
@@ -175,24 +175,8 @@ class CodeTree {
|
|
|
175
175
|
toggleLegendBtn.addEventListener('click', () => this.toggleLegend());
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
// Listen for show hidden files toggle
|
|
179
|
-
const showHiddenFilesCheckbox = document.getElementById('show-hidden-files');
|
|
180
|
-
if (showHiddenFilesCheckbox) {
|
|
181
|
-
showHiddenFilesCheckbox.addEventListener('change', () => {
|
|
182
|
-
// Clear tree and re-discover with new settings
|
|
183
|
-
this.autoDiscovered = false;
|
|
184
|
-
this.initializeTreeData();
|
|
185
|
-
this.autoDiscoverRootLevel();
|
|
186
|
-
this.showNotification(
|
|
187
|
-
showHiddenFilesCheckbox.checked ? 'Showing hidden files' : 'Hiding hidden files',
|
|
188
|
-
'info'
|
|
189
|
-
);
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
|
|
193
178
|
// Listen for working directory changes
|
|
194
179
|
document.addEventListener('workingDirectoryChanged', (e) => {
|
|
195
|
-
console.log('Working directory changed to:', e.detail.directory);
|
|
196
180
|
this.onWorkingDirectoryChanged(e.detail.directory);
|
|
197
181
|
});
|
|
198
182
|
}
|
|
@@ -402,17 +386,31 @@ class CodeTree {
|
|
|
402
386
|
.style('pointer-events', 'none');
|
|
403
387
|
}
|
|
404
388
|
|
|
389
|
+
/**
|
|
390
|
+
* Clear all D3 visualization elements
|
|
391
|
+
*/
|
|
392
|
+
clearD3Visualization() {
|
|
393
|
+
if (this.treeGroup) {
|
|
394
|
+
// Remove all existing nodes and links
|
|
395
|
+
this.treeGroup.selectAll('g.node').remove();
|
|
396
|
+
this.treeGroup.selectAll('path.link').remove();
|
|
397
|
+
}
|
|
398
|
+
// Reset node ID counter for proper tracking
|
|
399
|
+
this.nodeId = 0;
|
|
400
|
+
}
|
|
401
|
+
|
|
405
402
|
/**
|
|
406
403
|
* Initialize tree data structure
|
|
407
404
|
*/
|
|
408
405
|
initializeTreeData() {
|
|
409
406
|
const workingDir = this.getWorkingDirectory();
|
|
410
407
|
const dirName = workingDir ? workingDir.split('/').pop() || 'Project Root' : 'Project Root';
|
|
411
|
-
const path = workingDir || '.';
|
|
412
408
|
|
|
409
|
+
// Use '.' as the root path for consistency with relative path handling
|
|
410
|
+
// The actual working directory is retrieved via getWorkingDirectory() when needed
|
|
413
411
|
this.treeData = {
|
|
414
412
|
name: dirName,
|
|
415
|
-
path: path
|
|
413
|
+
path: '.', // Always use '.' for root to simplify path handling
|
|
416
414
|
type: 'root',
|
|
417
415
|
children: [],
|
|
418
416
|
loaded: false,
|
|
@@ -470,7 +468,6 @@ class CodeTree {
|
|
|
470
468
|
return;
|
|
471
469
|
}
|
|
472
470
|
|
|
473
|
-
console.log('Auto-discovering root level for:', workingDir);
|
|
474
471
|
|
|
475
472
|
this.autoDiscovered = true;
|
|
476
473
|
this.analyzing = true;
|
|
@@ -494,7 +491,7 @@ class CodeTree {
|
|
|
494
491
|
const dirName = workingDir.split('/').pop() || 'Project Root';
|
|
495
492
|
this.treeData = {
|
|
496
493
|
name: dirName,
|
|
497
|
-
path:
|
|
494
|
+
path: '.', // Use '.' for root to maintain consistency with relative path handling
|
|
498
495
|
type: 'root',
|
|
499
496
|
children: [],
|
|
500
497
|
loaded: false,
|
|
@@ -520,12 +517,7 @@ class CodeTree {
|
|
|
520
517
|
// Get ignore patterns
|
|
521
518
|
const ignorePatterns = document.getElementById('ignore-patterns')?.value || '';
|
|
522
519
|
|
|
523
|
-
//
|
|
524
|
-
const showHiddenFiles = document.getElementById('show-hidden-files')?.checked || false;
|
|
525
|
-
|
|
526
|
-
// Debug logging
|
|
527
|
-
console.log('[DEBUG] Show hidden files checkbox value:', showHiddenFiles);
|
|
528
|
-
console.log('[DEBUG] Checkbox element:', document.getElementById('show-hidden-files'));
|
|
520
|
+
// Enhanced debug logging
|
|
529
521
|
|
|
530
522
|
// Request top-level discovery with working directory
|
|
531
523
|
const requestPayload = {
|
|
@@ -533,10 +525,10 @@ class CodeTree {
|
|
|
533
525
|
depth: 'top_level',
|
|
534
526
|
languages: selectedLanguages,
|
|
535
527
|
ignore_patterns: ignorePatterns,
|
|
536
|
-
|
|
528
|
+
request_id: `discover_${Date.now()}` // Add request ID for tracking
|
|
537
529
|
};
|
|
538
530
|
|
|
539
|
-
|
|
531
|
+
// Sending top-level discovery request
|
|
540
532
|
|
|
541
533
|
if (this.socket) {
|
|
542
534
|
this.socket.emit('code:discover:top_level', requestPayload);
|
|
@@ -637,6 +629,7 @@ class CodeTree {
|
|
|
637
629
|
this.socket.on('code:analysis:error', (data) => this.onAnalysisError(data));
|
|
638
630
|
|
|
639
631
|
// Node discovery events
|
|
632
|
+
this.socket.on('code:top_level:discovered', (data) => this.onTopLevelDiscovered(data));
|
|
640
633
|
this.socket.on('code:directory:discovered', (data) => this.onDirectoryDiscovered(data));
|
|
641
634
|
this.socket.on('code:file:discovered', (data) => this.onFileDiscovered(data));
|
|
642
635
|
this.socket.on('code:file:analyzed', (data) => this.onFileAnalyzed(data));
|
|
@@ -649,27 +642,73 @@ class CodeTree {
|
|
|
649
642
|
this.socket.on('code:directory:contents', (data) => {
|
|
650
643
|
// Update the requested directory with its contents
|
|
651
644
|
if (data.path) {
|
|
652
|
-
|
|
645
|
+
// Convert absolute path back to relative path to match tree nodes
|
|
646
|
+
let searchPath = data.path;
|
|
647
|
+
const workingDir = this.getWorkingDirectory();
|
|
648
|
+
if (workingDir && searchPath.startsWith(workingDir)) {
|
|
649
|
+
// Remove working directory prefix to get relative path
|
|
650
|
+
searchPath = searchPath.substring(workingDir.length).replace(/^\//, '');
|
|
651
|
+
// If empty after removing prefix, it's the root
|
|
652
|
+
if (!searchPath) {
|
|
653
|
+
searchPath = '.';
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const node = this.findNodeByPath(searchPath);
|
|
653
658
|
if (node && data.children) {
|
|
654
|
-
// Find D3 node and remove loading pulse
|
|
655
|
-
const d3Node = this.findD3NodeByPath(
|
|
656
|
-
if (d3Node && this.loadingNodes.has(
|
|
659
|
+
// Find D3 node and remove loading pulse (use searchPath, not data.path)
|
|
660
|
+
const d3Node = this.findD3NodeByPath(searchPath);
|
|
661
|
+
if (d3Node && this.loadingNodes.has(searchPath)) {
|
|
657
662
|
this.removeLoadingPulse(d3Node);
|
|
658
663
|
}
|
|
659
|
-
node.children = data.children.map(child =>
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
664
|
+
node.children = data.children.map(child => {
|
|
665
|
+
// Construct full path for child by combining parent path with child name
|
|
666
|
+
// The backend now returns just the item name, not the full path
|
|
667
|
+
let childPath;
|
|
668
|
+
if (searchPath === '.' || searchPath === '') {
|
|
669
|
+
// Root level - child path is just the name
|
|
670
|
+
childPath = child.name || child.path;
|
|
671
|
+
} else {
|
|
672
|
+
// Subdirectory - combine parent path with child name
|
|
673
|
+
// Use child.name (backend returns just the name) or fallback to child.path
|
|
674
|
+
const childName = child.name || child.path;
|
|
675
|
+
childPath = `${searchPath}/${childName}`;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return {
|
|
679
|
+
...child,
|
|
680
|
+
path: childPath, // Override with constructed path
|
|
681
|
+
loaded: child.type === 'directory' ? false : undefined,
|
|
682
|
+
analyzed: child.type === 'file' ? false : undefined,
|
|
683
|
+
expanded: false,
|
|
684
|
+
children: []
|
|
685
|
+
};
|
|
686
|
+
});
|
|
666
687
|
node.loaded = true;
|
|
688
|
+
node.expanded = true; // Mark as expanded to show children
|
|
667
689
|
|
|
668
|
-
// Update D3 hierarchy
|
|
690
|
+
// Update D3 hierarchy and make sure the node is expanded
|
|
669
691
|
if (this.root && this.svg) {
|
|
692
|
+
// Store old root to preserve expansion state
|
|
693
|
+
const oldRoot = this.root;
|
|
694
|
+
|
|
695
|
+
// Recreate hierarchy with updated data
|
|
670
696
|
this.root = d3.hierarchy(this.treeData);
|
|
671
697
|
this.root.x0 = this.height / 2;
|
|
672
698
|
this.root.y0 = 0;
|
|
699
|
+
|
|
700
|
+
// Preserve expansion state from old tree
|
|
701
|
+
this.preserveExpansionState(oldRoot, this.root);
|
|
702
|
+
|
|
703
|
+
// Find the D3 node again after hierarchy recreation
|
|
704
|
+
const updatedD3Node = this.findD3NodeByPath(searchPath);
|
|
705
|
+
if (updatedD3Node) {
|
|
706
|
+
// Ensure children are visible (not collapsed)
|
|
707
|
+
updatedD3Node.children = updatedD3Node._children || updatedD3Node.children;
|
|
708
|
+
updatedD3Node._children = null;
|
|
709
|
+
updatedD3Node.data.expanded = true;
|
|
710
|
+
}
|
|
711
|
+
|
|
673
712
|
this.update(this.root);
|
|
674
713
|
}
|
|
675
714
|
|
|
@@ -689,6 +728,7 @@ class CodeTree {
|
|
|
689
728
|
// Top level discovery response
|
|
690
729
|
this.socket.on('code:top_level:discovered', (data) => {
|
|
691
730
|
if (data.items && Array.isArray(data.items)) {
|
|
731
|
+
|
|
692
732
|
// Add discovered items to the root node
|
|
693
733
|
this.treeData.children = data.items.map(item => ({
|
|
694
734
|
name: item.name,
|
|
@@ -713,9 +753,14 @@ class CodeTree {
|
|
|
713
753
|
|
|
714
754
|
// Update D3 hierarchy
|
|
715
755
|
if (typeof d3 !== 'undefined') {
|
|
756
|
+
// Clear any existing nodes before creating new ones
|
|
757
|
+
this.clearD3Visualization();
|
|
758
|
+
|
|
759
|
+
// Create new hierarchy
|
|
716
760
|
this.root = d3.hierarchy(this.treeData);
|
|
717
761
|
this.root.x0 = this.height / 2;
|
|
718
762
|
this.root.y0 = 0;
|
|
763
|
+
|
|
719
764
|
if (this.svg) {
|
|
720
765
|
this.update(this.root);
|
|
721
766
|
}
|
|
@@ -758,6 +803,77 @@ class CodeTree {
|
|
|
758
803
|
this.updateStats();
|
|
759
804
|
}
|
|
760
805
|
|
|
806
|
+
/**
|
|
807
|
+
* Handle top-level discovery event (initial root directory scan)
|
|
808
|
+
*/
|
|
809
|
+
onTopLevelDiscovered(data) {
|
|
810
|
+
// Received top-level discovery response
|
|
811
|
+
|
|
812
|
+
// Update activity ticker
|
|
813
|
+
this.updateActivityTicker(`📁 Discovered ${(data.items || []).length} top-level items`, 'success');
|
|
814
|
+
|
|
815
|
+
// Add to events display
|
|
816
|
+
this.addEventToDisplay(`📁 Found ${(data.items || []).length} top-level items in project root`, 'info');
|
|
817
|
+
|
|
818
|
+
// The root node (with path '.') should receive the children
|
|
819
|
+
const rootNode = this.findNodeByPath('.');
|
|
820
|
+
|
|
821
|
+
console.log('🔎 Looking for root node with path ".", found:', rootNode ? {
|
|
822
|
+
name: rootNode.name,
|
|
823
|
+
path: rootNode.path,
|
|
824
|
+
currentChildren: rootNode.children ? rootNode.children.length : 0
|
|
825
|
+
} : 'NOT FOUND');
|
|
826
|
+
|
|
827
|
+
if (rootNode && data.items) {
|
|
828
|
+
console.log('🌳 Populating root node with children');
|
|
829
|
+
|
|
830
|
+
// Update the root node with discovered children
|
|
831
|
+
rootNode.children = data.items.map(child => {
|
|
832
|
+
// Items at root level get their name as the path
|
|
833
|
+
const childPath = child.name;
|
|
834
|
+
|
|
835
|
+
console.log(` Adding child: ${child.name} with path: ${childPath}`);
|
|
836
|
+
|
|
837
|
+
return {
|
|
838
|
+
name: child.name,
|
|
839
|
+
path: childPath, // Just the name for top-level items
|
|
840
|
+
type: child.type,
|
|
841
|
+
loaded: child.type === 'directory' ? false : undefined,
|
|
842
|
+
analyzed: child.type === 'file' ? false : undefined,
|
|
843
|
+
expanded: false,
|
|
844
|
+
children: child.type === 'directory' ? [] : undefined,
|
|
845
|
+
size: child.size,
|
|
846
|
+
has_code: child.has_code
|
|
847
|
+
};
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
rootNode.loaded = true;
|
|
851
|
+
rootNode.expanded = true;
|
|
852
|
+
|
|
853
|
+
// Update D3 hierarchy and render
|
|
854
|
+
if (this.root && this.svg) {
|
|
855
|
+
// Recreate hierarchy with new data
|
|
856
|
+
this.root = d3.hierarchy(this.treeData);
|
|
857
|
+
this.root.x0 = this.height / 2;
|
|
858
|
+
this.root.y0 = 0;
|
|
859
|
+
|
|
860
|
+
// Update the tree visualization
|
|
861
|
+
this.update(this.root);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Hide loading and show success
|
|
865
|
+
this.hideLoading();
|
|
866
|
+
this.updateBreadcrumb(`Discovered ${data.items.length} items`, 'success');
|
|
867
|
+
this.showNotification(`Found ${data.items.length} top-level items`, 'success');
|
|
868
|
+
} else {
|
|
869
|
+
console.error('❌ Could not find root node to populate');
|
|
870
|
+
this.showNotification('Failed to populate root directory', 'error');
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// Mark analysis as complete
|
|
874
|
+
this.analyzing = false;
|
|
875
|
+
}
|
|
876
|
+
|
|
761
877
|
/**
|
|
762
878
|
* Handle directory discovered event
|
|
763
879
|
*/
|
|
@@ -768,48 +884,106 @@ class CodeTree {
|
|
|
768
884
|
// Add to events display
|
|
769
885
|
this.addEventToDisplay(`📁 Found ${(data.children || []).length} items in: ${data.name || data.path}`, 'info');
|
|
770
886
|
|
|
887
|
+
console.log('📥 Received directory discovery:', {
|
|
888
|
+
path: data.path,
|
|
889
|
+
name: data.name,
|
|
890
|
+
childrenCount: (data.children || []).length,
|
|
891
|
+
children: (data.children || []).map(c => ({ name: c.name, type: c.type })),
|
|
892
|
+
workingDir: this.getWorkingDirectory()
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
// Convert absolute path back to relative path to match tree nodes
|
|
896
|
+
let searchPath = data.path;
|
|
897
|
+
const workingDir = this.getWorkingDirectory();
|
|
898
|
+
if (workingDir && searchPath.startsWith(workingDir)) {
|
|
899
|
+
// Remove working directory prefix to get relative path
|
|
900
|
+
searchPath = searchPath.substring(workingDir.length).replace(/^\//, '');
|
|
901
|
+
// If empty after removing prefix, it's the root
|
|
902
|
+
if (!searchPath) {
|
|
903
|
+
searchPath = '.';
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
console.log('🔎 Searching for node with path:', searchPath);
|
|
908
|
+
|
|
771
909
|
// Find the node that was clicked to trigger this discovery
|
|
772
|
-
const node = this.findNodeByPath(
|
|
910
|
+
const node = this.findNodeByPath(searchPath);
|
|
911
|
+
|
|
912
|
+
// Located target node for expansion
|
|
913
|
+
|
|
773
914
|
if (node && data.children) {
|
|
774
915
|
// Update the node with discovered children
|
|
775
|
-
node.children = data.children.map(child =>
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
916
|
+
node.children = data.children.map(child => {
|
|
917
|
+
// Construct full path for child by combining parent path with child name
|
|
918
|
+
// The backend now returns just the item name, not the full path
|
|
919
|
+
let childPath;
|
|
920
|
+
if (searchPath === '.' || searchPath === '') {
|
|
921
|
+
// Root level - child path is just the name
|
|
922
|
+
childPath = child.name || child.path;
|
|
923
|
+
} else {
|
|
924
|
+
// Subdirectory - combine parent path with child name
|
|
925
|
+
// Use child.name (backend returns just the name) or fallback to child.path
|
|
926
|
+
const childName = child.name || child.path;
|
|
927
|
+
childPath = `${searchPath}/${childName}`;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
return {
|
|
931
|
+
name: child.name,
|
|
932
|
+
path: childPath, // Use constructed path instead of child.path
|
|
933
|
+
type: child.type,
|
|
934
|
+
loaded: child.type === 'directory' ? false : undefined,
|
|
935
|
+
analyzed: child.type === 'file' ? false : undefined,
|
|
936
|
+
expanded: false,
|
|
937
|
+
children: child.type === 'directory' ? [] : undefined,
|
|
938
|
+
size: child.size,
|
|
939
|
+
has_code: child.has_code
|
|
940
|
+
};
|
|
941
|
+
});
|
|
786
942
|
node.loaded = true;
|
|
787
943
|
node.expanded = true;
|
|
788
944
|
|
|
789
|
-
// Find D3 node and remove loading pulse
|
|
790
|
-
const d3Node = this.findD3NodeByPath(
|
|
945
|
+
// Find D3 node and remove loading pulse (use searchPath, not data.path)
|
|
946
|
+
const d3Node = this.findD3NodeByPath(searchPath);
|
|
791
947
|
if (d3Node) {
|
|
792
948
|
// Remove loading animation
|
|
793
|
-
if (this.loadingNodes.has(
|
|
949
|
+
if (this.loadingNodes.has(searchPath)) {
|
|
794
950
|
this.removeLoadingPulse(d3Node);
|
|
795
951
|
}
|
|
796
|
-
|
|
797
|
-
// Expand the node in D3
|
|
798
|
-
if (d3Node.data) {
|
|
799
|
-
d3Node.data.children = node.children;
|
|
800
|
-
d3Node._children = null;
|
|
801
|
-
}
|
|
802
952
|
}
|
|
803
953
|
|
|
804
|
-
// Update D3 hierarchy and redraw
|
|
954
|
+
// Update D3 hierarchy and redraw with expanded node
|
|
805
955
|
if (this.root && this.svg) {
|
|
956
|
+
// Store old root to preserve expansion state
|
|
957
|
+
const oldRoot = this.root;
|
|
958
|
+
|
|
959
|
+
// Recreate hierarchy with updated data
|
|
806
960
|
this.root = d3.hierarchy(this.treeData);
|
|
961
|
+
|
|
962
|
+
// Restore positions for smooth animation
|
|
963
|
+
this.root.x0 = this.height / 2;
|
|
964
|
+
this.root.y0 = 0;
|
|
965
|
+
|
|
966
|
+
// Preserve expansion state from old tree
|
|
967
|
+
this.preserveExpansionState(oldRoot, this.root);
|
|
968
|
+
|
|
969
|
+
// Find the D3 node again after hierarchy recreation
|
|
970
|
+
const updatedD3Node = this.findD3NodeByPath(searchPath);
|
|
971
|
+
if (updatedD3Node && updatedD3Node.data.children && updatedD3Node.data.children.length > 0) {
|
|
972
|
+
// Ensure the node is expanded to show children
|
|
973
|
+
updatedD3Node.children = updatedD3Node._children || updatedD3Node.children;
|
|
974
|
+
updatedD3Node._children = null;
|
|
975
|
+
// Mark data as expanded
|
|
976
|
+
updatedD3Node.data.expanded = true;
|
|
977
|
+
}
|
|
978
|
+
|
|
807
979
|
this.update(this.root);
|
|
808
980
|
}
|
|
809
981
|
|
|
810
982
|
this.updateBreadcrumb(`Loaded ${node.children.length} items from ${node.name}`, 'success');
|
|
811
983
|
this.updateStats();
|
|
812
984
|
} else if (!node) {
|
|
985
|
+
this.logAllPaths(this.treeData);
|
|
986
|
+
} else if (node && !data.children) {
|
|
813
987
|
// This might be a top-level directory discovery
|
|
814
988
|
const pathParts = data.path ? data.path.split('/').filter(p => p) : [];
|
|
815
989
|
const isTopLevel = pathParts.length === 1;
|
|
@@ -1069,7 +1243,6 @@ class CodeTree {
|
|
|
1069
1243
|
*/
|
|
1070
1244
|
onInfoEvent(data) {
|
|
1071
1245
|
// Log to console for debugging
|
|
1072
|
-
console.log('[INFO]', data.type, data.message);
|
|
1073
1246
|
|
|
1074
1247
|
// Update breadcrumb for certain events
|
|
1075
1248
|
if (data.type && data.type.startsWith('discovery.')) {
|
|
@@ -1080,7 +1253,6 @@ class CodeTree {
|
|
|
1080
1253
|
this.updateBreadcrumb(data.message, 'success');
|
|
1081
1254
|
// Show stats if available
|
|
1082
1255
|
if (data.stats) {
|
|
1083
|
-
console.log('[DISCOVERY STATS]', data.stats);
|
|
1084
1256
|
}
|
|
1085
1257
|
} else if (data.type === 'discovery.directory' || data.type === 'discovery.file') {
|
|
1086
1258
|
// Quick flash of discovery events
|
|
@@ -1095,7 +1267,6 @@ class CodeTree {
|
|
|
1095
1267
|
// Show stats if available
|
|
1096
1268
|
if (data.stats) {
|
|
1097
1269
|
const statsMsg = `Found: ${data.stats.classes || 0} classes, ${data.stats.functions || 0} functions, ${data.stats.methods || 0} methods`;
|
|
1098
|
-
console.log('[ANALYSIS STATS]', statsMsg);
|
|
1099
1270
|
}
|
|
1100
1271
|
} else if (data.type === 'analysis.class' || data.type === 'analysis.function' || data.type === 'analysis.method') {
|
|
1101
1272
|
// Show found elements briefly
|
|
@@ -1152,7 +1323,6 @@ class CodeTree {
|
|
|
1152
1323
|
}
|
|
1153
1324
|
|
|
1154
1325
|
// Could update a UI element here if we had an event log display
|
|
1155
|
-
console.log('[EVENT LOG]', data.type, data.message);
|
|
1156
1326
|
}
|
|
1157
1327
|
|
|
1158
1328
|
/**
|
|
@@ -1278,6 +1448,8 @@ class CodeTree {
|
|
|
1278
1448
|
findNodeByPath(path, node = null) {
|
|
1279
1449
|
if (!node) {
|
|
1280
1450
|
node = this.treeData;
|
|
1451
|
+
// Searching for node by path
|
|
1452
|
+
// Root node identified
|
|
1281
1453
|
}
|
|
1282
1454
|
|
|
1283
1455
|
if (node.path === path) {
|
|
@@ -1293,9 +1465,24 @@ class CodeTree {
|
|
|
1293
1465
|
}
|
|
1294
1466
|
}
|
|
1295
1467
|
|
|
1468
|
+
if (!node.parent) {
|
|
1469
|
+
// Node path not found in current tree structure
|
|
1470
|
+
}
|
|
1296
1471
|
return null;
|
|
1297
1472
|
}
|
|
1298
1473
|
|
|
1474
|
+
/**
|
|
1475
|
+
* Helper to log all paths in tree for debugging
|
|
1476
|
+
*/
|
|
1477
|
+
logAllPaths(node, indent = '') {
|
|
1478
|
+
console.log(`${indent}${node.path} (${node.name})`);
|
|
1479
|
+
if (node.children) {
|
|
1480
|
+
for (const child of node.children) {
|
|
1481
|
+
this.logAllPaths(child, indent + ' ');
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1299
1486
|
/**
|
|
1300
1487
|
* Find D3 hierarchy node by path
|
|
1301
1488
|
*/
|
|
@@ -1303,6 +1490,30 @@ class CodeTree {
|
|
|
1303
1490
|
if (!this.root) return null;
|
|
1304
1491
|
return this.root.descendants().find(d => d.data.path === path);
|
|
1305
1492
|
}
|
|
1493
|
+
|
|
1494
|
+
/**
|
|
1495
|
+
* Preserve expansion state when recreating hierarchy
|
|
1496
|
+
*/
|
|
1497
|
+
preserveExpansionState(oldRoot, newRoot) {
|
|
1498
|
+
if (!oldRoot || !newRoot) return;
|
|
1499
|
+
|
|
1500
|
+
// Create a map of expanded nodes from the old tree
|
|
1501
|
+
const expansionMap = new Map();
|
|
1502
|
+
oldRoot.descendants().forEach(node => {
|
|
1503
|
+
if (node.data.expanded || (node.children && !node._children)) {
|
|
1504
|
+
expansionMap.set(node.data.path, true);
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
// Apply expansion state to new tree
|
|
1509
|
+
newRoot.descendants().forEach(node => {
|
|
1510
|
+
if (expansionMap.has(node.data.path)) {
|
|
1511
|
+
node.children = node._children || node.children;
|
|
1512
|
+
node._children = null;
|
|
1513
|
+
node.data.expanded = true;
|
|
1514
|
+
}
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1306
1517
|
|
|
1307
1518
|
/**
|
|
1308
1519
|
* Update statistics display
|
|
@@ -1655,9 +1866,18 @@ class CodeTree {
|
|
|
1655
1866
|
centerOnNode(d) {
|
|
1656
1867
|
if (!this.svg || !this.zoom) return;
|
|
1657
1868
|
|
|
1658
|
-
|
|
1659
|
-
const
|
|
1660
|
-
|
|
1869
|
+
// Get current transform or use default zoom level
|
|
1870
|
+
const currentTransform = d3.zoomTransform(this.svg.node());
|
|
1871
|
+
// Zoom in to 2x for better focus on clicked node
|
|
1872
|
+
const targetScale = currentTransform.k < 2 ? 2 : currentTransform.k;
|
|
1873
|
+
|
|
1874
|
+
// Account for the initial tree group offset
|
|
1875
|
+
const initialOffsetX = this.margin.left + 100;
|
|
1876
|
+
const initialOffsetY = this.height / 2;
|
|
1877
|
+
|
|
1878
|
+
// Calculate position to center the node accounting for the tree group's transform
|
|
1879
|
+
const x = initialOffsetX - d.y * targetScale + this.width / 2;
|
|
1880
|
+
const y = initialOffsetY - d.x * targetScale + this.height / 2;
|
|
1661
1881
|
|
|
1662
1882
|
this.svg.transition()
|
|
1663
1883
|
.duration(750)
|
|
@@ -1665,7 +1885,7 @@ class CodeTree {
|
|
|
1665
1885
|
this.zoom.transform,
|
|
1666
1886
|
d3.zoomIdentity
|
|
1667
1887
|
.translate(x, y)
|
|
1668
|
-
.scale(
|
|
1888
|
+
.scale(targetScale)
|
|
1669
1889
|
);
|
|
1670
1890
|
}
|
|
1671
1891
|
|
|
@@ -1678,23 +1898,29 @@ class CodeTree {
|
|
|
1678
1898
|
// Use the same radialPoint function for consistency
|
|
1679
1899
|
const [x, y] = this.radialPoint(d.x, d.y);
|
|
1680
1900
|
|
|
1681
|
-
// Get current transform
|
|
1682
|
-
const
|
|
1901
|
+
// Get current transform or use default zoom level
|
|
1902
|
+
const currentTransform = d3.zoomTransform(this.svg.node());
|
|
1903
|
+
// Zoom in to 2x for better focus on clicked node
|
|
1904
|
+
const targetScale = currentTransform.k < 2 ? 2 : currentTransform.k;
|
|
1905
|
+
|
|
1906
|
+
// Account for the initial tree group centering
|
|
1907
|
+
const centerX = this.width / 2;
|
|
1908
|
+
const centerY = this.height / 2;
|
|
1683
1909
|
|
|
1684
1910
|
// Calculate translation to center the node
|
|
1685
|
-
// The tree is
|
|
1686
|
-
//
|
|
1687
|
-
const targetX =
|
|
1688
|
-
const targetY =
|
|
1911
|
+
// The tree group is centered at centerX, centerY
|
|
1912
|
+
// We need to offset by the node's position scaled
|
|
1913
|
+
const targetX = centerX - x * targetScale;
|
|
1914
|
+
const targetY = centerY - y * targetScale;
|
|
1689
1915
|
|
|
1690
|
-
// Apply smooth transition to center the node
|
|
1916
|
+
// Apply smooth transition to center the node with zoom
|
|
1691
1917
|
this.svg.transition()
|
|
1692
1918
|
.duration(750)
|
|
1693
1919
|
.call(
|
|
1694
1920
|
this.zoom.transform,
|
|
1695
1921
|
d3.zoomIdentity
|
|
1696
1922
|
.translate(targetX, targetY)
|
|
1697
|
-
.scale(
|
|
1923
|
+
.scale(targetScale)
|
|
1698
1924
|
);
|
|
1699
1925
|
}
|
|
1700
1926
|
|
|
@@ -1703,26 +1929,50 @@ class CodeTree {
|
|
|
1703
1929
|
*/
|
|
1704
1930
|
highlightActiveNode(d) {
|
|
1705
1931
|
// Reset all nodes to normal size and clear parent context
|
|
1706
|
-
|
|
1932
|
+
// First clear classes on the selection
|
|
1933
|
+
const allCircles = this.treeGroup.selectAll('circle.node-circle');
|
|
1934
|
+
allCircles
|
|
1935
|
+
.classed('active', false)
|
|
1936
|
+
.classed('parent-context', false);
|
|
1937
|
+
|
|
1938
|
+
// Then apply transition separately
|
|
1939
|
+
allCircles
|
|
1707
1940
|
.transition()
|
|
1708
1941
|
.duration(300)
|
|
1709
1942
|
.attr('r', 8)
|
|
1710
|
-
.classed('active', false)
|
|
1711
|
-
.classed('parent-context', false)
|
|
1712
1943
|
.style('stroke', null)
|
|
1713
1944
|
.style('stroke-width', null)
|
|
1714
1945
|
.style('opacity', null);
|
|
1715
1946
|
|
|
1947
|
+
// Reset all labels to normal
|
|
1948
|
+
this.treeGroup.selectAll('text.node-label')
|
|
1949
|
+
.style('font-weight', 'normal')
|
|
1950
|
+
.style('font-size', '12px');
|
|
1951
|
+
|
|
1716
1952
|
// Find and increase size of clicked node - use data matching
|
|
1717
|
-
|
|
1953
|
+
// Make the size increase MUCH more dramatic: 8 -> 20 (2.5x the size)
|
|
1954
|
+
const activeNodeCircle = this.treeGroup.selectAll('g.node')
|
|
1718
1955
|
.filter(node => node === d)
|
|
1719
|
-
.select('circle.node-circle')
|
|
1956
|
+
.select('circle.node-circle');
|
|
1957
|
+
|
|
1958
|
+
// First set the class (not part of transition)
|
|
1959
|
+
activeNodeCircle.classed('active', true);
|
|
1960
|
+
|
|
1961
|
+
// Then apply the transition with styles - MUCH LARGER
|
|
1962
|
+
activeNodeCircle
|
|
1720
1963
|
.transition()
|
|
1721
1964
|
.duration(300)
|
|
1722
|
-
.attr('r',
|
|
1723
|
-
.classed('active', true)
|
|
1965
|
+
.attr('r', 20) // Much larger radius (2.5x)
|
|
1724
1966
|
.style('stroke', '#3b82f6')
|
|
1725
|
-
.style('stroke-width',
|
|
1967
|
+
.style('stroke-width', 5) // Thicker border
|
|
1968
|
+
.style('filter', 'drop-shadow(0 0 15px rgba(59, 130, 246, 0.6))'); // Stronger glow effect
|
|
1969
|
+
|
|
1970
|
+
// Also make the label bold
|
|
1971
|
+
this.treeGroup.selectAll('g.node')
|
|
1972
|
+
.filter(node => node === d)
|
|
1973
|
+
.select('text.node-label')
|
|
1974
|
+
.style('font-weight', 'bold')
|
|
1975
|
+
.style('font-size', '14px'); // Slightly larger text
|
|
1726
1976
|
|
|
1727
1977
|
// Store active node
|
|
1728
1978
|
this.activeNode = d;
|
|
@@ -1740,9 +1990,9 @@ class CodeTree {
|
|
|
1740
1990
|
// Add to loading set
|
|
1741
1991
|
this.loadingNodes.add(d.data.path);
|
|
1742
1992
|
|
|
1743
|
-
// Add pulsing class and orange color
|
|
1744
|
-
node.classed('loading-pulse', true)
|
|
1745
|
-
|
|
1993
|
+
// Add pulsing class and orange color - separate operations
|
|
1994
|
+
node.classed('loading-pulse', true);
|
|
1995
|
+
node.style('fill', '#fb923c'); // Orange color for loading
|
|
1746
1996
|
|
|
1747
1997
|
// Create pulse animation
|
|
1748
1998
|
const pulseAnimation = () => {
|
|
@@ -1778,11 +2028,14 @@ class CodeTree {
|
|
|
1778
2028
|
.filter(node => node === d)
|
|
1779
2029
|
.select('circle.node-circle');
|
|
1780
2030
|
|
|
1781
|
-
|
|
1782
|
-
|
|
2031
|
+
// Clear class first
|
|
2032
|
+
node.classed('loading-pulse', false);
|
|
2033
|
+
|
|
2034
|
+
// Then interrupt and transition
|
|
2035
|
+
node.interrupt() // Stop animation
|
|
1783
2036
|
.transition()
|
|
1784
2037
|
.duration(300)
|
|
1785
|
-
.attr('r', this.activeNode === d ?
|
|
2038
|
+
.attr('r', this.activeNode === d ? 20 : 8) // Use 20 for active node
|
|
1786
2039
|
.style('opacity', 1)
|
|
1787
2040
|
.style('fill', d => this.getNodeColor(d)); // Restore original color
|
|
1788
2041
|
}
|
|
@@ -1797,9 +2050,10 @@ class CodeTree {
|
|
|
1797
2050
|
const parentNode = this.treeGroup.selectAll('g.node')
|
|
1798
2051
|
.filter(node => node === d.parent);
|
|
1799
2052
|
|
|
1800
|
-
// Highlight parent with different style
|
|
1801
|
-
parentNode.select('circle.node-circle')
|
|
1802
|
-
|
|
2053
|
+
// Highlight parent with different style - separate class from styles
|
|
2054
|
+
const parentCircle = parentNode.select('circle.node-circle');
|
|
2055
|
+
parentCircle.classed('parent-context', true);
|
|
2056
|
+
parentCircle
|
|
1803
2057
|
.style('stroke', '#10b981')
|
|
1804
2058
|
.style('stroke-width', 3)
|
|
1805
2059
|
.style('opacity', 0.8);
|
|
@@ -1847,56 +2101,146 @@ class CodeTree {
|
|
|
1847
2101
|
* Handle node click - implement lazy loading with enhanced visual feedback
|
|
1848
2102
|
*/
|
|
1849
2103
|
onNodeClick(event, d) {
|
|
1850
|
-
|
|
2104
|
+
// Handle node click interaction
|
|
1851
2105
|
|
|
1852
|
-
//
|
|
1853
|
-
if (
|
|
1854
|
-
|
|
2106
|
+
// Check event parameter
|
|
2107
|
+
if (event) {
|
|
2108
|
+
try {
|
|
2109
|
+
if (typeof event.stopPropagation === 'function') {
|
|
2110
|
+
event.stopPropagation();
|
|
2111
|
+
} else {
|
|
2112
|
+
}
|
|
2113
|
+
} catch (error) {
|
|
2114
|
+
console.error('[CodeTree] ERROR calling stopPropagation:', error);
|
|
2115
|
+
}
|
|
1855
2116
|
} else {
|
|
1856
|
-
this.centerOnNode(d);
|
|
1857
2117
|
}
|
|
1858
2118
|
|
|
1859
|
-
//
|
|
1860
|
-
|
|
2119
|
+
// Check d parameter structure
|
|
2120
|
+
if (!d) {
|
|
2121
|
+
console.error('[CodeTree] ERROR: d is null/undefined, cannot continue');
|
|
2122
|
+
return;
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
if (!d.data) {
|
|
2126
|
+
console.error('[CodeTree] ERROR: d.data is null/undefined, cannot continue');
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
// Node interaction detected
|
|
2131
|
+
|
|
2132
|
+
// === PHASE 1: Immediate Visual Effects (Synchronous) ===
|
|
2133
|
+
// These execute immediately before any async operations
|
|
2134
|
+
|
|
2135
|
+
|
|
2136
|
+
// Center on clicked node (immediate visual effect)
|
|
2137
|
+
try {
|
|
2138
|
+
if (this.isRadialLayout) {
|
|
2139
|
+
if (typeof this.centerOnNodeRadial === 'function') {
|
|
2140
|
+
this.centerOnNodeRadial(d);
|
|
2141
|
+
} else {
|
|
2142
|
+
console.error('[CodeTree] centerOnNodeRadial is not a function!');
|
|
2143
|
+
}
|
|
2144
|
+
} else {
|
|
2145
|
+
if (typeof this.centerOnNode === 'function') {
|
|
2146
|
+
this.centerOnNode(d);
|
|
2147
|
+
} else {
|
|
2148
|
+
console.error('[CodeTree] centerOnNode is not a function!');
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
} catch (error) {
|
|
2152
|
+
console.error('[CodeTree] ERROR during centering:', error, error.stack);
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
|
|
2156
|
+
// Highlight with larger icon (immediate visual effect)
|
|
2157
|
+
try {
|
|
2158
|
+
if (typeof this.highlightActiveNode === 'function') {
|
|
2159
|
+
this.highlightActiveNode(d);
|
|
2160
|
+
} else {
|
|
2161
|
+
console.error('[CodeTree] highlightActiveNode is not a function!');
|
|
2162
|
+
}
|
|
2163
|
+
} catch (error) {
|
|
2164
|
+
console.error('[CodeTree] ERROR during highlightActiveNode:', error, error.stack);
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
|
|
2168
|
+
// Show parent context (immediate visual effect)
|
|
2169
|
+
try {
|
|
2170
|
+
if (typeof this.showWithParent === 'function') {
|
|
2171
|
+
this.showWithParent(d);
|
|
2172
|
+
} else {
|
|
2173
|
+
console.error('[CodeTree] showWithParent is not a function!');
|
|
2174
|
+
}
|
|
2175
|
+
} catch (error) {
|
|
2176
|
+
console.error('[CodeTree] ERROR during showWithParent:', error, error.stack);
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
|
|
2180
|
+
// Add pulsing animation immediately for directories
|
|
2181
|
+
|
|
2182
|
+
if (d.data.type === 'directory' && !d.data.loaded) {
|
|
2183
|
+
try {
|
|
2184
|
+
if (typeof this.addLoadingPulse === 'function') {
|
|
2185
|
+
this.addLoadingPulse(d);
|
|
2186
|
+
} else {
|
|
2187
|
+
console.error('[CodeTree] addLoadingPulse is not a function!');
|
|
2188
|
+
}
|
|
2189
|
+
} catch (error) {
|
|
2190
|
+
console.error('[CodeTree] ERROR during addLoadingPulse:', error, error.stack);
|
|
2191
|
+
}
|
|
2192
|
+
} else {
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
|
|
2196
|
+
// === PHASE 2: Prepare Data (Synchronous) ===
|
|
1861
2197
|
|
|
1862
|
-
// Show parent context
|
|
1863
|
-
this.showWithParent(d);
|
|
1864
2198
|
|
|
1865
2199
|
// Get selected languages from checkboxes
|
|
1866
2200
|
const selectedLanguages = [];
|
|
1867
|
-
document.querySelectorAll('.language-checkbox:checked')
|
|
2201
|
+
const checkboxes = document.querySelectorAll('.language-checkbox:checked');
|
|
2202
|
+
checkboxes.forEach(cb => {
|
|
1868
2203
|
selectedLanguages.push(cb.value);
|
|
1869
2204
|
});
|
|
1870
2205
|
|
|
1871
2206
|
// Get ignore patterns
|
|
1872
|
-
const
|
|
2207
|
+
const ignorePatternsElement = document.getElementById('ignore-patterns');
|
|
2208
|
+
const ignorePatterns = ignorePatternsElement?.value || '';
|
|
1873
2209
|
|
|
1874
|
-
|
|
1875
|
-
|
|
2210
|
+
|
|
2211
|
+
// === PHASE 3: Async Operations (Delayed) ===
|
|
2212
|
+
// Add a small delay to ensure visual effects are rendered first
|
|
1876
2213
|
|
|
1877
2214
|
// For directories that haven't been loaded yet, request discovery
|
|
1878
2215
|
if (d.data.type === 'directory' && !d.data.loaded) {
|
|
1879
|
-
//
|
|
1880
|
-
|
|
2216
|
+
// Mark as loading immediately to prevent duplicate requests
|
|
2217
|
+
d.data.loaded = 'loading';
|
|
1881
2218
|
|
|
1882
2219
|
// Ensure path is absolute or relative to working directory
|
|
1883
2220
|
const fullPath = this.ensureFullPath(d.data.path);
|
|
1884
2221
|
|
|
1885
|
-
//
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
show_hidden_files: showHiddenFiles
|
|
1893
|
-
});
|
|
2222
|
+
// Sending discovery request for child content
|
|
2223
|
+
|
|
2224
|
+
// Store reference to the D3 node for later expansion
|
|
2225
|
+
const clickedD3Node = d;
|
|
2226
|
+
|
|
2227
|
+
// Delay the socket request to ensure visual effects are rendered
|
|
2228
|
+
setTimeout(() => {
|
|
1894
2229
|
|
|
1895
|
-
//
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
2230
|
+
// Request directory contents via Socket.IO
|
|
2231
|
+
if (this.socket) {
|
|
2232
|
+
this.socket.emit('code:discover:directory', {
|
|
2233
|
+
path: fullPath,
|
|
2234
|
+
depth: 1, // Only get immediate children
|
|
2235
|
+
languages: selectedLanguages,
|
|
2236
|
+
ignore_patterns: ignorePatterns
|
|
2237
|
+
});
|
|
2238
|
+
|
|
2239
|
+
this.updateBreadcrumb(`Loading ${d.data.name}...`, 'info');
|
|
2240
|
+
this.showNotification(`Loading directory: ${d.data.name}`, 'info');
|
|
2241
|
+
} else {
|
|
2242
|
+
}
|
|
2243
|
+
}, 100); // 100ms delay to ensure visual effects render first
|
|
1900
2244
|
}
|
|
1901
2245
|
// For files that haven't been analyzed, request analysis
|
|
1902
2246
|
else if (d.data.type === 'file' && !d.data.analyzed) {
|
|
@@ -1907,28 +2251,54 @@ class CodeTree {
|
|
|
1907
2251
|
return;
|
|
1908
2252
|
}
|
|
1909
2253
|
|
|
1910
|
-
// Add pulsing animation
|
|
2254
|
+
// Add pulsing animation immediately
|
|
1911
2255
|
this.addLoadingPulse(d);
|
|
1912
2256
|
|
|
2257
|
+
// Mark as loading immediately
|
|
2258
|
+
d.data.analyzed = 'loading';
|
|
2259
|
+
|
|
1913
2260
|
// Ensure path is absolute or relative to working directory
|
|
1914
2261
|
const fullPath = this.ensureFullPath(d.data.path);
|
|
1915
2262
|
|
|
1916
|
-
//
|
|
1917
|
-
|
|
1918
|
-
const showHiddenFiles = showHiddenFilesCheckbox ? showHiddenFilesCheckbox.checked : false;
|
|
1919
|
-
|
|
1920
|
-
if (this.socket) {
|
|
1921
|
-
this.socket.emit('code:analyze:file', {
|
|
1922
|
-
path: fullPath,
|
|
1923
|
-
show_hidden_files: showHiddenFiles
|
|
1924
|
-
});
|
|
2263
|
+
// Delay the socket request to ensure visual effects are rendered
|
|
2264
|
+
setTimeout(() => {
|
|
1925
2265
|
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
2266
|
+
if (this.socket) {
|
|
2267
|
+
this.socket.emit('code:analyze:file', {
|
|
2268
|
+
path: fullPath
|
|
2269
|
+
});
|
|
2270
|
+
|
|
2271
|
+
this.updateBreadcrumb(`Analyzing ${d.data.name}...`, 'info');
|
|
2272
|
+
this.showNotification(`Analyzing: ${d.data.name}`, 'info');
|
|
2273
|
+
}
|
|
2274
|
+
}, 100); // 100ms delay to ensure visual effects render first
|
|
1930
2275
|
}
|
|
1931
2276
|
// Toggle children visibility for already loaded nodes
|
|
2277
|
+
else if (d.data.type === 'directory' && d.data.loaded === true) {
|
|
2278
|
+
// Directory is loaded, toggle expansion
|
|
2279
|
+
if (d.children) {
|
|
2280
|
+
// Collapse - hide children
|
|
2281
|
+
d._children = d.children;
|
|
2282
|
+
d.children = null;
|
|
2283
|
+
d.data.expanded = false;
|
|
2284
|
+
} else if (d._children) {
|
|
2285
|
+
// Expand - show children
|
|
2286
|
+
d.children = d._children;
|
|
2287
|
+
d._children = null;
|
|
2288
|
+
d.data.expanded = true;
|
|
2289
|
+
} else if (d.data.children && d.data.children.length > 0) {
|
|
2290
|
+
// Children exist in data but not in D3 node, recreate hierarchy
|
|
2291
|
+
this.root = d3.hierarchy(this.treeData);
|
|
2292
|
+
const updatedD3Node = this.findD3NodeByPath(d.data.path);
|
|
2293
|
+
if (updatedD3Node) {
|
|
2294
|
+
updatedD3Node.children = updatedD3Node._children || updatedD3Node.children;
|
|
2295
|
+
updatedD3Node._children = null;
|
|
2296
|
+
updatedD3Node.data.expanded = true;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
this.update(this.root);
|
|
2300
|
+
}
|
|
2301
|
+
// Also handle other nodes that might have children
|
|
1932
2302
|
else if (d.children || d._children) {
|
|
1933
2303
|
if (d.children) {
|
|
1934
2304
|
d._children = d.children;
|
|
@@ -1940,37 +2310,58 @@ class CodeTree {
|
|
|
1940
2310
|
d.data.expanded = true;
|
|
1941
2311
|
}
|
|
1942
2312
|
this.update(d);
|
|
2313
|
+
} else {
|
|
1943
2314
|
}
|
|
1944
2315
|
|
|
1945
2316
|
// Update selection
|
|
1946
2317
|
this.selectedNode = d;
|
|
1947
|
-
|
|
2318
|
+
try {
|
|
2319
|
+
this.highlightNode(d);
|
|
2320
|
+
} catch (error) {
|
|
2321
|
+
console.error('[CodeTree] ERROR during highlightNode:', error);
|
|
2322
|
+
}
|
|
2323
|
+
|
|
1948
2324
|
}
|
|
1949
2325
|
|
|
1950
2326
|
/**
|
|
1951
2327
|
* Ensure path is absolute or relative to working directory
|
|
1952
2328
|
*/
|
|
1953
2329
|
ensureFullPath(path) {
|
|
2330
|
+
console.log('🔗 ensureFullPath called with:', path);
|
|
2331
|
+
|
|
1954
2332
|
if (!path) return path;
|
|
1955
2333
|
|
|
1956
2334
|
// If already absolute, return as is
|
|
1957
2335
|
if (path.startsWith('/')) {
|
|
2336
|
+
console.log(' → Already absolute, returning:', path);
|
|
1958
2337
|
return path;
|
|
1959
2338
|
}
|
|
1960
2339
|
|
|
1961
2340
|
// Get working directory
|
|
1962
2341
|
const workingDir = this.getWorkingDirectory();
|
|
2342
|
+
console.log(' → Working directory:', workingDir);
|
|
2343
|
+
|
|
1963
2344
|
if (!workingDir) {
|
|
2345
|
+
console.log(' → No working directory, returning original:', path);
|
|
1964
2346
|
return path;
|
|
1965
2347
|
}
|
|
1966
2348
|
|
|
1967
|
-
//
|
|
1968
|
-
if (path === '.'
|
|
2349
|
+
// Special handling for root path
|
|
2350
|
+
if (path === '.') {
|
|
2351
|
+
console.log(' → Root path detected, returning working dir:', workingDir);
|
|
2352
|
+
return workingDir;
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
// If path equals working directory, return as is
|
|
2356
|
+
if (path === workingDir) {
|
|
2357
|
+
console.log(' → Path equals working directory, returning:', workingDir);
|
|
1969
2358
|
return workingDir;
|
|
1970
2359
|
}
|
|
1971
2360
|
|
|
1972
2361
|
// Combine working directory with relative path
|
|
1973
|
-
|
|
2362
|
+
const result = `${workingDir}/${path}`.replace(/\/+/g, '/');
|
|
2363
|
+
console.log(' → Combining with working dir, result:', result);
|
|
2364
|
+
return result;
|
|
1974
2365
|
}
|
|
1975
2366
|
|
|
1976
2367
|
/**
|
|
@@ -2532,4 +2923,4 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2532
2923
|
}
|
|
2533
2924
|
});
|
|
2534
2925
|
}
|
|
2535
|
-
})
|
|
2926
|
+
});/* Cache buster: 1756393851 */
|