├── resources ├── demo.gif └── confirmation.png ├── examples ├── simple.js ├── title.js ├── custom-buttons.js └── download.js ├── yarn.lock ├── package.json ├── LICENSE ├── .gitignore ├── README.md └── src └── index.js /resources/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f/confirmation/HEAD/resources/demo.gif -------------------------------------------------------------------------------- /resources/confirmation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f/confirmation/HEAD/resources/confirmation.png -------------------------------------------------------------------------------- /examples/simple.js: -------------------------------------------------------------------------------- 1 | const confirmation = require('..'); 2 | 3 | (async () => { 4 | if (await confirmation('Are you sure?')) { 5 | console.log('Yay!'); 6 | } else { 7 | console.log('Oops!'); 8 | } 9 | })(); 10 | -------------------------------------------------------------------------------- /examples/title.js: -------------------------------------------------------------------------------- 1 | const confirmation = require('../src'); 2 | 3 | (async () => { 4 | if (await confirmation('Are you sure?', 'Are you sure to confirm what you do?')) { 5 | console.log('Yay!'); 6 | } else { 7 | console.log('Oops!'); 8 | } 9 | })(); 10 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | blessed@^0.1.81: 6 | version "0.1.81" 7 | resolved "https://registry.yarnpkg.com/blessed/-/blessed-0.1.81.tgz#f962d687ec2c369570ae71af843256e6d0ca1129" 8 | integrity sha1-+WLWh+wsNpVwrnGvhDJW5tDKESk= 9 | -------------------------------------------------------------------------------- /examples/custom-buttons.js: -------------------------------------------------------------------------------- 1 | const confirmation = require('../src'); 2 | 3 | (async () => { 4 | if (await await confirmation( 5 | 'Warning', 6 | 'You will delete your items, are you sure?', 7 | 'I am sure', 8 | 'No!' 9 | )) { 10 | console.log('Yay!'); 11 | } else { 12 | console.log('Oops!'); 13 | } 14 | })(); 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "confirmation", 3 | "version": "0.0.5", 4 | "description": "A confirmation window for Node scripts.", 5 | "main": "src/index.js", 6 | "devDependencies": {}, 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "files": [ 11 | "src/index.js" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/f/confirmation.git" 16 | }, 17 | "keywords": [ 18 | "confirm", 19 | "node", 20 | "confirmation", 21 | "popup" 22 | ], 23 | "author": "Fatih Kadir Akin ", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/f/confirmation/issues" 27 | }, 28 | "homepage": "https://github.com/f/confirmation#readme", 29 | "dependencies": { 30 | "blessed": "^0.1.81" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/download.js: -------------------------------------------------------------------------------- 1 | const confirmation = require('../src'); 2 | 3 | const sleep = delay => new Promise(r => setTimeout(r, delay * 1000)); 4 | 5 | (async () => { 6 | process.stdout.write('Downloading internet...'); 7 | await sleep(1); 8 | process.stdout.write('.'); 9 | await sleep(0.2); 10 | process.stdout.write('.'); 11 | await sleep(0.1); 12 | process.stdout.write('.'); 13 | await sleep(0.1); 14 | process.stdout.write('.'); 15 | await sleep(0.1); 16 | process.stdout.write('.'); 17 | await sleep(0.1); 18 | process.stdout.write('.\n'); 19 | await sleep(3); 20 | console.log('Downloaded to file internet.zip!'); 21 | await sleep(2); 22 | process.stdout.write('.\n'); 23 | if (await confirmation( 24 | 'Extract internet.zip?', 25 | 'The internet.zip file will be extracted to your computer, are you sure?', 26 | 'Yes, I am', 27 | 'Nope' 28 | )) { 29 | console.log('You\'ve extracted the internet.zip!'); 30 | } else { 31 | console.log('Internet won\'t be extracted.'); 32 | } 33 | })(); 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Fatih Kadir Akın 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. 22 | -------------------------------------------------------------------------------- /.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 | .env.test 60 | 61 | # parcel-bundler cache (https://parceljs.org/) 62 | .cache 63 | 64 | # next.js build output 65 | .next 66 | 67 | # nuxt.js build output 68 | .nuxt 69 | 70 | # vuepress build output 71 | .vuepress/dist 72 | 73 | # Serverless directories 74 | .serverless/ 75 | 76 | # FuseBox cache 77 | .fusebox/ 78 | 79 | # DynamoDB Local files 80 | .dynamodb/ 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⁉️ confirmation 2 | 3 | A simple Node tool to have Browser's `confirm` popup on your CLI. 4 | 5 | ![confirmation](resources/demo.gif?v1) 6 | 7 | ## Installation 8 | 9 | ``` 10 | yarn add confirmation 11 | ``` 12 | 13 | ## Usage 14 | 15 | In an `async` function just call `await confirmation`. 16 | 17 | ```js 18 | if (await confirmation('Are you sure?')) { 19 | console.log('Yay!'); 20 | } else { 21 | console.log('Oops!'); 22 | } 23 | ``` 24 | 25 | ![confirmation](resources/confirmation.png?v1) 26 | 27 | ## API 28 | 29 | ### `await confirmation(message) : bool` 30 | 31 | Shows a message and `OK` and `Cancel` buttons. 32 | 33 | _Example_: 34 | ```js 35 | const result = await confirmation('You will delete your items, are you sure?') 36 | ``` 37 | 38 | ### `await confirmation(title, message) : bool` 39 | 40 | Shows a message with a title and `OK` and `Cancel` buttons. 41 | 42 | _Example_: 43 | ```js 44 | const result = await confirmation('Warning', 'You will delete your items, are you sure?') 45 | ``` 46 | 47 | ### `await confirmation(title, message, ok, cancel) : bool` 48 | 49 | Shows a message with a title and custom `OK` and custom `Cancel` buttons. 50 | 51 | _Example_: 52 | ```js 53 | const result = await confirmation( 54 | 'Warning', 55 | 'You will delete your items, are you sure?', 56 | 'I am sure', 57 | 'No!' 58 | ) 59 | ``` 60 | 61 | ## The Key bindings 62 | 63 | The Y for **OK** and N for **Cancel** are the default key bindings. 64 | 65 | When you customize your **OK** and **Cancel** buttons, the **first letter** will be the key binding. If you make **I am Sure** instead of **OK**, you'll need to press I to confirm. 66 | 67 | ### Example 1 68 | _Example_: 69 | ```js 70 | const result = await confirmation( 71 | 'Warning', 72 | 'You will delete your items, are you sure?', 73 | '[O] OK', 74 | '[X] Close' 75 | ) 76 | ``` 77 | Keymap: 78 | - O to Confirm 79 | - X to Close 80 | 81 | ### Example 2 82 | _Example_: 83 | ```js 84 | const result = await confirmation( 85 | 'Warnin\'', 86 | 'Ye will scuttle yer items, are ye sure?', 87 | 'Aye', 88 | 'No' 89 | ) 90 | ``` 91 | Keymap: 92 | - A (_A_ye) to Confirm 93 | - N (_N_o) to Close 94 | 95 | ## License 96 | MIT © Fatih Kadir Akin 97 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const blessed = require("blessed"); 2 | 3 | const shortcut = text => text.match(/\[?(\w)\]?/)[1].toLowerCase(); 4 | const shortcutifyText = text => { 5 | const key = shortcut(text); 6 | return text.replace( 7 | new RegExp(key, "i"), 8 | `{underline}${key.toUpperCase()}{/underline}` 9 | ); 10 | }; 11 | 12 | function confirm(title, message, okText = "[Y] OK", cancelText = "[N] Cancel") { 13 | if (!message) { 14 | message = title; 15 | title = ''; 16 | } 17 | var screen = blessed.screen({ 18 | smartCSR: true, 19 | title: title || message, 20 | fullUnicode: true 21 | }); 22 | 23 | var form = blessed.form({ 24 | keys: true, 25 | top: "center", 26 | left: "center", 27 | width: "50%", 28 | height: "shrink", 29 | content: `${title ? `{bold}${title}{/bold}\n\n` : ''}${message}\n\n\n`, 30 | tags: true, 31 | border: { 32 | type: "bg" 33 | }, 34 | padding: { left: 2, right: 2, top: 1, bottom: 1 }, 35 | style: { 36 | bg: "#cccccc", 37 | fg: "black", 38 | border: { 39 | fg: "red" 40 | } 41 | } 42 | }); 43 | 44 | var submit = blessed.button({ 45 | parent: form, 46 | mouse: true, 47 | keys: true, 48 | shrink: true, 49 | tags: true, 50 | right: 0, 51 | bottom: 0, 52 | name: "submit", 53 | content: shortcutifyText(okText), 54 | padding: { left: 2, right: 2, top: 1, bottom: 1 }, 55 | style: { 56 | bg: "green", 57 | focus: { bg: "cyan" }, 58 | hover: { bg: "cyan" } 59 | } 60 | }); 61 | 62 | var cancel = blessed.button({ 63 | parent: form, 64 | mouse: true, 65 | keys: true, 66 | shrink: true, 67 | tags: true, 68 | right: 69 | submit.content.length + 70 | submit.padding.left + 71 | submit.padding.right + 72 | 1 - 73 | "{underline}{/underline}".length, 74 | bottom: 0, 75 | name: "cancel", 76 | content: shortcutifyText(cancelText), 77 | padding: { left: 2, right: 2, top: 1, bottom: 1 }, 78 | style: { 79 | bg: "red", 80 | focus: { bg: "cyan" }, 81 | hover: { bg: "cyan" } 82 | } 83 | }); 84 | 85 | const makeDecision = (resolver, value) => { 86 | screen.destroy(); 87 | resolver(value); 88 | }; 89 | 90 | return new Promise(resolve => { 91 | submit.on("press", () => makeDecision(resolve, true)); 92 | cancel.on("press", () => makeDecision(resolve, false)); 93 | screen.key([shortcut(okText)], () => makeDecision(resolve, true)); 94 | screen.key([shortcut(cancelText)], () => makeDecision(resolve, false)); 95 | screen.key(["escape", "q", "C-c"], () => makeDecision(resolve, false)); 96 | screen.append(form); 97 | form.focus(); 98 | screen.render(); 99 | }); 100 | } 101 | 102 | module.exports = confirm; 103 | --------------------------------------------------------------------------------