@@ -12,6 +12,7 @@ const LANG_COLORS: Record<string, string> = {
1212 yaml : '#cb171e' , json : '#555' , ruby : '#701516' , scala : '#c22d40' ,
1313 cpp : '#f34b7d' , shell : '#89e051' , markdown : '#083fa1' , html : '#e34c26' ,
1414 css : '#563d7c' , sql : '#e38c00' , proto : '#60a0b0' , dockerfile : '#384d54' ,
15+ other : '#888' ,
1516} ;
1617
1718const EXT_TO_LANG : Record < string , string > = {
@@ -25,7 +26,7 @@ const EXT_TO_LANG: Record<string, string> = {
2526
2627interface EChartsTreeNode {
2728 name : string ;
28- value : number ;
29+ value ? : number ;
2930 children ?: EChartsTreeNode [ ] ;
3031 itemStyle ?: { color : string } ;
3132}
@@ -35,40 +36,47 @@ function inferLang(name: string): string {
3536 return EXT_TO_LANG [ ext ] ?? 'other' ;
3637}
3738
38- function fileTreeToECharts ( nodes : FileTreeNode [ ] ) : EChartsTreeNode [ ] {
39- return nodes
40- . filter ( n => n . nodeCount > 0 || ( n . children && n . children . length > 0 ) )
41- . map ( n => {
42- if ( n . type === 'directory' && n . children && n . children . length > 0 ) {
43- const children = fileTreeToECharts ( n . children ) ;
44- // Dominant language from children names
45- const langCounts : Record < string , number > = { } ;
46- function countLangs ( items : FileTreeNode [ ] ) {
47- for ( const item of items ) {
48- if ( item . type === 'file' ) {
49- const lang = inferLang ( item . name ) ;
50- langCounts [ lang ] = ( langCounts [ lang ] ?? 0 ) + ( item . nodeCount || 1 ) ;
51- }
52- if ( item . children ) countLangs ( item . children ) ;
53- }
54- }
55- countLangs ( n . children ) ;
56- const dominant = Object . entries ( langCounts ) . sort ( ( a , b ) => b [ 1 ] - a [ 1 ] ) [ 0 ] ?. [ 0 ] ?? 'other' ;
57-
58- return {
59- name : n . name ,
60- value : n . nodeCount ,
61- children : children . length > 0 ? children : undefined ,
62- itemStyle : { color : LANG_COLORS [ dominant ] ?? '#666' } ,
63- } ;
39+ function dominantLang ( nodes : FileTreeNode [ ] ) : string {
40+ const counts : Record < string , number > = { } ;
41+ function walk ( items : FileTreeNode [ ] ) {
42+ for ( const item of items ) {
43+ if ( item . type === 'file' ) {
44+ const lang = inferLang ( item . name ) ;
45+ counts [ lang ] = ( counts [ lang ] ?? 0 ) + ( item . nodeCount || 1 ) ;
6446 }
47+ if ( item . children ) walk ( item . children ) ;
48+ }
49+ }
50+ walk ( nodes ) ;
51+ return Object . entries ( counts ) . sort ( ( a , b ) => b [ 1 ] - a [ 1 ] ) [ 0 ] ?. [ 0 ] ?? 'other' ;
52+ }
53+
54+ function fileTreeToECharts ( nodes : FileTreeNode [ ] ) : EChartsTreeNode [ ] {
55+ const result : EChartsTreeNode [ ] = [ ] ;
56+ for ( const n of nodes ) {
57+ if ( n . nodeCount <= 0 && ( ! n . children || n . children . length === 0 ) ) continue ;
58+
59+ if ( n . type === 'directory' && n . children && n . children . length > 0 ) {
60+ const children = fileTreeToECharts ( n . children ) ;
61+ if ( children . length === 0 ) continue ;
62+ const lang = dominantLang ( n . children ) ;
63+ // Directory nodes: NO value — ECharts sums from children for correct proportions
64+ result . push ( {
65+ name : n . name ,
66+ children,
67+ itemStyle : { color : LANG_COLORS [ lang ] ?? '#666' } ,
68+ } ) ;
69+ } else {
70+ // Leaf file node: value = nodeCount (determines rectangle size)
6571 const lang = inferLang ( n . name ) ;
66- return {
72+ result . push ( {
6773 name : n . name ,
6874 value : Math . max ( n . nodeCount , 1 ) ,
6975 itemStyle : { color : LANG_COLORS [ lang ] ?? '#666' } ,
70- } ;
71- } ) ;
76+ } ) ;
77+ }
78+ }
79+ return result ;
7280}
7381
7482function collectLanguages ( nodes : FileTreeNode [ ] ) : string [ ] {
@@ -97,68 +105,78 @@ export default function CodebaseMap() {
97105 const tree = treeData ?. tree ?? [ ] ;
98106 const totalFiles = treeData ?. total_files ?? 0 ;
99107 const uniqueLangs = useMemo ( ( ) => collectLanguages ( tree ) , [ tree ] ) ;
100-
101- const treemapData = useMemo ( ( ) => {
102- // TODO: apply langFilter if needed
103- return fileTreeToECharts ( tree ) ;
104- } , [ tree , langFilter ] ) ;
108+ const treemapData = useMemo ( ( ) => fileTreeToECharts ( tree ) , [ tree ] ) ;
105109
106110 const chartOption = useMemo ( ( ) => ( {
107111 tooltip : {
108112 formatter : ( info : { name : string ; value : number ; treePathInfo ?: Array < { name : string } > } ) => {
109113 const path = info . treePathInfo ?. map ( p => p . name ) . filter ( Boolean ) . join ( '/' ) ?? info . name ;
110- return `<b>${ path } </b><br/>Nodes: ${ info . value ? .toLocaleString ( ) } ` ;
114+ return `<b>${ path } </b><br/>Nodes: ${ ( info . value ?? 0 ) . toLocaleString ( ) } ` ;
111115 } ,
112116 } ,
113117 series : [ {
114118 type : 'treemap' ,
115119 data : treemapData ,
116- roam : 'move' ,
117- leafDepth : 2 ,
118- drillDownIcon : '▶' ,
120+ leafDepth : 1 ,
121+ drillDownIcon : '▶ ' ,
122+ roam : false ,
123+ nodeClick : 'zoomToNode' ,
119124 breadcrumb : {
120125 show : true ,
121126 top : 4 ,
122127 left : 4 ,
123128 itemStyle : {
124- color : isDark ? '#2a2a2e' : '#f5f5f5' ,
125- borderColor : isDark ? '#3f3f46' : '#d9d9d9' ,
129+ color : isDark ? '#1a1a1a' : '#f5f5f5' ,
130+ borderColor : isDark ? '#303030' : '#d9d9d9' ,
131+ } ,
132+ textStyle : {
133+ color : isDark ? '#e0e0e0' : '#333' ,
134+ fontSize : 13 ,
126135 } ,
127- textStyle : { color : isDark ? '#e0e0e0' : '#333' } ,
128136 } ,
129137 levels : [
130138 {
131139 itemStyle : {
132- borderColor : isDark ? '#444 ' : '#ccc ' ,
133- borderWidth : 2 ,
134- gapWidth : 2 ,
140+ borderColor : isDark ? '#303030 ' : '#bbb ' ,
141+ borderWidth : 3 ,
142+ gapWidth : 3 ,
135143 } ,
136144 upperLabel : {
137145 show : true ,
138- height : 28 ,
146+ height : 30 ,
139147 color : isDark ? '#e0e0e0' : '#333' ,
140- fontSize : 13 ,
148+ fontSize : 14 ,
141149 fontWeight : 'bold' as const ,
142150 } ,
143151 } ,
144152 {
145153 itemStyle : {
146- borderColor : isDark ? '#555' : '#ddd' ,
147- borderWidth : 1 ,
148- gapWidth : 1 ,
154+ borderColor : isDark ? '#404040' : '#ccc' ,
155+ borderWidth : 2 ,
156+ gapWidth : 2 ,
157+ } ,
158+ upperLabel : {
159+ show : true ,
160+ height : 24 ,
161+ fontSize : 12 ,
162+ color : isDark ? '#ccc' : '#555' ,
149163 } ,
150- upperLabel : { show : true , height : 22 , fontSize : 12 } ,
151164 } ,
152165 {
153166 itemStyle : {
154- borderColor : isDark ? '#666 ' : '#eee ' ,
155- borderWidth : 0.5 ,
156- gapWidth : 0.5 ,
167+ borderColor : isDark ? '#4a4a4a ' : '#ddd ' ,
168+ borderWidth : 1 ,
169+ gapWidth : 1 ,
157170 } ,
158171 label : { show : true , fontSize : 11 } ,
159172 } ,
160173 ] ,
161- label : { show : true , formatter : '{b}' , fontSize : 12 } ,
174+ label : {
175+ show : true ,
176+ formatter : '{b}' ,
177+ fontSize : 12 ,
178+ color : isDark ? '#ddd' : '#333' ,
179+ } ,
162180 } ] ,
163181 } ) , [ treemapData , isDark ] ) ;
164182
0 commit comments