├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package-lock.json └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # parcel-bundler cache (https://parceljs.org/) 61 | .cache 62 | 63 | # next.js build output 64 | .next 65 | 66 | # nuxt.js build output 67 | .nuxt 68 | 69 | # vuepress build output 70 | .vuepress/dist 71 | 72 | # Serverless directories 73 | .serverless 74 | 75 | # FuseBox cache 76 | .fusebox/ 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lau Skeeter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tailflix 2 | 3 | drop in replacement for tail -F that asks you if you are still watching 4 | 5 |
screenshot 2018-12-30 at 12 39 05 pm
6 | 7 | ## usage 8 | 9 | ``` 10 | npx tailflix /path/to/log.log 11 | ``` 12 | 13 | ## why 14 | 15 | Im having A Moment 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const Tail = require('tail').Tail; 4 | const chalk = require('chalk'); 5 | const keypress = require('keypress'); 6 | 7 | const getCenter = (total, inner) => Math.ceil((total - inner) / 2); 8 | 9 | const title = name => `Are you still watching "${name}"?`; 10 | const choices = ['Continue watching', 'Exit']; 11 | const buttonSize = choices.sort((a, b) => b.length > a.length)[0].length + 2; 12 | const wrappedChoices = choices.map(choice => { 13 | const diff = getCenter(buttonSize, choice.length); 14 | return [ 15 | ...new Array(diff).fill(' ').join(''), 16 | choice, 17 | ...new Array(diff).fill(' ').join(''), 18 | ].join(''); 19 | }); 20 | 21 | const center = str => 22 | new Array(getCenter(process.stdout.columns, str.length)).fill(' ').join(''); 23 | 24 | const draw = (active, filename) => { 25 | const height = 2 + choices.length; 26 | const paddingTop = getCenter(process.stdout.rows, height); 27 | const paddingBottom = process.stdout.rows - height - paddingTop; 28 | 29 | new Array(paddingTop).fill(' ').forEach(() => { 30 | console.log(''); 31 | }); 32 | 33 | console.log(center(title(filename)) + title(filename)); 34 | console.log(''); 35 | 36 | wrappedChoices.map((choice, i) => { 37 | i === active 38 | ? console.log(center(choice) + chalk.bgWhite.black(choice)) 39 | : console.log(center(choice) + choice); 40 | }); 41 | 42 | new Array(paddingBottom).fill(' ').forEach(() => { 43 | console.log(''); 44 | }); 45 | }; 46 | 47 | const prompt = filename => 48 | new Promise(yay => { 49 | keypress(process.stdin); 50 | 51 | let active = 0; 52 | draw(active, filename); 53 | 54 | process.stdin.on('keypress', function(ch, key) { 55 | if (key && key.name === 'down') { 56 | if (active >= choices.length - 1) { 57 | active = 0; 58 | } else { 59 | active += 1; 60 | } 61 | draw(active, filename); 62 | } 63 | if (key && key.name === 'up') { 64 | if (active <= 0) { 65 | active = choices.length - 1; 66 | } else { 67 | active -= 1; 68 | } 69 | draw(active, filename); 70 | } 71 | if (key && key.name === 'return') { 72 | process.stdin.setRawMode(false); 73 | process.stdout.write('\033c'); 74 | if (active === 0) { 75 | yay(); 76 | } else { 77 | process.exit(); 78 | } 79 | } 80 | if (key && key.ctrl && key.name == 'c') { 81 | process.stdin.setRawMode(false); 82 | } 83 | }); 84 | 85 | process.stdin.setRawMode(true); 86 | process.stdin.resume(); 87 | }); 88 | 89 | const log = file => { 90 | const tail = new Tail(file); 91 | 92 | let count = 0; 93 | let buffer = []; 94 | 95 | tail.on('line', function(data) { 96 | count++; 97 | if (count === 4) { 98 | buffer.push(data); 99 | prompt(file).then(() => { 100 | buffer.forEach(line => { 101 | console.log(line); 102 | }); 103 | buffer = []; 104 | count = 0; 105 | }); 106 | } else if (count >= 4) { 107 | buffer.push(data); 108 | } else { 109 | console.log(data); 110 | } 111 | }); 112 | 113 | tail.on('error', function(error) { 114 | console.log('ERROR: ', error); 115 | }); 116 | }; 117 | 118 | log(process.argv[process.argv.length - 1]); 119 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tailflix", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-styles": { 8 | "version": "3.2.1", 9 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 10 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 11 | "requires": { 12 | "color-convert": "^1.9.0" 13 | } 14 | }, 15 | "chalk": { 16 | "version": "2.4.1", 17 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 18 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 19 | "requires": { 20 | "ansi-styles": "^3.2.1", 21 | "escape-string-regexp": "^1.0.5", 22 | "supports-color": "^5.3.0" 23 | } 24 | }, 25 | "color-convert": { 26 | "version": "1.9.3", 27 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 28 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 29 | "requires": { 30 | "color-name": "1.1.3" 31 | } 32 | }, 33 | "color-name": { 34 | "version": "1.1.3", 35 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 36 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 37 | }, 38 | "escape-string-regexp": { 39 | "version": "1.0.5", 40 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 41 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 42 | }, 43 | "has-flag": { 44 | "version": "3.0.0", 45 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 46 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 47 | }, 48 | "keypress": { 49 | "version": "0.2.1", 50 | "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.2.1.tgz", 51 | "integrity": "sha1-HoBFQlABjbrUw/6USX1uZ7YmnHc=" 52 | }, 53 | "minimist": { 54 | "version": "1.2.0", 55 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 56 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 57 | }, 58 | "supports-color": { 59 | "version": "5.5.0", 60 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 61 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 62 | "requires": { 63 | "has-flag": "^3.0.0" 64 | } 65 | }, 66 | "tail": { 67 | "version": "2.0.2", 68 | "resolved": "https://registry.npmjs.org/tail/-/tail-2.0.2.tgz", 69 | "integrity": "sha512-raFipiKWdGKEzxbvZwnhUGqjvsv0gpa/1A479rL//NOxnNwYZDN4MPk6xJJdUFs8P2Xrff3nbH5fcyYRLU4UHQ==" 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tailflix", 3 | "version": "1.0.1", 4 | "description": "tail but asks you if youre still watching", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "bin": { 10 | "tailflix": "index.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/walaura/tailflix.git" 15 | }, 16 | "author": "lau mayhem", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/walaura/tailflix/issues" 20 | }, 21 | "homepage": "https://github.com/walaura/tailflix#readme", 22 | "dependencies": { 23 | "chalk": "^2.4.1", 24 | "keypress": "^0.2.1", 25 | "minimist": "^1.2.0", 26 | "tail": "^2.0.2" 27 | } 28 | } 29 | --------------------------------------------------------------------------------