import * as chalk from 'chalk'; import * as cliSpinners from 'cli-spinners'; import {Spinner} from 'cli-spinners'; import {stringWidth, terminal, truncateString} from 'terminal-kit'; const SPINNERS = [ cliSpinners.dots, cliSpinners.dots2, cliSpinners.dots3, cliSpinners.dots4, cliSpinners.dots5, cliSpinners.dots6, cliSpinners.dots7, cliSpinners.dots8, cliSpinners.dots10, cliSpinners.dots11 ]; export class TerminalSpinner { private text = ''; private counter = 0; private interval!: NodeJS.Timeout; private spinner: Spinner; constructor(spinner?: Spinner) { if (spinner) { this.spinner = spinner; } else { this.spinner = SPINNERS[Math.floor(Math.random() * SPINNERS.length)]; } } public start(text: string) { this.text = text; terminal.on('resize', this.onResize).grabInput(true); this.interval = setInterval(() => { this.counter++; terminal.saveCursor(); this.renderSpinner(); terminal.restoreCursor(); }, this.spinner.interval); terminal.hideCursor(); this.render(); return this; } public update(text: string) { this.text = text; this.render(); } public stop() { terminal.off('resize', this.onResize).grabInput(false); clearInterval(this.interval); terminal.hideCursor(false).eraseDisplayBelow(); } private onResize = () => { this.render(); } private renderSpinner() { terminal(` ${chalk.yellow(this.spinner.frames[this.counter % this.spinner.frames.length])} `); } private render() { terminal.eraseDisplayBelow().saveCursor(); let str = this.text; if (stringWidth(this.text) + 3 > terminal.width) { str = truncateString(this.text, terminal.width - 4) + '…'; } this.renderSpinner(); terminal(str).restoreCursor(); } }