This commit is contained in:
abhigyantrumio 2025-09-25 19:05:45 +05:30
parent a5086bd1fe
commit 4d5f37fbc7

View file

@ -12,8 +12,6 @@ interface GraphExplorerProps {
export default function GraphExplorer({ graph, isLoading, onNodeSelect, fileContents }: GraphExplorerProps) {
const [selectedNode, setSelectedNode] = useState<string | null>(null);
const [hideExternalNodes, setHideExternalNodes] = useState(false);
const [hideAllIsolatedNodes, setHideAllIsolatedNodes] = useState(false);
const [floatingViewerOpen, setFloatingViewerOpen] = useState(false);
const [floatingViewerPosition, setFloatingViewerPosition] = useState({ x: 0, y: 0 });
const [floatingViewerPinned, setFloatingViewerPinned] = useState(false);
@ -45,126 +43,6 @@ export default function GraphExplorer({ graph, isLoading, onNodeSelect, fileCont
}
};
// Filter out isolated external nodes if toggle is enabled
const filteredGraph = useMemo(() => {
if (!graph || (!hideExternalNodes && !hideAllIsolatedNodes)) return graph;
// Find nodes that have no relationships (isolated nodes)
const connectedNodeIds = new Set<string>();
graph.relationships.forEach(rel => {
connectedNodeIds.add(rel.source);
connectedNodeIds.add(rel.target);
});
// Get all isolated nodes
const isolatedNodes = graph.nodes.filter(node => !connectedNodeIds.has(node.id));
// Debug: Log what we're filtering
console.log('🔍 GraphExplorer Filtering Debug:', {
totalNodes: graph.nodes.length,
connectedNodes: connectedNodeIds.size,
isolatedNodes: isolatedNodes.length,
hideExternal: hideExternalNodes,
hideAll: hideAllIsolatedNodes,
allNodeTypes: [...new Set(graph.nodes.map(n => n.label))],
isolatedNodeDetails: isolatedNodes.map(n => ({
id: n.id,
label: n.label,
name: n.properties.name || n.properties.functionName || 'unnamed',
allProperties: Object.keys(n.properties)
}))
});
let nodesToHide: typeof graph.nodes = [];
if (hideAllIsolatedNodes) {
// Hide ALL isolated nodes
nodesToHide = isolatedNodes;
console.log('🔍 Hiding ALL isolated nodes:', nodesToHide.length);
} else if (hideExternalNodes) {
// Hide external library nodes (regardless of whether they're isolated)
nodesToHide = graph.nodes.filter(node => {
const nodeName = (node.properties.name || node.properties.functionName || '') as string;
console.log('🔍 Checking node for external patterns:', {
id: node.id,
label: node.label,
name: nodeName,
allProps: node.properties
});
// Check for external library patterns
const isExternalLibrary = (
// Specific external library names
nodeName.includes('when_all') ||
nodeName.includes('ruleset') ||
nodeName.includes('APIRouter') ||
nodeName.includes('StateGraph') ||
nodeName.includes('AsyncAzureOpenAI') ||
nodeName.includes('FastAPI') ||
nodeName.includes('HTTPException') ||
nodeName.includes('assert_fact') ||
nodeName.includes('get_facts') ||
nodeName.includes('delete_state') ||
nodeName.includes('anyItem') ||
nodeName.includes('allItems') ||
nodeName.includes('matches') ||
// Common external patterns
/^[A-Z][a-zA-Z]*Model$/.test(nodeName) || // Models ending in 'Model'
/^[A-Z][a-zA-Z]*Client$/.test(nodeName) || // Clients
/^[A-Z][a-zA-Z]*Router$/.test(nodeName) || // Routers
/^[A-Z][a-zA-Z]*Exception$/.test(nodeName) || // Exceptions
// CamelCase starting with uppercase (often external classes)
(/^[A-Z][a-zA-Z]*$/.test(nodeName) && nodeName.length > 3)
);
// Check for decorators and functions that are likely external
const isLikelyExternal = (
(node.label === 'Decorator' || node.label === 'Function') &&
isExternalLibrary
);
console.log('🔍 External check result:', {
name: nodeName,
label: node.label,
isExternalLibrary,
isLikelyExternal
});
return isLikelyExternal;
});
console.log('🔍 Hiding external library nodes:', nodesToHide.length);
}
console.log('🔍 Final nodes to hide:', nodesToHide.map(n => ({
id: n.id,
label: n.label,
name: n.properties.name || n.properties.functionName || 'unnamed'
})));
const nodeIdsToHide = new Set(nodesToHide.map(n => n.id));
// Filter out the nodes we want to hide
const filteredNodes = graph.nodes.filter(node => !nodeIdsToHide.has(node.id));
// Filter out relationships involving hidden nodes
const filteredRelationships = graph.relationships.filter(rel =>
!nodeIdsToHide.has(rel.source) && !nodeIdsToHide.has(rel.target)
);
console.log('🔍 Filtering result:', {
originalNodes: graph.nodes.length,
filteredNodes: filteredNodes.length,
hiddenNodes: nodeIdsToHide.size,
originalRels: graph.relationships.length,
filteredRels: filteredRelationships.length
});
return {
nodes: filteredNodes,
relationships: filteredRelationships
};
}, [graph, hideExternalNodes, hideAllIsolatedNodes]);
const containerStyle: React.CSSProperties = {
width: '100%',
@ -174,15 +52,6 @@ export default function GraphExplorer({ graph, isLoading, onNodeSelect, fileCont
overflow: 'hidden'
};
const controlsStyle: React.CSSProperties = {
padding: '8px 12px',
borderBottom: '1px solid #e0e0e0',
backgroundColor: '#f8f9fa',
display: 'flex',
alignItems: 'center',
gap: '12px',
fontSize: '14px'
};
const loadingStyle: React.CSSProperties = {
display: 'flex',
@ -206,84 +75,12 @@ export default function GraphExplorer({ graph, isLoading, onNodeSelect, fileCont
return <div style={containerStyle}><div style={loadingStyle}>No graph data available</div></div>;
}
// Count isolated nodes for display
const connectedNodeIds = new Set<string>();
graph.relationships.forEach(rel => {
connectedNodeIds.add(rel.source);
connectedNodeIds.add(rel.target);
});
const isolatedCount = graph.nodes.filter(node => !connectedNodeIds.has(node.id)).length;
// Count external nodes (not just isolated ones)
const externalCount = useMemo(() => {
if (!graph) return 0;
return graph.nodes.filter(node => {
const nodeName = (node.properties.name || node.properties.functionName || '') as string;
const isExternalLibrary = (
nodeName.includes('when_all') ||
nodeName.includes('ruleset') ||
nodeName.includes('APIRouter') ||
nodeName.includes('StateGraph') ||
nodeName.includes('AsyncAzureOpenAI') ||
nodeName.includes('FastAPI') ||
nodeName.includes('HTTPException') ||
nodeName.includes('assert_fact') ||
nodeName.includes('get_facts') ||
nodeName.includes('delete_state') ||
nodeName.includes('anyItem') ||
nodeName.includes('allItems') ||
nodeName.includes('matches') ||
/^[A-Z][a-zA-Z]*Model$/.test(nodeName) ||
/^[A-Z][a-zA-Z]*Client$/.test(nodeName) ||
/^[A-Z][a-zA-Z]*Router$/.test(nodeName) ||
/^[A-Z][a-zA-Z]*Exception$/.test(nodeName) ||
(/^[A-Z][a-zA-Z]*$/.test(nodeName) && nodeName.length > 3)
);
return (node.label === 'Decorator' || node.label === 'Function') && isExternalLibrary;
}).length;
}, [graph]);
// Count what's actually being hidden
const hiddenCount = useMemo(() => {
if (!graph) return 0;
if (hideAllIsolatedNodes) {
return isolatedCount;
} else if (hideExternalNodes) {
return externalCount;
}
return 0;
}, [graph, hideExternalNodes, hideAllIsolatedNodes, isolatedCount, externalCount]);
return (
<div style={containerStyle} ref={containerRef}>
<div style={controlsStyle}>
<label style={{ display: 'flex', alignItems: 'center', gap: '6px', cursor: 'pointer' }}>
<input
type="checkbox"
checked={hideExternalNodes}
onChange={(e) => setHideExternalNodes(e.target.checked)}
style={{ cursor: 'pointer' }}
/>
<span>Hide external libraries</span>
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '6px', cursor: 'pointer' }}>
<input
type="checkbox"
checked={hideAllIsolatedNodes}
onChange={(e) => setHideAllIsolatedNodes(e.target.checked)}
style={{ cursor: 'pointer' }}
/>
<span>Hide all isolated nodes</span>
</label>
<span style={{ color: '#666', fontSize: '12px' }}>
({isolatedCount} isolated, {externalCount} external, {hiddenCount} hidden)
</span>
</div>
<div style={graphContainerStyle}>
<GraphVisualization
graph={filteredGraph || graph}
graph={graph}
onNodeSelect={handleNodeSelect}
selectedNodeId={selectedNode}
/>