tri-cli 0.0.52 → 0.0.53

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.
Files changed (3) hide show
  1. package/dist/Treemap.js +56 -20
  2. package/dist/cli.js +215 -26
  3. package/package.json +1 -1
package/dist/Treemap.js CHANGED
@@ -20,6 +20,7 @@ const oneHunter = {
20
20
 
21
21
  // Expanded color palette for directories
22
22
  const dirColors = [oneHunter.blue, oneHunter.cyan, oneHunter.green, oneHunter.yellow, oneHunter.orange, oneHunter.red, oneHunter.purple, oneHunter.magenta, '#4a9eff', '#7ec699', '#e8b339', '#ff7eb6'];
23
+ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
23
24
 
24
25
  // Function to format bytes to human readable
25
26
  const formatBytes = bytes => {
@@ -30,6 +31,14 @@ const formatBytes = bytes => {
30
31
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
31
32
  };
32
33
 
34
+ // Function to format counts
35
+ const formatCount = count => {
36
+ if (count === 0) return '0';
37
+ if (count < 1000) return count.toString();
38
+ if (count < 1000000) return (count / 1000).toFixed(1) + 'K';
39
+ return (count / 1000000).toFixed(1) + 'M';
40
+ };
41
+
33
42
  // Get color based on file extension
34
43
  const getFileColor = filename => {
35
44
  var _filename$match;
@@ -99,8 +108,19 @@ const assignGroupColors = root => {
99
108
  };
100
109
  export const TreeMapView = ({
101
110
  selectedNode,
102
- stdout
111
+ stdout,
112
+ mode = 'size',
113
+ loadingPaths = new Set(),
114
+ calculatedCounts = new Map(),
115
+ calculatedSizes = new Map()
103
116
  }) => {
117
+ const [spinnerFrame, setSpinnerFrame] = React.useState(0);
118
+
119
+ // Spinner animation loop
120
+ React.useEffect(() => {
121
+ const interval = setInterval(() => setSpinnerFrame(f => (f + 1) % SPINNER_FRAMES.length), 80);
122
+ return () => clearInterval(interval);
123
+ }, []);
104
124
  if (!selectedNode || !selectedNode.data) {
105
125
  return /*#__PURE__*/React.createElement(Box, {
106
126
  flexDirection: "column"
@@ -110,16 +130,20 @@ export const TreeMapView = ({
110
130
  }, "Error: Invalid node selected"), /*#__PURE__*/React.createElement(Text, {
111
131
  dimColor: true,
112
132
  color: oneHunter.textAlt
113
- }, "Press 't' to go back to tree view"));
133
+ }, "Press any key to go back to tree view"));
114
134
  }
115
135
  const width = Math.min((stdout === null || stdout === void 0 ? void 0 : stdout.columns) || 80, 120) - 4;
116
136
  const height = Math.min((stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24, 40) - 8;
117
137
 
138
+ // Choose formatter based on mode
139
+ const formatValue = mode === 'count' ? formatCount : formatBytes;
140
+ const modeLabel = mode === 'count' ? 'Count' : 'Size';
141
+
118
142
  // If it's a file, show just that file as a big rectangle
119
143
  if (selectedNode.data.isFile) {
120
144
  const color = getFileColor(selectedNode.data.name);
121
145
  const name = selectedNode.data.name;
122
- const sizeStr = selectedNode.data.size ? formatBytes(selectedNode.data.size) : '';
146
+ const valueStr = mode === 'count' ? '1 file' : selectedNode.data.size ? formatBytes(selectedNode.data.size) : '';
123
147
  const grid = Array(height).fill(null).map(() => Array(width).fill(null).map(() => ({
124
148
  char: ' ',
125
149
  color
@@ -149,12 +173,12 @@ export const TreeMapView = ({
149
173
  };
150
174
  }
151
175
  }
152
- if (sizeStr && midY + 2 < height - 2) {
153
- const sizeX = Math.floor((width - sizeStr.length) / 2);
154
- for (let i = 0; i < sizeStr.length && sizeX + i < width - 2; i++) {
176
+ if (valueStr && midY + 2 < height - 2) {
177
+ const sizeX = Math.floor((width - valueStr.length) / 2);
178
+ for (let i = 0; i < valueStr.length && sizeX + i < width - 2; i++) {
155
179
  if (sizeX + i > 0) {
156
180
  grid[midY + 2][sizeX + i] = {
157
- char: sizeStr[i],
181
+ char: valueStr[i],
158
182
  color
159
183
  };
160
184
  }
@@ -194,10 +218,10 @@ export const TreeMapView = ({
194
218
  }, /*#__PURE__*/React.createElement(Text, {
195
219
  bold: true,
196
220
  color: oneHunter.cyan
197
- }, "TreeMap View: ", name, " (File)"), /*#__PURE__*/React.createElement(Text, {
221
+ }, "TreeMap View (", modeLabel, "): ", name, " (File)"), /*#__PURE__*/React.createElement(Text, {
198
222
  dimColor: true,
199
223
  color: oneHunter.textAlt
200
- }, "Press 't' to toggle back to tree view | Ctrl+C: Exit"), /*#__PURE__*/React.createElement(Box, {
224
+ }, "Press any key to go back to tree view | Ctrl+C: Exit"), /*#__PURE__*/React.createElement(Box, {
201
225
  borderStyle: "single",
202
226
  borderColor: oneHunter.textAlt,
203
227
  flexDirection: "column",
@@ -206,7 +230,7 @@ export const TreeMapView = ({
206
230
  dimColor: true,
207
231
  color: oneHunter.textAlt,
208
232
  marginTop: 1
209
- }, "File: ", name, " | Size: ", sizeStr || 'Unknown'));
233
+ }, "File: ", name, " | ", modeLabel, ": ", valueStr || 'Unknown'));
210
234
  }
211
235
 
212
236
  // It's a directory - show treemap of its children
@@ -216,10 +240,10 @@ export const TreeMapView = ({
216
240
  }, /*#__PURE__*/React.createElement(Text, {
217
241
  bold: true,
218
242
  color: oneHunter.cyan
219
- }, "TreeMap View: ", selectedNode.data.name, " (Empty Directory)"), /*#__PURE__*/React.createElement(Text, {
243
+ }, "TreeMap View (", modeLabel, "): ", selectedNode.data.name, " (Empty Directory)"), /*#__PURE__*/React.createElement(Text, {
220
244
  dimColor: true,
221
245
  color: oneHunter.textAlt
222
- }, "Press 't' to toggle back to tree view | Ctrl+C: Exit"), /*#__PURE__*/React.createElement(Box, {
246
+ }, "Press any key to go back to tree view | Ctrl+C: Exit"), /*#__PURE__*/React.createElement(Box, {
223
247
  borderStyle: "single",
224
248
  borderColor: oneHunter.textAlt,
225
249
  marginTop: 1,
@@ -248,12 +272,18 @@ export const TreeMapView = ({
248
272
  const x1 = Math.ceil(node.x1);
249
273
  const y1 = Math.ceil(node.y1);
250
274
  const isFile = node.data.isFile;
275
+ const isNodeLoading = loadingPaths.has(node.data.path);
251
276
  const boxWidth = x1 - x0;
252
277
  const boxHeight = y1 - y0;
253
278
  if (boxWidth < 1 || boxHeight < 1) return;
254
279
 
255
- // Color based on type
256
- const color = isFile ? getFileColor(node.data.name) : node.groupColor || oneHunter.blue;
280
+ // Color based on type (dimmed if loading)
281
+ let color = isFile ? getFileColor(node.data.name) : node.groupColor || oneHunter.blue;
282
+
283
+ // Dim the color if loading
284
+ if (isNodeLoading) {
285
+ color = oneHunter.textAlt;
286
+ }
257
287
 
258
288
  // Draw border
259
289
  for (let y = y0; y < y1 && y < height; y++) {
@@ -277,14 +307,14 @@ export const TreeMapView = ({
277
307
 
278
308
  // Draw label if there's room
279
309
  const name = node.data.name;
280
- const sizeStr = node.value ? ` ${formatBytes(node.value)}` : '';
310
+ const valueStr = isNodeLoading ? ` ${SPINNER_FRAMES[spinnerFrame]}` : node.value ? ` ${formatValue(node.value)}${mode === 'count' ? ' files' : ''}` : '';
281
311
  if (boxWidth >= 8 && boxHeight >= 2) {
282
312
  const labelY = y0 + 1;
283
313
  const labelX = x0 + 1;
284
314
  const maxLength = boxWidth - 2;
285
315
 
286
- // Combine name and size
287
- let label = name + sizeStr;
316
+ // Combine name and value
317
+ let label = name + valueStr;
288
318
  if (label.length > maxLength) {
289
319
  // Try just name
290
320
  if (name.length <= maxLength) {
@@ -374,15 +404,21 @@ export const TreeMapView = ({
374
404
  key: y
375
405
  }, segments);
376
406
  });
407
+ const totalLabel = mode === 'count' ? `${formatCount(root.value || 0)} files` : formatBytes(root.value || 0);
408
+ const isLoading = loadingPaths.size > 0;
377
409
  return /*#__PURE__*/React.createElement(Box, {
378
410
  flexDirection: "column"
379
411
  }, /*#__PURE__*/React.createElement(Text, {
380
412
  bold: true,
381
413
  color: oneHunter.cyan
382
- }, "TreeMap View: ", selectedNode.data.name), /*#__PURE__*/React.createElement(Text, {
414
+ }, "TreeMap View (", modeLabel, "): ", selectedNode.data.name), /*#__PURE__*/React.createElement(Box, {
415
+ justifyContent: "space-between"
416
+ }, /*#__PURE__*/React.createElement(Text, {
383
417
  dimColor: true,
384
418
  color: oneHunter.textAlt
385
- }, "Press 't' to toggle back to tree view | \u2191/\u2193: Navigate in tree | Ctrl+C: Exit"), /*#__PURE__*/React.createElement(Box, {
419
+ }, "Press any key to go back to tree view | \u2191/\u2193: Navigate in tree | Ctrl+C: Exit"), isLoading && /*#__PURE__*/React.createElement(Text, {
420
+ color: oneHunter.yellow
421
+ }, ' ', SPINNER_FRAMES[spinnerFrame], " Loading ", loadingPaths.size, "...")), /*#__PURE__*/React.createElement(Box, {
386
422
  borderStyle: "single",
387
423
  borderColor: oneHunter.textAlt,
388
424
  flexDirection: "column",
@@ -391,5 +427,5 @@ export const TreeMapView = ({
391
427
  dimColor: true,
392
428
  color: oneHunter.textAlt,
393
429
  marginTop: 1
394
- }, "Showing ", firstLevelNodes.length, " items | Total:", ' ', formatBytes(root.value || 0)));
430
+ }, "Showing ", firstLevelNodes.length, " items | Total: ", totalLabel));
395
431
  };
package/dist/cli.js CHANGED
@@ -30,6 +30,12 @@ const formatBytes = bytes => {
30
30
  const i = Math.floor(Math.log(bytes) / Math.log(k));
31
31
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
32
32
  };
33
+ const formatCount = count => {
34
+ if (count === 0) return '0';
35
+ if (count < 1000) return count.toString();
36
+ if (count < 1000000) return (count / 1000).toFixed(1) + 'K';
37
+ return (count / 1000000).toFixed(1) + 'M';
38
+ };
33
39
  const formatDate = timestamp => {
34
40
  const date = new Date(timestamp);
35
41
  const now = new Date();
@@ -66,6 +72,38 @@ const getDirSizeAsync = async dirPath => {
66
72
  return 0;
67
73
  }
68
74
  };
75
+
76
+ // Fast file counting - much faster than size calculation
77
+ const countFilesAsync = async dirPath => {
78
+ try {
79
+ const stats = fs.statSync(dirPath);
80
+ if (!stats.isDirectory()) return 1;
81
+ let count = 0;
82
+ const countRecursive = async dir => {
83
+ try {
84
+ const entries = fs.readdirSync(dir, {
85
+ withFileTypes: true
86
+ });
87
+ for (const entry of entries) {
88
+ // Skip hidden files
89
+ if (entry.name.startsWith('.')) continue;
90
+ const fullPath = path.join(dir, entry.name);
91
+ if (entry.isFile()) {
92
+ count++;
93
+ } else if (entry.isDirectory()) {
94
+ await countRecursive(fullPath);
95
+ }
96
+ }
97
+ } catch {
98
+ // Skip directories we can't read
99
+ }
100
+ };
101
+ await countRecursive(dirPath);
102
+ return count;
103
+ } catch {
104
+ return 0;
105
+ }
106
+ };
69
107
  const readDirTree = (dirPath, noSize = true, currentLevel = 0, maxLevel = Infinity) => {
70
108
  try {
71
109
  const stats = fs.statSync(dirPath);
@@ -182,6 +220,7 @@ const TreeRow = /*#__PURE__*/React.memo(({
182
220
  shortcutInput,
183
221
  noSize,
184
222
  calculatedSizes,
223
+ calculatedCounts,
185
224
  loadingPaths,
186
225
  spinnerFrame,
187
226
  lastModifiedDates
@@ -199,6 +238,7 @@ const TreeRow = /*#__PURE__*/React.memo(({
199
238
  } else {
200
239
  displaySize = node.data.size;
201
240
  }
241
+ const fileCount = calculatedCounts === null || calculatedCounts === void 0 ? void 0 : calculatedCounts.get(node.data.path);
202
242
  const lastModified = lastModifiedDates.get(node.data.path);
203
243
  const treeLines = getTreeLines(node);
204
244
 
@@ -235,7 +275,11 @@ const TreeRow = /*#__PURE__*/React.memo(({
235
275
  backgroundColor: isSelected ? oneHunter.green : undefined
236
276
  }, node.data.name, !node.data.isFile && !isRoot ? '/' : ''), hasChildren && !isCollapsed && /*#__PURE__*/React.createElement(Text, {
237
277
  color: oneHunter.textAlt
238
- }, " (", node.children.length, ")"), isLoading ? /*#__PURE__*/React.createElement(Text, {
278
+ }, " (", node.children.length, ")"), fileCount !== undefined && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, {
279
+ color: oneHunter.purple
280
+ }, " [", formatCount(fileCount), " files]"), isLoading && /*#__PURE__*/React.createElement(Text, {
281
+ color: oneHunter.yellow
282
+ }, " ", SPINNER_FRAMES[spinnerFrame])), isLoading ? /*#__PURE__*/React.createElement(Text, {
239
283
  color: oneHunter.yellow
240
284
  }, " ", SPINNER_FRAMES[spinnerFrame]) : displaySize > 0 ? /*#__PURE__*/React.createElement(Text, {
241
285
  color: oneHunter.cyan
@@ -253,8 +297,10 @@ const TreeVisualization = ({
253
297
  const [collapsed, setCollapsed] = useState(new Set());
254
298
  const [shortcutInput, setShortcutInput] = useState('');
255
299
  const [showTreeMap, setShowTreeMap] = useState(false);
300
+ const [treeMapMode, setTreeMapMode] = useState('size'); // 'size' or 'count'
256
301
  const [initialized, setInitialized] = useState(false);
257
302
  const [calculatedSizes, setCalculatedSizes] = useState(new Map());
303
+ const [calculatedCounts, setCalculatedCounts] = useState(new Map());
258
304
  const [loadingPaths, setLoadingPaths] = useState(new Set());
259
305
  const [spinnerFrame, setSpinnerFrame] = useState(0);
260
306
  const [rootTree, setRootTree] = useState(() => hierarchy(readDirTree(dirPath, noSize, 0, maxLevel)));
@@ -385,6 +431,85 @@ const TreeVisualization = ({
385
431
  await new Promise(r => setTimeout(r, 0));
386
432
  }
387
433
  };
434
+ const countFilesInTreeAsync = async node => {
435
+ // Collect node and all its descendants
436
+ const nodesToCount = [];
437
+ const collectNodes = n => {
438
+ // Skip if already counted
439
+ if (calculatedCounts.has(n.data.path)) {
440
+ // Still recurse to children in case they need counting
441
+ if (n.children) {
442
+ n.children.forEach(collectNodes);
443
+ }
444
+ return;
445
+ }
446
+ nodesToCount.push(n);
447
+ if (n.children) {
448
+ n.children.forEach(collectNodes);
449
+ }
450
+ };
451
+ collectNodes(node);
452
+ if (nodesToCount.length === 0) return; // Nothing to count
453
+
454
+ // Mark all nodes as loading
455
+ const pathsToLoad = nodesToCount.map(n => n.data.path);
456
+ setLoadingPaths(prev => new Set([...prev, ...pathsToLoad]));
457
+ const newMap = new Map(calculatedCounts);
458
+
459
+ // Count and stream updates for each node individually
460
+ for (const targetNode of nodesToCount) {
461
+ // For directories, stream partial counts as we count
462
+ if (!targetNode.data.isFile) {
463
+ let runningCount = 0;
464
+ let updateCounter = 0;
465
+ const countRecursive = async dir => {
466
+ try {
467
+ const entries = fs.readdirSync(dir, {
468
+ withFileTypes: true
469
+ });
470
+ for (const entry of entries) {
471
+ // Skip hidden files
472
+ if (entry.name.startsWith('.')) continue;
473
+ const fullPath = path.join(dir, entry.name);
474
+ if (entry.isFile()) {
475
+ runningCount++;
476
+ updateCounter++;
477
+ // Stream update every 100 files for better performance
478
+ if (updateCounter >= 100) {
479
+ updateCounter = 0;
480
+ newMap.set(targetNode.data.path, runningCount);
481
+ React.startTransition(() => {
482
+ setCalculatedCounts(new Map(newMap));
483
+ });
484
+ await new Promise(r => setTimeout(r, 0));
485
+ }
486
+ } else if (entry.isDirectory()) {
487
+ await countRecursive(fullPath);
488
+ }
489
+ }
490
+ } catch {
491
+ // Skip directories we can't read
492
+ }
493
+ };
494
+ await countRecursive(targetNode.data.path);
495
+ newMap.set(targetNode.data.path, runningCount);
496
+ } else {
497
+ // Files are always 1
498
+ newMap.set(targetNode.data.path, 1);
499
+ }
500
+
501
+ // Stream the final update and remove from loading
502
+ React.startTransition(() => {
503
+ setCalculatedCounts(new Map(newMap));
504
+ setLoadingPaths(prev => {
505
+ const next = new Set(prev);
506
+ next.delete(targetNode.data.path);
507
+ return next;
508
+ });
509
+ });
510
+ await new Promise(r => setTimeout(r, 0));
511
+ }
512
+ };
388
513
  const getLastModifiedAsync = async node => {
389
514
  // Collect node and all its descendants
390
515
  const nodesToProcess = [];
@@ -460,8 +585,8 @@ const TreeVisualization = ({
460
585
  return;
461
586
  }
462
587
  if (input === 'q') process.exit(0);
463
- if (input === 'C') {
464
- // Toggle collapse/expand all children of the selected node's PARENT
588
+ if (input === 'C' && !key.shift) {
589
+ // Toggle collapse/expand all children of the selected node's PARENT (lowercase handling moved)
465
590
  const selectedNode = visibleNodes[selectedIndex];
466
591
  if (!selectedNode || !selectedNode.parent) return;
467
592
  const parentNode = selectedNode.parent;
@@ -492,6 +617,40 @@ const TreeVisualization = ({
492
617
  });
493
618
  return;
494
619
  }
620
+ if (key.shift && input === 'C') {
621
+ // Shift+C: Show count treemap
622
+ const node = visibleNodes[selectedIndex];
623
+ if (!node) return;
624
+
625
+ // Collect all nodes that need counting
626
+ const nodesToCount = [];
627
+ const collectNodes = n => {
628
+ if (calculatedCounts.has(n.data.path)) {
629
+ if (n.children) {
630
+ n.children.forEach(collectNodes);
631
+ }
632
+ return;
633
+ }
634
+ nodesToCount.push(n);
635
+ if (n.children) {
636
+ n.children.forEach(collectNodes);
637
+ }
638
+ };
639
+ collectNodes(node);
640
+
641
+ // Mark nodes as loading before starting
642
+ if (nodesToCount.length > 0) {
643
+ const pathsToLoad = nodesToCount.map(n => n.data.path);
644
+ setLoadingPaths(prev => new Set([...prev, ...pathsToLoad]));
645
+ }
646
+
647
+ // Start calculating counts in the background
648
+ countFilesInTreeAsync(node);
649
+ // Show count treemap immediately
650
+ setTreeMapMode('count');
651
+ setShowTreeMap(true);
652
+ return;
653
+ }
495
654
  if (input === 'O') {
496
655
  // Toggle collapse/expand ALL nodes in the entire tree
497
656
  const allNodesWithChildren = [];
@@ -524,7 +683,8 @@ const TreeVisualization = ({
524
683
  if (!node) return;
525
684
  // Always start calculating sizes in the background (calculateSizeAsync skips already-calculated nodes)
526
685
  calculateSizeAsync(node);
527
- // Show treemap immediately
686
+ // Show size treemap immediately
687
+ setTreeMapMode('size');
528
688
  setShowTreeMap(true);
529
689
  return;
530
690
  }
@@ -533,6 +693,11 @@ const TreeVisualization = ({
533
693
  if (node) await calculateSizeAsync(node);
534
694
  return;
535
695
  }
696
+ if (input === 'c') {
697
+ const node = visibleNodes[selectedIndex];
698
+ if (node) await countFilesInTreeAsync(node);
699
+ return;
700
+ }
536
701
  if (input === 'l') {
537
702
  const node = visibleNodes[selectedIndex];
538
703
  if (node) await getLastModifiedAsync(node);
@@ -597,31 +762,52 @@ const TreeVisualization = ({
597
762
 
598
763
  // Set value on the DATA object
599
764
  const data = hierarchyNode.data;
600
- if (data.isFile) {
601
- // Files: use calculatedSize or read from disk
602
- const calcSize = calculatedSizes.get(data.path);
603
- if (calcSize !== undefined) {
604
- data.value = calcSize;
605
- } else {
606
- try {
607
- data.value = fs.statSync(data.path).size;
608
- } catch {
765
+ if (treeMapMode === 'count') {
766
+ // COUNT MODE: Use file counts
767
+ if (data.isFile) {
768
+ // Files always count as 1
769
+ data.value = 1;
770
+ } else if (data.children === undefined || !hierarchyNode.children || hierarchyNode.children.length === 0) {
771
+ // Directory with no children loaded OR empty directory
772
+ // Use calculatedCount if available
773
+ const calcCount = calculatedCounts.get(data.path);
774
+ if (calcCount !== undefined) {
775
+ data.value = calcCount;
776
+ } else {
609
777
  data.value = 0;
610
778
  }
611
- }
612
- } else if (data.children === undefined || !hierarchyNode.children || hierarchyNode.children.length === 0) {
613
- // Directory with no children loaded OR empty directory
614
- // Use calculatedSize if available
615
- const calcSize = calculatedSizes.get(data.path);
616
- if (calcSize !== undefined) {
617
- data.value = calcSize;
618
779
  } else {
619
- // For unloaded directories without calculated size, will show as 0
620
- data.value = data.size || 0;
780
+ // Directory with loaded children - let d3's .sum() aggregate from children
781
+ data.value = undefined;
621
782
  }
622
783
  } else {
623
- // Directory with loaded children - let d3's .sum() aggregate from children
624
- data.value = undefined;
784
+ // SIZE MODE: Use file sizes
785
+ if (data.isFile) {
786
+ // Files: use calculatedSize or read from disk
787
+ const calcSize = calculatedSizes.get(data.path);
788
+ if (calcSize !== undefined) {
789
+ data.value = calcSize;
790
+ } else {
791
+ try {
792
+ data.value = fs.statSync(data.path).size;
793
+ } catch {
794
+ data.value = 0;
795
+ }
796
+ }
797
+ } else if (data.children === undefined || !hierarchyNode.children || hierarchyNode.children.length === 0) {
798
+ // Directory with no children loaded OR empty directory
799
+ // Use calculatedSize if available
800
+ const calcSize = calculatedSizes.get(data.path);
801
+ if (calcSize !== undefined) {
802
+ data.value = calcSize;
803
+ } else {
804
+ // For unloaded directories without calculated size, will show as 0
805
+ data.value = data.size || 0;
806
+ }
807
+ } else {
808
+ // Directory with loaded children - let d3's .sum() aggregate from children
809
+ data.value = undefined;
810
+ }
625
811
  }
626
812
  };
627
813
  populateValues(node);
@@ -629,7 +815,9 @@ const TreeVisualization = ({
629
815
  selectedNode: node,
630
816
  stdout: stdout,
631
817
  calculatedSizes: calculatedSizes,
632
- loadingPaths: loadingPaths
818
+ calculatedCounts: calculatedCounts,
819
+ loadingPaths: loadingPaths,
820
+ mode: treeMapMode
633
821
  });
634
822
  }
635
823
  const start = Math.max(0, selectedIndex - Math.floor(viewportHeight / 2));
@@ -643,7 +831,7 @@ const TreeVisualization = ({
643
831
  }, /*#__PURE__*/React.createElement(Text, {
644
832
  color: oneHunter.textAlt,
645
833
  dimColor: true
646
- }, "\u2191/\u2193: Navigate | Enter: Expand/Traverse | d: Size | l: Date | m: Map | q: Quit"), loadingPaths.size > 0 && /*#__PURE__*/React.createElement(Text, {
834
+ }, "\u2191/\u2193: Navigate | Enter: Expand/Traverse | c: Count | d: Size | l: Date | m: Map | Shift+C: CountMap | q: Quit"), loadingPaths.size > 0 && /*#__PURE__*/React.createElement(Text, {
647
835
  color: oneHunter.yellow
648
836
  }, ' ', SPINNER_FRAMES[spinnerFrame], " Loading ", loadingPaths.size, "...")), viewportNodes.map((node, i) => /*#__PURE__*/React.createElement(TreeRow, {
649
837
  key: start + i,
@@ -653,6 +841,7 @@ const TreeVisualization = ({
653
841
  shortcutInput: shortcutInput,
654
842
  noSize: noSize,
655
843
  calculatedSizes: calculatedSizes,
844
+ calculatedCounts: calculatedCounts,
656
845
  loadingPaths: loadingPaths,
657
846
  spinnerFrame: spinnerFrame,
658
847
  lastModifiedDates: lastModifiedDates
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tri-cli",
3
- "version": "0.0.52",
3
+ "version": "0.0.53",
4
4
  "description": "Interactive CLI directory tree visualizer with treemap view",
5
5
  "author": "Jared Wilber",
6
6
  "license": "MIT",