Skip to content

Commit 91803c4

Browse files
aksOpsclaude
andcommitted
fix: collapse single-child directory chains in treemap for usable drill-down
Deeply nested Java-style paths (src/main/java/io/github/...) created 10+ levels of single-child directories, making the treemap show "1 node" at each level and requiring excessive clicks to reach files. Now collapses chains like GitHub/IntelliJ (e.g. "src/main/java/io/github" as one node). Also bumps leafDepth from 1 to 2 for better visibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 369880b commit 91803c4

1 file changed

Lines changed: 36 additions & 3 deletions

File tree

src/main/frontend/src/pages/CodebaseMap.tsx

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,42 @@ function dominantLang(nodes: FileTreeNode[]): string {
5151
return Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0] ?? 'other';
5252
}
5353

54-
function fileTreeToECharts(nodes: FileTreeNode[]): EChartsTreeNode[] {
54+
/**
55+
* Collapse single-child directory chains into one node.
56+
* e.g., src → main → java → io → github becomes "src/main/java/io/github"
57+
* This avoids 10+ clicks through single-child directories (common in Java packages).
58+
*/
59+
function collapseTree(nodes: FileTreeNode[]): FileTreeNode[] {
60+
return nodes.map(n => {
61+
if (n.type !== 'directory' || !n.children || n.children.length === 0) return n;
62+
63+
// Collapse: if this directory has exactly 1 child that is also a directory, merge names
64+
let current = n;
65+
let collapsedName = n.name;
66+
while (
67+
current.type === 'directory' &&
68+
current.children &&
69+
current.children.length === 1 &&
70+
current.children[0].type === 'directory' &&
71+
current.children[0].children &&
72+
current.children[0].children.length > 0
73+
) {
74+
current = current.children[0];
75+
collapsedName += '/' + current.name;
76+
}
77+
78+
const collapsedChildren = collapseTree(current.children ?? []);
79+
return { ...current, name: collapsedName, children: collapsedChildren, nodeCount: n.nodeCount };
80+
});
81+
}
82+
83+
function toEChartsNodes(nodes: FileTreeNode[]): EChartsTreeNode[] {
5584
const result: EChartsTreeNode[] = [];
5685
for (const n of nodes) {
5786
if (n.nodeCount <= 0 && (!n.children || n.children.length === 0)) continue;
5887

5988
if (n.type === 'directory' && n.children && n.children.length > 0) {
60-
const children = fileTreeToECharts(n.children);
89+
const children = toEChartsNodes(n.children);
6190
if (children.length === 0) continue;
6291
const lang = dominantLang(n.children);
6392
// Directory nodes: NO value — ECharts sums from children for correct proportions
@@ -79,6 +108,10 @@ function fileTreeToECharts(nodes: FileTreeNode[]): EChartsTreeNode[] {
79108
return result;
80109
}
81110

111+
function fileTreeToECharts(nodes: FileTreeNode[]): EChartsTreeNode[] {
112+
return toEChartsNodes(collapseTree(nodes));
113+
}
114+
82115
function collectLanguages(nodes: FileTreeNode[]): string[] {
83116
const langs = new Set<string>();
84117
function walk(items: FileTreeNode[]) {
@@ -117,7 +150,7 @@ export default function CodebaseMap() {
117150
series: [{
118151
type: 'treemap',
119152
data: treemapData,
120-
leafDepth: 1,
153+
leafDepth: 2,
121154
drillDownIcon: '▶ ',
122155
roam: false,
123156
nodeClick: 'zoomToNode',

0 commit comments

Comments
 (0)