@@ -10,15 +10,106 @@ import './components.css';
1010cytoscape . use ( dagre ) ;
1111cytoscape . use ( fcose ) ;
1212
13+ /**
14+ * Get Cytoscape styles based on theme
15+ * @param {boolean } isDark - Whether to use dark theme
16+ * @returns {Array } Cytoscape style array
17+ */
18+ function getCytoscapeStyles ( isDark ) {
19+ return [
20+ // Regular module nodes - rectangle
21+ {
22+ selector : 'node[type="file"]' ,
23+ style : {
24+ 'shape' : 'roundrectangle' ,
25+ 'background-color' : isDark ? '#1f2937' : '#ffffff' ,
26+ 'label' : 'data(label)' ,
27+ 'text-valign' : 'center' ,
28+ 'text-halign' : 'center' ,
29+ 'font-size' : '11px' ,
30+ 'font-family' : 'Inter, sans-serif' ,
31+ 'color' : isDark ? '#e5e7eb' : '#374151' ,
32+ 'width' : 80 ,
33+ 'height' : 40 ,
34+ 'text-wrap' : 'wrap' ,
35+ 'text-max-width' : 70 ,
36+ 'border-width' : 2 ,
37+ 'border-color' : isDark ? '#4b5563' : '#374151' ,
38+ 'corner-radius' : 4 ,
39+ } ,
40+ } ,
41+ // Circular dependency nodes - diamond
42+ {
43+ selector : 'node[type="circular"]' ,
44+ style : {
45+ 'shape' : 'diamond' ,
46+ 'background-color' : isDark ? '#7f1d1d' : '#fecaca' ,
47+ 'label' : 'data(label)' ,
48+ 'text-valign' : 'center' ,
49+ 'text-halign' : 'center' ,
50+ 'font-size' : '11px' ,
51+ 'font-family' : 'Inter, sans-serif' ,
52+ 'color' : isDark ? '#fca5a5' : '#991b1b' ,
53+ 'width' : 70 ,
54+ 'height' : 70 ,
55+ 'text-wrap' : 'wrap' ,
56+ 'text-max-width' : 60 ,
57+ 'border-width' : 2 ,
58+ 'border-color' : '#dc2626' ,
59+ } ,
60+ } ,
61+ // Selected state
62+ {
63+ selector : 'node:selected' ,
64+ style : {
65+ 'border-width' : 3 ,
66+ 'border-color' : isDark ? '#60a5fa' : '#2563eb' ,
67+ } ,
68+ } ,
69+ // Regular import edges - solid line
70+ {
71+ selector : 'edge[type="import"]' ,
72+ style : {
73+ 'width' : 1.5 ,
74+ 'line-color' : isDark ? '#6b7280' : '#9ca3af' ,
75+ 'target-arrow-color' : isDark ? '#6b7280' : '#9ca3af' ,
76+ 'target-arrow-shape' : 'triangle' ,
77+ 'curve-style' : 'bezier' ,
78+ 'arrow-scale' : 0.6 ,
79+ 'line-style' : 'solid' ,
80+ } ,
81+ } ,
82+ // Circular dependency edges - dashed line
83+ {
84+ selector : 'edge[type="circular"]' ,
85+ style : {
86+ 'width' : 2 ,
87+ 'line-color' : '#dc2626' ,
88+ 'target-arrow-color' : '#dc2626' ,
89+ 'target-arrow-shape' : 'triangle' ,
90+ 'curve-style' : 'bezier' ,
91+ 'arrow-scale' : 0.7 ,
92+ 'line-style' : 'dashed' ,
93+ } ,
94+ } ,
95+ ] ;
96+ }
97+
1398/**
1499 * Project Dependency Visualization Component
15100 * Uses Cytoscape.js to display project file dependency graphs
16101 */
17102function ProjectVisualization ( { projectResult, theme, viewMode = '2d' } ) {
18103 const containerRef = useRef ( null ) ;
19104 const cyRef = useRef ( null ) ;
105+ const themeRef = useRef ( theme ) ;
20106 const [ selectedNode , setSelectedNode ] = useState ( null ) ;
21107
108+ // Keep themeRef in sync with theme prop
109+ useEffect ( ( ) => {
110+ themeRef . current = theme ;
111+ } , [ theme ] ) ;
112+
22113 // Process dependency data into graph data
23114 const graphData = useMemo ( ( ) => {
24115 if ( ! projectResult ) return { nodes : [ ] , edges : [ ] } ;
@@ -108,7 +199,7 @@ function ProjectVisualization({ projectResult, theme, viewMode = '2d' }) {
108199 container : container ,
109200 elements : [ ...graphData . nodes , ...graphData . edges ] ,
110201
111- style : getCytoscapeStyles ( theme ) ,
202+ style : getCytoscapeStyles ( themeRef . current === 'dark' ) ,
112203
113204 layout : {
114205 name : 'fcose' ,
@@ -144,95 +235,14 @@ function ProjectVisualization({ projectResult, theme, viewMode = '2d' }) {
144235 logger . error ( 'Cytoscape initialization error' , { error : error . message } ) ;
145236 return false ;
146237 }
147- } , [ graphData ] ) ; // Remove theme dependency to avoid rebuilding on theme change
148-
149- // Get Cytoscape styles based on theme
150- const getCytoscapeStyles = useCallback ( ( isDark ) => {
151- return [
152- // Regular module nodes - rectangle
153- {
154- selector : 'node[type="file"]' ,
155- style : {
156- 'shape' : 'roundrectangle' ,
157- 'background-color' : isDark ? '#1f2937' : '#ffffff' ,
158- 'label' : 'data(label)' ,
159- 'text-valign' : 'center' ,
160- 'text-halign' : 'center' ,
161- 'font-size' : '11px' ,
162- 'font-family' : 'Inter, sans-serif' ,
163- 'color' : isDark ? '#e5e7eb' : '#374151' ,
164- 'width' : 80 ,
165- 'height' : 40 ,
166- 'text-wrap' : 'wrap' ,
167- 'text-max-width' : 70 ,
168- 'border-width' : 2 ,
169- 'border-color' : isDark ? '#4b5563' : '#374151' ,
170- 'corner-radius' : 4 ,
171- } ,
172- } ,
173- // Circular dependency nodes - diamond
174- {
175- selector : 'node[type="circular"]' ,
176- style : {
177- 'shape' : 'diamond' ,
178- 'background-color' : isDark ? '#7f1d1d' : '#fecaca' ,
179- 'label' : 'data(label)' ,
180- 'text-valign' : 'center' ,
181- 'text-halign' : 'center' ,
182- 'font-size' : '11px' ,
183- 'font-family' : 'Inter, sans-serif' ,
184- 'color' : isDark ? '#fca5a5' : '#991b1b' ,
185- 'width' : 70 ,
186- 'height' : 70 ,
187- 'text-wrap' : 'wrap' ,
188- 'text-max-width' : 60 ,
189- 'border-width' : 2 ,
190- 'border-color' : '#dc2626' ,
191- } ,
192- } ,
193- // Selected state
194- {
195- selector : 'node:selected' ,
196- style : {
197- 'border-width' : 3 ,
198- 'border-color' : isDark ? '#60a5fa' : '#2563eb' ,
199- } ,
200- } ,
201- // Regular import edges - solid line
202- {
203- selector : 'edge[type="import"]' ,
204- style : {
205- 'width' : 1.5 ,
206- 'line-color' : isDark ? '#6b7280' : '#9ca3af' ,
207- 'target-arrow-color' : isDark ? '#6b7280' : '#9ca3af' ,
208- 'target-arrow-shape' : 'triangle' ,
209- 'curve-style' : 'bezier' ,
210- 'arrow-scale' : 0.6 ,
211- 'line-style' : 'solid' ,
212- } ,
213- } ,
214- // Circular dependency edges - dashed line
215- {
216- selector : 'edge[type="circular"]' ,
217- style : {
218- 'width' : 2 ,
219- 'line-color' : '#dc2626' ,
220- 'target-arrow-color' : '#dc2626' ,
221- 'target-arrow-shape' : 'triangle' ,
222- 'curve-style' : 'bezier' ,
223- 'arrow-scale' : 0.7 ,
224- 'line-style' : 'dashed' ,
225- } ,
226- } ,
227- ] ;
228- } , [ ] ) ;
238+ } , [ graphData ] ) ;
229239
230240 // Update styles when theme changes (without rebuilding the entire graph)
231241 useEffect ( ( ) => {
232242 if ( cyRef . current ) {
233243 cyRef . current . style ( getCytoscapeStyles ( theme === 'dark' ) ) ;
234244 }
235- } , [ theme , getCytoscapeStyles ] ) ;
245+ } , [ theme ] ) ;
236246
237247 // Delayed initialization to ensure container dimensions are correct
238248 useEffect ( ( ) => {
0 commit comments