tri-cli 0.0.4 → 0.0.52

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.
package/README.md CHANGED
@@ -1,15 +1,52 @@
1
1
  # tri-cli
2
2
 
3
- A terminal-based interactive directory visualizer, like `tree` but with keyboard navgiation, shortcuts, colors, and [treemap](https://en.wikipedia.org/wiki/Treemapping) visuals:
3
+ A terminal-based interactive directory visualizer, like `tree` but with keyboard navigation, shortcuts, colors, and [treemap](https://en.wikipedia.org/wiki/Treemapping) visuals:
4
4
 
5
5
  ![tri demo in gif](/assets/readme.gif)
6
6
 
7
+
7
8
  ## Installation
8
9
 
10
+ ### Global Installation (Recommended)
11
+
12
+ It's recommended to install globally:
13
+
9
14
  ```bash
10
15
  npm install -g tri-cli
11
16
  ```
12
17
 
18
+ After global installation, you can run `tri` directly from anywhere:
19
+
20
+ ```bash
21
+ tri .
22
+ ```
23
+
24
+ ### Local Installation
25
+
26
+ If you don't want to install globally, you can install locally and then run anywhere with `npx`:
27
+
28
+ ```bash
29
+ npm install tri-cli
30
+ ```
31
+
32
+ For local installations, use `npx` to run the command:
33
+
34
+ ```bash
35
+ npx tri .
36
+ ```
37
+
38
+ Or add it to your `package.json` scripts:
39
+
40
+ ```json
41
+ {
42
+ "scripts": {
43
+ "tree": "tri"
44
+ }
45
+ }
46
+ ```
47
+
48
+ Then run with `npm run tree`.
49
+
13
50
  ## Usage
14
51
 
15
52
  ```bash
@@ -22,46 +59,72 @@ tri [directory] [options]
22
59
  tri . # visualize current directory
23
60
  tri ../bionemo -L 2 # show 2 levels deep
24
61
  tri --dir src # specify directory with flag
25
- tri --dir src -ns # create tree without dir/file size (faster, but no treemap)
62
+ tri src -s # preload all sizes (enables immediate treemap)
63
+ tri --dir src -c # start with all directories collapsed
26
64
  ```
27
65
 
28
66
  ### Interactive Controls
29
67
 
30
- | Key | Action |
31
- | ----- | ------------------------------ |
32
- | ↑ / ↓ | Navigate files and folders |
33
- | Enter | Expand or collapse a directory |
34
- | t | Toggle Treemap view |
35
- | Esc | Clear typed shortcut |
36
- | q | Quit |
37
-
38
- ### Options
39
-
40
- | Option | Description |
41
- | -------------- | ----------------------------------------------- |
42
- | `--dir <path>` | Directory to visualize (optional if positional) |
43
- | `-L <level>` | Depth level to expand initially (default: 1) |
44
- | `-ns` | Don't calculate sizes (faster, but no treemap) |
45
- | `-h, --help` | Show help message |
46
-
47
- **Example:**
68
+ | Key | Action |
69
+ | ------------- | ----------------------------------------- |
70
+ | ↑ / ↓ | Navigate files and folders |
71
+ | Enter | Expand or collapse a directory |
72
+ | [a-z]/[0-9] | Type shortcut to jump to item |
73
+ | d | Calculate and display sizes recursively |
74
+ | l | Show last modified dates (ls -l style) |
75
+ | m | Toggle treemap view |
76
+ | Esc | Clear typed shortcut |
77
+ | q | Quit |
78
+
79
+ ### Command Line Options
80
+
81
+ | Option | Description |
82
+ | ---------------- | ------------------------------------------------ |
83
+ | `[directory]` | Directory to visualize (default: current dir) |
84
+ | `-L <level>` | Maximum depth to load initially (default: 1) |
85
+ | `-s, --size` | Preload all file/directory sizes |
86
+ | `-c, --collapse` | Start with all directories collapsed |
87
+ | `-h, --help` | Show help message |
88
+
89
+ ## Features
90
+
91
+ ### Tree View
92
+ - **Keyboard shortcuts**: Each item gets a shortcut label - just type it to jump there instantly
93
+ - **Tree-style lines**: Visual hierarchy with Unicode box-drawing characters (├─, ╰─, │)
94
+ - **Color coding**: Different colors for different file types
95
+ - **Lazy loading**: Only loads directory contents when you expand them (fast for large directories)
96
+ - **Size calculation**: Press `d` on any item to calculate disk usage recursively
97
+ - **Date display**: Press `l` to show last modified dates in `ls -l` format
98
+
99
+ ### Treemap View
100
+ - Press `m` to visualize the selected directory as a treemap
101
+ - Visual representation of file/directory sizes
102
+ - Hierarchical rectangles sized proportionally to disk usage
103
+ - Color-coded by file type or directory group
104
+
105
+ ## Development
106
+
107
+ Clone the repository and install dependencies:
48
108
 
49
109
  ```bash
50
- tri ../bionemo -L 2
110
+ git clone https://github.com/jwilber/tri-cli.git
111
+ cd tri-cli
112
+ npm install
51
113
  ```
52
114
 
53
- Type `t` to open (and close) the treemap view, navigate with arrow keys, and press `q` to quit.
54
-
55
- **Example:**
56
-
57
- It's probably easiest to just uninstall and link:
115
+ Build and link for local development:
58
116
 
59
117
  ```bash
60
118
  npm run build
61
-
62
119
  npm link
63
120
  ```
64
121
 
65
- **License:**
122
+ Now you can run `tri` from anywhere and it will use your local development version.
123
+
124
+ ## License
66
125
 
67
126
  MIT
127
+
128
+ ## Author
129
+
130
+ Jared Wilber
package/dist/Treemap.js CHANGED
@@ -32,7 +32,8 @@ const formatBytes = bytes => {
32
32
 
33
33
  // Get color based on file extension
34
34
  const getFileColor = filename => {
35
- const ext = filename.match(/\.[^.]+$/)?.[0]?.toLowerCase();
35
+ var _filename$match;
36
+ const ext = (_filename$match = filename.match(/\.[^.]+$/)) === null || _filename$match === void 0 || (_filename$match = _filename$match[0]) === null || _filename$match === void 0 ? void 0 : _filename$match.toLowerCase();
36
37
  const colorMap = {
37
38
  '.js': oneHunter.yellow,
38
39
  '.jsx': oneHunter.yellow,
@@ -111,8 +112,8 @@ export const TreeMapView = ({
111
112
  color: oneHunter.textAlt
112
113
  }, "Press 't' to go back to tree view"));
113
114
  }
114
- const width = Math.min(stdout?.columns || 80, 120) - 4;
115
- const height = Math.min(stdout?.rows || 24, 40) - 8;
115
+ const width = Math.min((stdout === null || stdout === void 0 ? void 0 : stdout.columns) || 80, 120) - 4;
116
+ const height = Math.min((stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24, 40) - 8;
116
117
 
117
118
  // If it's a file, show just that file as a big rectangle
118
119
  if (selectedNode.data.isFile) {
@@ -307,8 +308,9 @@ export const TreeMapView = ({
307
308
  const maxLength = boxWidth - 2;
308
309
  let label;
309
310
  if (isFile) {
311
+ var _name$match;
310
312
  // Show extension for files
311
- const ext = name.match(/\.([^.]{1,3})$/)?.[1];
313
+ const ext = (_name$match = name.match(/\.([^.]{1,3})$/)) === null || _name$match === void 0 ? void 0 : _name$match[1];
312
314
  label = ext || name.slice(0, maxLength);
313
315
  } else {
314
316
  // Show first few chars for directories
package/dist/cli.js CHANGED
@@ -30,6 +30,24 @@ 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 formatDate = timestamp => {
34
+ const date = new Date(timestamp);
35
+ const now = new Date();
36
+ const diffDays = Math.floor((now - date) / (1000 * 60 * 60 * 24));
37
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
38
+ const month = months[date.getMonth()];
39
+ const day = date.getDate().toString().padStart(2, ' ');
40
+
41
+ // If file is from this year, show time; otherwise show year
42
+ if (diffDays < 180) {
43
+ const hours = date.getHours().toString().padStart(2, '0');
44
+ const minutes = date.getMinutes().toString().padStart(2, '0');
45
+ return `${month} ${day} ${hours}:${minutes}`;
46
+ } else {
47
+ const year = date.getFullYear();
48
+ return `${month} ${day} ${year}`;
49
+ }
50
+ };
33
51
 
34
52
  // Async non-blocking directory size
35
53
  const getDirSizeAsync = async dirPath => {
@@ -117,7 +135,7 @@ const getFileColor = filename => {
117
135
 
118
136
  // Exclude m, t, d
119
137
  const generateShortcut = (index, isFile, parentShortcut = '') => {
120
- const letters = 'abcefgijklnopqrsuvwxyz';
138
+ const letters = 'abcefgijknopqrsuvwxyz';
121
139
  const base = letters.length;
122
140
  if (isFile) return `${parentShortcut}${index + 1}`;
123
141
  let label = '';
@@ -128,6 +146,34 @@ const generateShortcut = (index, isFile, parentShortcut = '') => {
128
146
  }
129
147
  return parentShortcut + label;
130
148
  };
149
+
150
+ // Generate tree lines (like the `tree` command)
151
+ const getTreeLines = node => {
152
+ if (node.depth === 0) return ''; // Root has no lines
153
+
154
+ const lines = [];
155
+ let current = node;
156
+
157
+ // Build the lines from current node up to (but not including) root
158
+ while (current.parent && current.parent.depth > 0) {
159
+ const parent = current.parent;
160
+ const isLastChild = parent.children && current === parent.children[parent.children.length - 1];
161
+ lines.unshift(isLastChild ? ' ' : '│ ');
162
+ current = parent;
163
+ }
164
+
165
+ // Add the final connector for this node
166
+ const parent = node.parent;
167
+ if (parent && parent.depth === 0) {
168
+ // Direct child of root
169
+ const isLastChild = parent.children && node === parent.children[parent.children.length - 1];
170
+ return isLastChild ? '╰─' : '├─';
171
+ } else if (parent) {
172
+ const isLastChild = parent.children && node === parent.children[parent.children.length - 1];
173
+ return lines.join('') + (isLastChild ? '╰─' : '├─');
174
+ }
175
+ return '';
176
+ };
131
177
  const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
132
178
  const TreeRow = /*#__PURE__*/React.memo(({
133
179
  node,
@@ -137,12 +183,15 @@ const TreeRow = /*#__PURE__*/React.memo(({
137
183
  noSize,
138
184
  calculatedSizes,
139
185
  loadingPaths,
140
- spinnerFrame
186
+ spinnerFrame,
187
+ lastModifiedDates
141
188
  }) => {
142
189
  const isCollapsed = collapsed.has(node.data.path);
143
190
  const hasChildren = node.children && node.children.length > 0;
144
191
  const isLoading = loadingPaths.has(node.data.path);
145
192
  const fileColor = node.data.isFile ? getFileColor(node.data.name) : oneHunter.magenta;
193
+ const isRoot = node.depth === 0;
194
+ const isDirectory = !node.data.isFile;
146
195
  let displaySize = 0;
147
196
  // Use calculated size if available, otherwise fall back to node.data.size
148
197
  if (calculatedSizes && calculatedSizes.has(node.data.path)) {
@@ -150,6 +199,8 @@ const TreeRow = /*#__PURE__*/React.memo(({
150
199
  } else {
151
200
  displaySize = node.data.size;
152
201
  }
202
+ const lastModified = lastModifiedDates.get(node.data.path);
203
+ const treeLines = getTreeLines(node);
153
204
 
154
205
  // ✅ Restore highlight logic
155
206
  const renderShortcut = () => {
@@ -172,21 +223,25 @@ const TreeRow = /*#__PURE__*/React.memo(({
172
223
  dimColor: true
173
224
  }, ' ', "[", node.shortcut, "]");
174
225
  };
175
- return /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Text, null, /*#__PURE__*/React.createElement(Text, {
226
+ return /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Text, null, !isRoot && /*#__PURE__*/React.createElement(Text, {
227
+ color: oneHunter.textAlt
228
+ }, treeLines), !isRoot && isDirectory && /*#__PURE__*/React.createElement(Text, {
176
229
  color: oneHunter.textAlt
177
- }, node.depth > 0 ? ' '.repeat(node.depth * 2) : ''), /*#__PURE__*/React.createElement(Text, {
178
- color: hasChildren ? oneHunter.cyan : oneHunter.textAlt
179
- }, hasChildren ? isCollapsed ? '▶ ' : '▼ ' : ' '), /*#__PURE__*/React.createElement(Text, {
230
+ }, hasChildren && !isCollapsed ? ' ' : ''), !isRoot && !isDirectory && /*#__PURE__*/React.createElement(Text, {
231
+ color: oneHunter.textAlt
232
+ }, "\u2500 "), /*#__PURE__*/React.createElement(Text, {
180
233
  bold: isSelected,
181
234
  color: isSelected ? oneHunter.bg : fileColor,
182
235
  backgroundColor: isSelected ? oneHunter.green : undefined
183
- }, node.data.name), hasChildren && !isCollapsed && /*#__PURE__*/React.createElement(Text, {
236
+ }, node.data.name, !node.data.isFile && !isRoot ? '/' : ''), hasChildren && !isCollapsed && /*#__PURE__*/React.createElement(Text, {
184
237
  color: oneHunter.textAlt
185
238
  }, " (", node.children.length, ")"), isLoading ? /*#__PURE__*/React.createElement(Text, {
186
239
  color: oneHunter.yellow
187
240
  }, " ", SPINNER_FRAMES[spinnerFrame]) : displaySize > 0 ? /*#__PURE__*/React.createElement(Text, {
188
241
  color: oneHunter.cyan
189
- }, " ", formatBytes(displaySize)) : /*#__PURE__*/React.createElement(Text, null, " "), renderShortcut()));
242
+ }, " ", formatBytes(displaySize)) : /*#__PURE__*/React.createElement(Text, null, " "), renderShortcut(), lastModified && /*#__PURE__*/React.createElement(Text, {
243
+ color: oneHunter.text2
244
+ }, " ", formatDate(lastModified))));
190
245
  });
191
246
  const TreeVisualization = ({
192
247
  dirPath = '.',
@@ -203,10 +258,11 @@ const TreeVisualization = ({
203
258
  const [loadingPaths, setLoadingPaths] = useState(new Set());
204
259
  const [spinnerFrame, setSpinnerFrame] = useState(0);
205
260
  const [rootTree, setRootTree] = useState(() => hierarchy(readDirTree(dirPath, noSize, 0, maxLevel)));
261
+ const [lastModifiedDates, setLastModifiedDates] = useState(new Map());
206
262
  const {
207
263
  stdout
208
264
  } = useStdout();
209
- const terminalHeight = stdout?.rows || 24;
265
+ const terminalHeight = (stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24;
210
266
  const viewportHeight = Math.max(5, terminalHeight - 7);
211
267
 
212
268
  // Spinner animation loop
@@ -329,6 +385,44 @@ const TreeVisualization = ({
329
385
  await new Promise(r => setTimeout(r, 0));
330
386
  }
331
387
  };
388
+ const getLastModifiedAsync = async node => {
389
+ // Collect node and all its descendants
390
+ const nodesToProcess = [];
391
+ const collectNodes = n => {
392
+ // Skip if already have date
393
+ if (lastModifiedDates.has(n.data.path)) {
394
+ // Still recurse to children in case they need dates
395
+ if (n.children) {
396
+ n.children.forEach(collectNodes);
397
+ }
398
+ return;
399
+ }
400
+ nodesToProcess.push(n);
401
+ if (n.children) {
402
+ n.children.forEach(collectNodes);
403
+ }
404
+ };
405
+ collectNodes(node);
406
+ if (nodesToProcess.length === 0) return; // Nothing to process
407
+
408
+ const newMap = new Map(lastModifiedDates);
409
+
410
+ // Get mtime for each node
411
+ for (const targetNode of nodesToProcess) {
412
+ try {
413
+ const stats = fs.statSync(targetNode.data.path);
414
+ newMap.set(targetNode.data.path, stats.mtimeMs);
415
+ } catch {
416
+ // If we can't stat the file, skip it
417
+ }
418
+
419
+ // Stream the update immediately
420
+ React.startTransition(() => {
421
+ setLastModifiedDates(new Map(newMap));
422
+ });
423
+ await new Promise(r => setTimeout(r, 0));
424
+ }
425
+ };
332
426
  const traverseDir = async node => {
333
427
  if (node.data.isFile) return;
334
428
 
@@ -425,18 +519,11 @@ const TreeVisualization = ({
425
519
  });
426
520
  return;
427
521
  }
428
- if (input === 't') {
429
- const node = visibleNodes[selectedIndex];
430
- if (node) await traverseDir(node);
431
- return;
432
- }
433
522
  if (input === 'm') {
434
523
  const node = visibleNodes[selectedIndex];
435
524
  if (!node) return;
436
- // Start calculating sizes in the background if not already done (don't await)
437
- if (!calculatedSizes.has(node.data.path)) {
438
- calculateSizeAsync(node);
439
- }
525
+ // Always start calculating sizes in the background (calculateSizeAsync skips already-calculated nodes)
526
+ calculateSizeAsync(node);
440
527
  // Show treemap immediately
441
528
  setShowTreeMap(true);
442
529
  return;
@@ -446,6 +533,11 @@ const TreeVisualization = ({
446
533
  if (node) await calculateSizeAsync(node);
447
534
  return;
448
535
  }
536
+ if (input === 'l') {
537
+ const node = visibleNodes[selectedIndex];
538
+ if (node) await getLastModifiedAsync(node);
539
+ return;
540
+ }
449
541
  if (key.upArrow) {
450
542
  setSelectedIndex(p => Math.max(0, p - 1));
451
543
  setShortcutInput('');
@@ -458,11 +550,24 @@ const TreeVisualization = ({
458
550
  }
459
551
  if (key.return) {
460
552
  const node = visibleNodes[selectedIndex];
461
- if (node && node.children) setCollapsed(prev => {
462
- const next = new Set(prev);
463
- if (next.has(node.data.path)) next.delete(node.data.path);else next.add(node.data.path);
464
- return next;
465
- });
553
+ if (!node) return;
554
+
555
+ // Don't allow collapsing/expanding the root
556
+ if (node.depth === 0) return;
557
+ if (node.data.isFile) return; // Do nothing for files
558
+
559
+ // If it's a directory with no children loaded yet, traverse into it
560
+ if (node.data.children === undefined) {
561
+ await traverseDir(node);
562
+ } else {
563
+ // ← Changed from `else if (node.children)`
564
+ // Children already loaded, toggle collapse (even if empty)
565
+ setCollapsed(prev => {
566
+ const next = new Set(prev);
567
+ if (next.has(node.data.path)) next.delete(node.data.path);else next.add(node.data.path);
568
+ return next;
569
+ });
570
+ }
466
571
  setShortcutInput('');
467
572
  return;
468
573
  }
@@ -479,22 +584,44 @@ const TreeVisualization = ({
479
584
  if (showTreeMap) {
480
585
  const node = visibleNodes[selectedIndex];
481
586
 
482
- // Populate value property for treemap visualization from calculatedSizes
483
- const populateValues = n => {
484
- if (!n) return;
485
- // Set value from calculatedSizes if available
486
- const calcSize = calculatedSizes.get(n.data.path);
487
- if (calcSize !== undefined) {
488
- n.data.value = calcSize;
489
- } else if (n.data.isFile) {
490
- n.data.value = n.data.size;
491
- } else {
492
- // For directories without calculated size, use 0 (will be updated as data streams in)
493
- n.data.value = 0;
587
+ // Populate value property on DATA objects for treemap visualization
588
+ // This is important because TreeMapView creates a new hierarchy from node.data,
589
+ // so we need to set values on the data objects, not the hierarchy nodes
590
+ const populateValues = hierarchyNode => {
591
+ if (!hierarchyNode) return;
592
+
593
+ // Recursively populate children first (depth-first, bottom-up)
594
+ if (hierarchyNode.children) {
595
+ hierarchyNode.children.forEach(populateValues);
494
596
  }
495
- // Recursively populate children
496
- if (n.children) {
497
- n.children.forEach(populateValues);
597
+
598
+ // Set value on the DATA object
599
+ 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 {
609
+ data.value = 0;
610
+ }
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
+ } else {
619
+ // For unloaded directories without calculated size, will show as 0
620
+ data.value = data.size || 0;
621
+ }
622
+ } else {
623
+ // Directory with loaded children - let d3's .sum() aggregate from children
624
+ data.value = undefined;
498
625
  }
499
626
  };
500
627
  populateValues(node);
@@ -516,7 +643,7 @@ const TreeVisualization = ({
516
643
  }, /*#__PURE__*/React.createElement(Text, {
517
644
  color: oneHunter.textAlt,
518
645
  dimColor: true
519
- }, "\u2191/\u2193: Navigate | Enter: Toggle | d: Size | t: Traverse | m: Map | q: Quit"), loadingPaths.size > 0 && /*#__PURE__*/React.createElement(Text, {
646
+ }, "\u2191/\u2193: Navigate | Enter: Expand/Traverse | d: Size | l: Date | m: Map | q: Quit"), loadingPaths.size > 0 && /*#__PURE__*/React.createElement(Text, {
520
647
  color: oneHunter.yellow
521
648
  }, ' ', SPINNER_FRAMES[spinnerFrame], " Loading ", loadingPaths.size, "...")), viewportNodes.map((node, i) => /*#__PURE__*/React.createElement(TreeRow, {
522
649
  key: start + i,
@@ -527,7 +654,8 @@ const TreeVisualization = ({
527
654
  noSize: noSize,
528
655
  calculatedSizes: calculatedSizes,
529
656
  loadingPaths: loadingPaths,
530
- spinnerFrame: spinnerFrame
657
+ spinnerFrame: spinnerFrame,
658
+ lastModifiedDates: lastModifiedDates
531
659
  })));
532
660
  };
533
661
 
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "tri-cli",
3
- "version": "0.0.4",
3
+ "version": "0.0.52",
4
4
  "description": "Interactive CLI directory tree visualizer with treemap view",
5
5
  "author": "Jared Wilber",
6
6
  "license": "MIT",
7
+ "type": "module",
7
8
  "keywords": [
8
9
  "cli",
9
10
  "tree",
@@ -19,7 +20,6 @@
19
20
  "bin": {
20
21
  "tri": "dist/cli.js"
21
22
  },
22
- "type": "module",
23
23
  "engines": {
24
24
  "node": ">=16"
25
25
  },
@@ -28,20 +28,20 @@
28
28
  "postbuild": "chmod +x dist/cli.js",
29
29
  "dev": "babel --out-dir=dist --watch source",
30
30
  "test": "prettier --check . && xo && ava",
31
- "test:local": "npm run build && tri --dir . -L 2"
31
+ "test:local": "npm run build && node dist/cli.js --dir . -L 2"
32
32
  },
33
33
  "files": [
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
37
37
  "d3-hierarchy": "^3.1.2",
38
- "ink": "^4.1.0",
39
- "meow": "^11.0.0",
40
- "react": "^18.2.0",
41
- "tri-cli": "^0.0.3"
38
+ "ink": "^3.2.0",
39
+ "meow": "^9.0.0",
40
+ "react": "^18.2.0"
42
41
  },
43
42
  "devDependencies": {
44
43
  "@babel/cli": "^7.21.0",
44
+ "@babel/preset-env": "^7.22.0",
45
45
  "@babel/preset-react": "^7.28.5",
46
46
  "@vdemedes/prettier-config": "^2.0.1",
47
47
  "ava": "^5.2.0",
@@ -73,6 +73,15 @@
73
73
  "prettier": "@vdemedes/prettier-config",
74
74
  "babel": {
75
75
  "presets": [
76
+ [
77
+ "@babel/preset-env",
78
+ {
79
+ "targets": {
80
+ "node": "16"
81
+ },
82
+ "modules": false
83
+ }
84
+ ],
76
85
  "@babel/preset-react"
77
86
  ]
78
87
  }
package/readme.tape ADDED
@@ -0,0 +1,65 @@
1
+ Output readme2.gif
2
+ Set FontSize 18
3
+ Set Width 1000
4
+ Set Height 600
5
+ Set Theme "Dracula"
6
+
7
+ Type "# tri: interactive tree + treemap visual"
8
+ Enter
9
+ Sleep 500ms
10
+
11
+ Type "tri ."
12
+ Enter
13
+ Sleep 1s
14
+
15
+ Down
16
+ Sleep 250ms
17
+ Down
18
+ Sleep 250ms
19
+ Down
20
+ Sleep 250ms
21
+ Enter
22
+ Sleep 1s
23
+ Type "d"
24
+ Sleep 1.5s
25
+
26
+ Type "a"
27
+ Sleep 250ms
28
+ Type "e"
29
+ Sleep 500ms
30
+ Enter
31
+ Sleep 1s
32
+
33
+
34
+ Type "a"
35
+ Sleep 250ms
36
+ Type "e"
37
+ Sleep 500ms
38
+ Type "b"
39
+ Sleep 500ms
40
+ Enter
41
+ Sleep 1s
42
+
43
+ # Toggle treemap view
44
+ Type "m"
45
+ Sleep 2s
46
+
47
+ # Return to tree view
48
+ Type "m"
49
+ Sleep 2s
50
+
51
+ Type "a"
52
+ Sleep 250ms
53
+ Type "e"
54
+ Sleep 500ms
55
+ Enter
56
+ Sleep 1s
57
+
58
+
59
+ # Quit
60
+ Type "q"
61
+ Sleep 1s
62
+
63
+ Type "# install with: npm i -g tri-cli"
64
+ Enter
65
+ Sleep 2s