├── .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 |

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 |
--------------------------------------------------------------------------------