11#!/usr/bin/env node
22
3- const fs = require ( 'fs' ) ;
4- const path = require ( 'path' ) ;
5- const readline = require ( 'readline' ) ;
3+ const MdTail = require ( './lib/mdtail' ) ;
64
7- // Parse command-line arguments
5+ // Create instance and run
6+ const mdtail = new MdTail ( ) ;
87const args = process . argv . slice ( 2 ) ;
9- let files = [ ] ;
10- let currentTabIndex = 0 ;
118
12- // Handle help flag
13- if ( args . includes ( '-h' ) || args . includes ( '--help' ) ) {
14- console . log ( `
15- mdtail - Terminal markdown viewer with live refresh
16-
17- Usage:
18- mdtail [file1.md] [file2.md] ... Watch specific markdown files
19- mdtail Watch TODO.md (default)
20- mdtail -h, --help Show this help message
21-
22- Navigation:
23- ← / → Arrow Keys Switch between tabs
24- Ctrl+C Exit
25-
26- Examples:
27- mdtail README.md Watch README.md
28- mdtail todo.md notes.md Watch multiple files with tabs
29- mdtail *.md Watch all markdown files in tabs
30- ` ) ;
31- process . exit ( 0 ) ;
32- }
33-
34- // Get markdown files from arguments or default to todo.md
35- if ( args . length > 0 ) {
36- // Expand wildcards and resolve paths
37- args . forEach ( arg => {
38- if ( arg . includes ( '*' ) ) {
39- // Handle wildcards
40- const glob = require ( 'fs' ) . readdirSync ( process . cwd ( ) )
41- . filter ( file => file . endsWith ( '.md' ) )
42- . sort ( ) ; // Sort files alphabetically
43- files . push ( ...glob . map ( f => path . resolve ( f ) ) ) ;
44- } else {
45- // Resolve individual file paths
46- const filePath = path . resolve ( arg ) ;
47- if ( fs . existsSync ( filePath ) && filePath . endsWith ( '.md' ) ) {
48- files . push ( filePath ) ;
49- } else if ( ! filePath . endsWith ( '.md' ) ) {
50- console . error ( `Warning: ${ arg } is not a markdown file` ) ;
51- } else {
52- console . error ( `Warning: ${ arg } not found` ) ;
53- }
54- }
55- } ) ;
56- } else {
57- // Default to TODO.md in current directory
58- const defaultFile = path . join ( process . cwd ( ) , 'TODO.md' ) ;
59- if ( fs . existsSync ( defaultFile ) ) {
60- files . push ( defaultFile ) ;
61- }
62- }
63-
64- // Remove duplicates
65- files = [ ...new Set ( files ) ] ;
66-
67- if ( files . length === 0 ) {
68- console . error ( 'Error: No markdown files found to watch' ) ;
69- console . log ( 'Run "mdtail --help" for usage information' ) ;
70- process . exit ( 1 ) ;
71- }
72-
73- function clearScreen ( ) {
74- console . clear ( ) ;
75- }
76-
77- function renderTabs ( width ) {
78- if ( files . length === 1 ) return '' ;
79-
80- const tabs = files . map ( ( file , index ) => {
81- const filename = path . basename ( file ) ;
82- const isActive = index === currentTabIndex ;
83-
84- if ( isActive ) {
85- return `[${ filename } ]` ;
86- } else {
87- return ` ${ filename } ` ;
88- }
89- } ) ;
90-
91- const tabLine = tabs . join ( ' │ ' ) ;
92- const navigation = ' ← → Navigate tabs' ;
93-
94- return `${ tabLine } \n${ '─' . repeat ( width ) } \n` ;
95- }
96-
97- function displayCurrentFile ( ) {
98- clearScreen ( ) ;
99- process . stdout . write ( '\x1B[?25l' ) ; // Hide cursor
100-
101- const width = process . stdout . columns || 80 ;
102- const currentFile = files [ currentTabIndex ] ;
103-
104- try {
105- const content = fs . readFileSync ( currentFile , 'utf8' ) ;
106- const filename = path . basename ( currentFile ) ;
107-
108- // Header
109- console . log ( '\n' + '═' . repeat ( width ) ) ;
110-
111- // Tabs (if multiple files)
112- if ( files . length > 1 ) {
113- console . log ( renderTabs ( width ) ) ;
114- } else {
115- console . log ( filename . toUpperCase ( ) ) ;
116- console . log ( '═' . repeat ( width ) + '\n' ) ;
117- }
118-
119- // Content
120- console . log ( content ) ;
121-
122- // Footer
123- console . log ( '\n' + '═' . repeat ( width ) ) ;
124-
125- if ( files . length > 1 ) {
126- console . log ( `Tab ${ currentTabIndex + 1 } of ${ files . length } │ ← → Navigate │ Ctrl+C Exit` ) ;
127- } else {
128- console . log ( 'Watching for changes... (Ctrl+C to exit)' ) ;
129- }
130- } catch ( error ) {
131- console . error ( `Error reading ${ currentFile } :` , error . message ) ;
132- }
133- }
134-
135- function setupKeyboardNavigation ( ) {
136- // Skip keyboard setup if not in TTY mode
137- if ( ! process . stdin . isTTY ) {
138- return ;
139- }
140-
141- readline . emitKeypressEvents ( process . stdin ) ;
142- process . stdin . setRawMode ( true ) ;
143- process . stdin . resume ( ) ; // Start reading from stdin
144-
145- process . stdin . on ( 'keypress' , ( str , key ) => {
146- if ( key && key . ctrl && key . name === 'c' ) {
147- // Exit
148- process . stdin . setRawMode ( false ) ;
149- process . stdout . write ( '\x1B[?25h' ) ; // Show cursor again
150- console . log ( '\n\nStopping mdtail...' ) ;
151- process . exit ( 0 ) ;
152- }
153-
154- if ( files . length > 1 ) {
155- if ( key && key . name === 'left' ) {
156- // Previous tab
157- currentTabIndex = ( currentTabIndex - 1 + files . length ) % files . length ;
158- displayCurrentFile ( ) ;
159- } else if ( key && key . name === 'right' ) {
160- // Next tab
161- currentTabIndex = ( currentTabIndex + 1 ) % files . length ;
162- displayCurrentFile ( ) ;
163- }
164- }
165- } ) ;
166- }
167-
168- function startWatching ( ) {
169- // Setup keyboard navigation
170- setupKeyboardNavigation ( ) ;
171-
172- // Initial display
173- displayCurrentFile ( ) ;
174-
175- // Watch each file
176- files . forEach ( ( file , index ) => {
177- fs . watchFile ( file , { interval : 100 } , ( curr , prev ) => {
178- if ( curr . mtime !== prev . mtime ) {
179- // Only redraw if we're viewing the changed file or if there's only one file
180- if ( index === currentTabIndex || files . length === 1 ) {
181- displayCurrentFile ( ) ;
182- }
183- }
184- } ) ;
185- } ) ;
186-
187- // Show initial file list
188- if ( files . length > 1 ) {
189- console . log ( `\nWatching ${ files . length } files. Use arrow keys to navigate.` ) ;
190- setTimeout ( ( ) => {
191- displayCurrentFile ( ) ;
192- } , 1500 ) ;
193- }
194- }
195-
196- // Handle exit gracefully
197- process . on ( 'SIGINT' , ( ) => {
198- if ( process . stdin . isTTY ) {
199- process . stdin . setRawMode ( false ) ;
200- }
201- process . stdout . write ( '\x1B[?25h' ) ; // Show cursor again
202- console . log ( '\n\nStopping mdtail...' ) ;
203- process . exit ( 0 ) ;
204- } ) ;
205-
206- // Start watching the files
207- startWatching ( ) ;
9+ mdtail . run ( args ) ;
0 commit comments