multi-progress-bars
Advanced tools
Comparing version 1.1.4 to 2.0.1
@@ -5,2 +5,20 @@ # Changelog | ||
### [2.0.1](https://github.com/kamiyo/multi-progress-bars/compare/v2.0.0...v2.0.1) (2020-08-30) | ||
### Bug Fixes | ||
* update demo gif ([81343fe](https://github.com/kamiyo/multi-progress-bars/commit/81343febc0b3c366584b6c0bacab1439814cbcf9)) | ||
## [2.0.0](https://github.com/kamiyo/multi-progress-bars/compare/v1.1.4...v2.0.0) (2020-08-30) | ||
### ⚠ BREAKING CHANGES | ||
* **behavior:** Overrides console so that console.logs can be preserved for scrollback. This requires a virtual-console class that manages fixed and dynamic rows. API technically hasn't changed, but marked as breaking since behavior is changed. | ||
### Features | ||
* **behavior:** Overrides console so that console.logs can be preserved for scrollback. This requires a virtual-console class that manages fixed and dynamic rows. API technically hasn't changed, but marked as breaking since behavior is changed. ([e045917](https://github.com/kamiyo/multi-progress-bars/commit/e0459174995cb17d8e0bb21d4ee09da53a089d00)) | ||
### [1.1.4](https://github.com/kamiyo/multi-progress-bars/compare/v1.1.3...v1.1.4) (2020-07-29) | ||
@@ -7,0 +25,0 @@ |
@@ -10,2 +10,3 @@ 'use strict'; | ||
var stringWidth = _interopDefault(require('string-width')); | ||
var util = require('util'); | ||
@@ -39,2 +40,144 @@ /*! ***************************************************************************** | ||
const ESC = '\x1B'; | ||
const CSI = ESC + '['; | ||
const RESET = CSI + '0m'; | ||
const numberTo1StringHelper = (number) => (number !== undefined) ? (number + 1).toFixed(0) : ''; | ||
/** CUrsor Position | ||
* | ||
* @param row (required) 0-index absolute row | ||
* @param column (optional) 0-index absolute column | ||
*/ | ||
const CUP = (row, column) => CSI | ||
+ numberTo1StringHelper(row) | ||
+ ';' | ||
+ numberTo1StringHelper(column) | ||
+ 'H'; | ||
var EL_MODE; | ||
(function (EL_MODE) { | ||
EL_MODE[EL_MODE["TO_END"] = 0] = "TO_END"; | ||
EL_MODE[EL_MODE["TO_BEGINNING"] = 1] = "TO_BEGINNING"; | ||
EL_MODE[EL_MODE["ENTIRE_LINE"] = 2] = "ENTIRE_LINE"; | ||
})(EL_MODE || (EL_MODE = {})); | ||
const EL = (mode = EL_MODE.TO_END) => { | ||
return CSI + mode.toString() + 'K'; | ||
}; | ||
var ED_MODE; | ||
(function (ED_MODE) { | ||
ED_MODE[ED_MODE["TO_END"] = 0] = "TO_END"; | ||
ED_MODE[ED_MODE["TO_BEGINNING"] = 1] = "TO_BEGINNING"; | ||
ED_MODE[ED_MODE["ENTIRE_SCREEN"] = 2] = "ENTIRE_SCREEN"; | ||
ED_MODE[ED_MODE["ENTIRE_SCREEN_DELETE_SCROLLBACK"] = 3] = "ENTIRE_SCREEN_DELETE_SCROLLBACK"; | ||
})(ED_MODE || (ED_MODE = {})); | ||
const ED = (mode = ED_MODE.TO_END) => { | ||
return CSI + mode.toString() + 'J'; | ||
}; | ||
// Always puts a reset ANSI escape code, just in case it was stripped. | ||
// Anyways, probably don't want any styling codes to linger past one line. | ||
const clampString = (message, width) => { | ||
while (stringWidth(message) > width) { | ||
// Can't be sure we are slicing off a character vs a control sequence or colors | ||
// so do it this way, checking each time. | ||
message = message.slice(0, message.length - 1); | ||
} | ||
return message + RESET; | ||
}; | ||
class VirtualConsole { | ||
constructor(options) { | ||
this.originalConsole = console; | ||
this.stream = options.stream; | ||
this.width = process.stdout.columns; | ||
this.height = process.stdout.rows - 1; | ||
this.progressHeight = 0; | ||
this.consoleHeight = this.height; | ||
this.progressBuffer = []; | ||
this.consoleBuffer = []; | ||
console = this; | ||
this.init(); | ||
} | ||
cleanup() { | ||
this.stream.write('\x1b[0m'); | ||
} | ||
init() { | ||
process.on('SIGINT', this.cleanup); | ||
const blank = '\n'.repeat(this.stream.rows) + CUP(0) + ED(ED_MODE.TO_END); | ||
this.stream.write(blank); | ||
} | ||
/** Add or Update Progress Entry | ||
* | ||
* @param options | ||
* index: number | ||
* data: string | ||
*/ | ||
upsertProgress(options) { | ||
if (options.index < this.progressHeight) { | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
if (options.refresh === undefined || options.refresh) | ||
this.refresh(); | ||
return; | ||
} | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
const numToExtend = 1 + options.index - this.progressHeight; | ||
this.progressHeight = Math.max(options.index + 1, this.progressHeight); | ||
// this.consoleHeight = this.height - this.progressHeight; | ||
if (numToExtend > 0) { | ||
this.consoleHeight -= numToExtend; | ||
const topLines = this.consoleBuffer.splice(0, numToExtend); | ||
if (topLines.length) { | ||
this.log(...topLines); | ||
} | ||
else { | ||
this.refresh(); | ||
} | ||
} | ||
else { | ||
this.refresh(); | ||
} | ||
} | ||
updateProgress(options) { | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
if (options.refresh) { | ||
this.refresh(); | ||
} | ||
} | ||
writeLines(...indexes) { | ||
let writeString = indexes.reduce((prev, index) => { | ||
return prev += CUP(index) + this.progressBuffer[index]; | ||
}, ''); | ||
this.stream.write(writeString); | ||
} | ||
refresh() { | ||
const outString = CUP(0) | ||
+ this.progressBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ (this.progressBuffer.length ? '\n' : '') | ||
+ this.consoleBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ '\n'; | ||
this.stream.write(outString); | ||
} | ||
log(...data) { | ||
const writeString = util.format.apply(null, data); | ||
const clampedLines = writeString.split('\n').reduce((prev, curr) => { | ||
const clamped = []; | ||
do { | ||
const front = curr.slice(0, this.width); | ||
curr = curr.slice(this.width); | ||
clamped.push(front); | ||
} while (curr.length > this.width); | ||
return [...prev, ...clamped]; | ||
}, []); | ||
this.consoleBuffer.push(...clampedLines); | ||
const topLines = (this.consoleBuffer.length > this.consoleHeight) ? | ||
this.consoleBuffer.splice(0, this.consoleBuffer.length - this.consoleHeight) : []; | ||
const outString = CUP(0) | ||
+ topLines.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ (topLines.length ? '\n' : '') | ||
+ this.progressBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ (this.progressBuffer.length ? '\n' : '') | ||
+ this.consoleBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ '\n'; | ||
this.stream.write(outString); | ||
} | ||
} | ||
const defaultTransformFn = (s) => s; | ||
class MultiProgressBars { | ||
@@ -54,7 +197,23 @@ /** | ||
this.FULL_CHAR = this.CHARS[this.CHARS.length - 1]; | ||
this.endLine = 0; | ||
this.longestNameLength = 0; | ||
this.t = 0; | ||
this.logger = new VirtualConsole({ stream: process.stdout }); | ||
this.resizeConsole = () => { | ||
this.consoleSize = { | ||
width: this.stream.columns, | ||
height: this.stream.rows, | ||
}; | ||
}; | ||
this.cleanup = () => { | ||
if (this.intervalID) { | ||
clearInterval(this.intervalID); | ||
} | ||
}; | ||
const { stream = process.stdout, spinnerFPS = 10, spinnerGenerator = this.hilbertSpinner, } = options || {}; | ||
let { progressWidth = 40, numCrawlers = 4, initMessage, } = options || {}; | ||
this.stream = stream; | ||
this.stream.on('resize', () => { | ||
this.resizeConsole(); | ||
}); | ||
this.spinnerFPS = Math.min(spinnerFPS, 60); | ||
@@ -75,2 +234,3 @@ this.spinnerGenerator = spinnerGenerator; | ||
this.progressWidth = progressWidth; | ||
this.resizeConsole(); | ||
if (initMessage === undefined) { | ||
@@ -85,21 +245,16 @@ initMessage = '$ ' + process.argv.map((arg) => { | ||
// setup cleanup | ||
process.on('SIGINT', () => { | ||
if (this.intervalID) { | ||
clearInterval(this.intervalID); | ||
} | ||
process.on('SIGINT', this.cleanup); | ||
// TODO make this account for lines that wrap | ||
const splitMessage = message.split('\n'); | ||
splitMessage.forEach((msg, idx) => { | ||
this.logger.upsertProgress({ | ||
index: idx, | ||
data: msg, | ||
}); | ||
}); | ||
const splitMessage = message.split('\n').map((str) => stringWidth(str)); | ||
const cols = this.stream.columns; | ||
const eachCols = splitMessage.map((msg) => Math.ceil(msg / cols)); | ||
this.initialLines = eachCols.reduce((prev, curr) => prev + curr, 0); | ||
const blank = '\n'.repeat(this.stream.rows); | ||
this.stream.write(blank); | ||
this.stream.cursorTo(0); | ||
this.stream.clearScreenDown(); | ||
this.stream.write(message); | ||
const blankMinInit = '\n'.repeat(this.stream.rows - this.initialLines); | ||
this.stream.write(blankMinInit); | ||
this.initialLines = splitMessage.length; | ||
} | ||
addTask(name, _a) { | ||
var { index } = _a, options = __rest(_a, ["index"]); | ||
// if task exists, update fields | ||
if (this.tasks[name] !== undefined) { | ||
@@ -110,3 +265,8 @@ Object.keys(options).forEach((key) => options[key] === undefined && delete options[key]); | ||
else { | ||
const { type, barColorFn = (s) => s, percentage = 0, message = '', } = options; | ||
// otherwise make a new task | ||
const { type, barColorFn = defaultTransformFn, percentage = 0, message = '', } = options; | ||
this.endLine = Math.max.apply(null, [ | ||
index, | ||
...Object.entries(this.tasks).map(([_, task]) => task.index) | ||
]) + 1; | ||
this.tasks[name] = { | ||
@@ -118,3 +278,3 @@ type, | ||
name, | ||
index: (index === undefined) ? Object.entries(this.tasks).length : index, | ||
index, | ||
done: false, | ||
@@ -144,7 +304,5 @@ }; | ||
}); | ||
// Go to bottom of tasks and clear downwards. | ||
this.stream.cursorTo(0, Object.entries(this.tasks).length + this.initialLines); | ||
this.stream.clearScreenDown(); | ||
} | ||
progressString(name, percentage, message, barColorFn = (b) => b) { | ||
progressString(task) { | ||
const { name, barColorFn, message, percentage, } = task; | ||
// scale progress bar to percentage of total width | ||
@@ -180,7 +338,11 @@ const scaled = percentage * this.progressWidth; | ||
} | ||
indefiniteString(task, spinner) { | ||
const { name, barColorFn, message, } = task; | ||
return name.padStart(this.longestNameLength) + ': ' + barColorFn(spinner) + ' ' + message; | ||
} | ||
writeTask(task) { | ||
this.stream.cursorTo(0, this.initialLines + task.index); | ||
this.stream.write(this.progressString(task.name, task.percentage, task.message, task.barColorFn).slice(0, this.stream.columns)); | ||
this.stream.clearLine(1); | ||
this.stream.cursorTo(0, Object.entries(this.tasks).length + this.initialLines); | ||
this.logger.upsertProgress({ | ||
index: task.index + this.initialLines, | ||
data: this.progressString(task), | ||
}); | ||
} | ||
@@ -227,8 +389,7 @@ incrementTask(name, _a = {}) { | ||
const task = this.tasks[name]; | ||
this.stream.cursorTo(0, this.initialLines + task.index); | ||
const bar = task.barColorFn(this.FULL_CHAR.repeat(this.progressWidth)); | ||
// TODO customizable format. Maybe unify this with writeTask | ||
this.stream.write(name.padStart(this.longestNameLength) + ': ' + bar + ' ' + message); | ||
this.stream.clearLine(1); | ||
this.stream.cursorTo(0, Object.entries(this.tasks).length + this.initialLines); | ||
this.logger.upsertProgress({ | ||
index: task.index + this.initialLines, | ||
data: name.padStart(this.longestNameLength) + ': ' + bar + ' ' + message, | ||
}); | ||
// Stop animation if all tasks are done, and resolve the promise. | ||
@@ -286,9 +447,13 @@ if (Object.entries(this.tasks).reduce((prev, [_, curr]) => { | ||
const spinner = this.spinnerGenerator(this.t, this.progressWidth); | ||
Object.entries(this.tasks).forEach(([name, task]) => { | ||
Object.entries(this.tasks).forEach(([_, task]) => { | ||
if (task.type === 'indefinite' && task.done === false) { | ||
this.stream.cursorTo(0, this.initialLines + task.index); | ||
this.stream.write(name.padStart(this.longestNameLength) + ': ' + task.barColorFn(spinner) + ' ' + task.message); | ||
this.stream.clearLine(1); | ||
let progressString = this.indefiniteString(task, spinner); | ||
this.logger.upsertProgress({ | ||
index: task.index + this.initialLines, | ||
data: progressString, | ||
refresh: false, | ||
}); | ||
} | ||
}); | ||
this.logger.refresh(); | ||
this.t = this.t + 1; | ||
@@ -295,0 +460,0 @@ } |
@@ -61,2 +61,4 @@ /// <reference types="node" /> | ||
private FULL_CHAR; | ||
private consoleSize; | ||
private endLine; | ||
private intervalID; | ||
@@ -68,2 +70,3 @@ private numCrawlers; | ||
private spinnerGenerator; | ||
private logger; | ||
promise: Promise<void>; | ||
@@ -75,5 +78,8 @@ /** | ||
constructor(options?: Partial<CtorOptions>); | ||
private resizeConsole; | ||
cleanup: () => void; | ||
private init; | ||
addTask(name: string, { index, ...options }: Omit<Task, 'name' | 'done' | 'message'> & Partial<Pick<Task, 'message'>>): void; | ||
private progressString; | ||
private indefiniteString; | ||
private writeTask; | ||
@@ -80,0 +86,0 @@ incrementTask(name: string, { percentage, ...options }?: UpdateOptions): void; |
import { green } from 'chalk'; | ||
import { parse } from 'path'; | ||
import stringWidth from 'string-width'; | ||
import { format } from 'util'; | ||
@@ -32,2 +33,144 @@ /*! ***************************************************************************** | ||
const ESC = '\x1B'; | ||
const CSI = ESC + '['; | ||
const RESET = CSI + '0m'; | ||
const numberTo1StringHelper = (number) => (number !== undefined) ? (number + 1).toFixed(0) : ''; | ||
/** CUrsor Position | ||
* | ||
* @param row (required) 0-index absolute row | ||
* @param column (optional) 0-index absolute column | ||
*/ | ||
const CUP = (row, column) => CSI | ||
+ numberTo1StringHelper(row) | ||
+ ';' | ||
+ numberTo1StringHelper(column) | ||
+ 'H'; | ||
var EL_MODE; | ||
(function (EL_MODE) { | ||
EL_MODE[EL_MODE["TO_END"] = 0] = "TO_END"; | ||
EL_MODE[EL_MODE["TO_BEGINNING"] = 1] = "TO_BEGINNING"; | ||
EL_MODE[EL_MODE["ENTIRE_LINE"] = 2] = "ENTIRE_LINE"; | ||
})(EL_MODE || (EL_MODE = {})); | ||
const EL = (mode = EL_MODE.TO_END) => { | ||
return CSI + mode.toString() + 'K'; | ||
}; | ||
var ED_MODE; | ||
(function (ED_MODE) { | ||
ED_MODE[ED_MODE["TO_END"] = 0] = "TO_END"; | ||
ED_MODE[ED_MODE["TO_BEGINNING"] = 1] = "TO_BEGINNING"; | ||
ED_MODE[ED_MODE["ENTIRE_SCREEN"] = 2] = "ENTIRE_SCREEN"; | ||
ED_MODE[ED_MODE["ENTIRE_SCREEN_DELETE_SCROLLBACK"] = 3] = "ENTIRE_SCREEN_DELETE_SCROLLBACK"; | ||
})(ED_MODE || (ED_MODE = {})); | ||
const ED = (mode = ED_MODE.TO_END) => { | ||
return CSI + mode.toString() + 'J'; | ||
}; | ||
// Always puts a reset ANSI escape code, just in case it was stripped. | ||
// Anyways, probably don't want any styling codes to linger past one line. | ||
const clampString = (message, width) => { | ||
while (stringWidth(message) > width) { | ||
// Can't be sure we are slicing off a character vs a control sequence or colors | ||
// so do it this way, checking each time. | ||
message = message.slice(0, message.length - 1); | ||
} | ||
return message + RESET; | ||
}; | ||
class VirtualConsole { | ||
constructor(options) { | ||
this.originalConsole = console; | ||
this.stream = options.stream; | ||
this.width = process.stdout.columns; | ||
this.height = process.stdout.rows - 1; | ||
this.progressHeight = 0; | ||
this.consoleHeight = this.height; | ||
this.progressBuffer = []; | ||
this.consoleBuffer = []; | ||
console = this; | ||
this.init(); | ||
} | ||
cleanup() { | ||
this.stream.write('\x1b[0m'); | ||
} | ||
init() { | ||
process.on('SIGINT', this.cleanup); | ||
const blank = '\n'.repeat(this.stream.rows) + CUP(0) + ED(ED_MODE.TO_END); | ||
this.stream.write(blank); | ||
} | ||
/** Add or Update Progress Entry | ||
* | ||
* @param options | ||
* index: number | ||
* data: string | ||
*/ | ||
upsertProgress(options) { | ||
if (options.index < this.progressHeight) { | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
if (options.refresh === undefined || options.refresh) | ||
this.refresh(); | ||
return; | ||
} | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
const numToExtend = 1 + options.index - this.progressHeight; | ||
this.progressHeight = Math.max(options.index + 1, this.progressHeight); | ||
// this.consoleHeight = this.height - this.progressHeight; | ||
if (numToExtend > 0) { | ||
this.consoleHeight -= numToExtend; | ||
const topLines = this.consoleBuffer.splice(0, numToExtend); | ||
if (topLines.length) { | ||
this.log(...topLines); | ||
} | ||
else { | ||
this.refresh(); | ||
} | ||
} | ||
else { | ||
this.refresh(); | ||
} | ||
} | ||
updateProgress(options) { | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
if (options.refresh) { | ||
this.refresh(); | ||
} | ||
} | ||
writeLines(...indexes) { | ||
let writeString = indexes.reduce((prev, index) => { | ||
return prev += CUP(index) + this.progressBuffer[index]; | ||
}, ''); | ||
this.stream.write(writeString); | ||
} | ||
refresh() { | ||
const outString = CUP(0) | ||
+ this.progressBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ (this.progressBuffer.length ? '\n' : '') | ||
+ this.consoleBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ '\n'; | ||
this.stream.write(outString); | ||
} | ||
log(...data) { | ||
const writeString = format.apply(null, data); | ||
const clampedLines = writeString.split('\n').reduce((prev, curr) => { | ||
const clamped = []; | ||
do { | ||
const front = curr.slice(0, this.width); | ||
curr = curr.slice(this.width); | ||
clamped.push(front); | ||
} while (curr.length > this.width); | ||
return [...prev, ...clamped]; | ||
}, []); | ||
this.consoleBuffer.push(...clampedLines); | ||
const topLines = (this.consoleBuffer.length > this.consoleHeight) ? | ||
this.consoleBuffer.splice(0, this.consoleBuffer.length - this.consoleHeight) : []; | ||
const outString = CUP(0) | ||
+ topLines.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ (topLines.length ? '\n' : '') | ||
+ this.progressBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ (this.progressBuffer.length ? '\n' : '') | ||
+ this.consoleBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ '\n'; | ||
this.stream.write(outString); | ||
} | ||
} | ||
const defaultTransformFn = (s) => s; | ||
class MultiProgressBars { | ||
@@ -47,7 +190,23 @@ /** | ||
this.FULL_CHAR = this.CHARS[this.CHARS.length - 1]; | ||
this.endLine = 0; | ||
this.longestNameLength = 0; | ||
this.t = 0; | ||
this.logger = new VirtualConsole({ stream: process.stdout }); | ||
this.resizeConsole = () => { | ||
this.consoleSize = { | ||
width: this.stream.columns, | ||
height: this.stream.rows, | ||
}; | ||
}; | ||
this.cleanup = () => { | ||
if (this.intervalID) { | ||
clearInterval(this.intervalID); | ||
} | ||
}; | ||
const { stream = process.stdout, spinnerFPS = 10, spinnerGenerator = this.hilbertSpinner, } = options || {}; | ||
let { progressWidth = 40, numCrawlers = 4, initMessage, } = options || {}; | ||
this.stream = stream; | ||
this.stream.on('resize', () => { | ||
this.resizeConsole(); | ||
}); | ||
this.spinnerFPS = Math.min(spinnerFPS, 60); | ||
@@ -68,2 +227,3 @@ this.spinnerGenerator = spinnerGenerator; | ||
this.progressWidth = progressWidth; | ||
this.resizeConsole(); | ||
if (initMessage === undefined) { | ||
@@ -78,21 +238,16 @@ initMessage = '$ ' + process.argv.map((arg) => { | ||
// setup cleanup | ||
process.on('SIGINT', () => { | ||
if (this.intervalID) { | ||
clearInterval(this.intervalID); | ||
} | ||
process.on('SIGINT', this.cleanup); | ||
// TODO make this account for lines that wrap | ||
const splitMessage = message.split('\n'); | ||
splitMessage.forEach((msg, idx) => { | ||
this.logger.upsertProgress({ | ||
index: idx, | ||
data: msg, | ||
}); | ||
}); | ||
const splitMessage = message.split('\n').map((str) => stringWidth(str)); | ||
const cols = this.stream.columns; | ||
const eachCols = splitMessage.map((msg) => Math.ceil(msg / cols)); | ||
this.initialLines = eachCols.reduce((prev, curr) => prev + curr, 0); | ||
const blank = '\n'.repeat(this.stream.rows); | ||
this.stream.write(blank); | ||
this.stream.cursorTo(0); | ||
this.stream.clearScreenDown(); | ||
this.stream.write(message); | ||
const blankMinInit = '\n'.repeat(this.stream.rows - this.initialLines); | ||
this.stream.write(blankMinInit); | ||
this.initialLines = splitMessage.length; | ||
} | ||
addTask(name, _a) { | ||
var { index } = _a, options = __rest(_a, ["index"]); | ||
// if task exists, update fields | ||
if (this.tasks[name] !== undefined) { | ||
@@ -103,3 +258,8 @@ Object.keys(options).forEach((key) => options[key] === undefined && delete options[key]); | ||
else { | ||
const { type, barColorFn = (s) => s, percentage = 0, message = '', } = options; | ||
// otherwise make a new task | ||
const { type, barColorFn = defaultTransformFn, percentage = 0, message = '', } = options; | ||
this.endLine = Math.max.apply(null, [ | ||
index, | ||
...Object.entries(this.tasks).map(([_, task]) => task.index) | ||
]) + 1; | ||
this.tasks[name] = { | ||
@@ -111,3 +271,3 @@ type, | ||
name, | ||
index: (index === undefined) ? Object.entries(this.tasks).length : index, | ||
index, | ||
done: false, | ||
@@ -137,7 +297,5 @@ }; | ||
}); | ||
// Go to bottom of tasks and clear downwards. | ||
this.stream.cursorTo(0, Object.entries(this.tasks).length + this.initialLines); | ||
this.stream.clearScreenDown(); | ||
} | ||
progressString(name, percentage, message, barColorFn = (b) => b) { | ||
progressString(task) { | ||
const { name, barColorFn, message, percentage, } = task; | ||
// scale progress bar to percentage of total width | ||
@@ -173,7 +331,11 @@ const scaled = percentage * this.progressWidth; | ||
} | ||
indefiniteString(task, spinner) { | ||
const { name, barColorFn, message, } = task; | ||
return name.padStart(this.longestNameLength) + ': ' + barColorFn(spinner) + ' ' + message; | ||
} | ||
writeTask(task) { | ||
this.stream.cursorTo(0, this.initialLines + task.index); | ||
this.stream.write(this.progressString(task.name, task.percentage, task.message, task.barColorFn).slice(0, this.stream.columns)); | ||
this.stream.clearLine(1); | ||
this.stream.cursorTo(0, Object.entries(this.tasks).length + this.initialLines); | ||
this.logger.upsertProgress({ | ||
index: task.index + this.initialLines, | ||
data: this.progressString(task), | ||
}); | ||
} | ||
@@ -220,8 +382,7 @@ incrementTask(name, _a = {}) { | ||
const task = this.tasks[name]; | ||
this.stream.cursorTo(0, this.initialLines + task.index); | ||
const bar = task.barColorFn(this.FULL_CHAR.repeat(this.progressWidth)); | ||
// TODO customizable format. Maybe unify this with writeTask | ||
this.stream.write(name.padStart(this.longestNameLength) + ': ' + bar + ' ' + message); | ||
this.stream.clearLine(1); | ||
this.stream.cursorTo(0, Object.entries(this.tasks).length + this.initialLines); | ||
this.logger.upsertProgress({ | ||
index: task.index + this.initialLines, | ||
data: name.padStart(this.longestNameLength) + ': ' + bar + ' ' + message, | ||
}); | ||
// Stop animation if all tasks are done, and resolve the promise. | ||
@@ -279,9 +440,13 @@ if (Object.entries(this.tasks).reduce((prev, [_, curr]) => { | ||
const spinner = this.spinnerGenerator(this.t, this.progressWidth); | ||
Object.entries(this.tasks).forEach(([name, task]) => { | ||
Object.entries(this.tasks).forEach(([_, task]) => { | ||
if (task.type === 'indefinite' && task.done === false) { | ||
this.stream.cursorTo(0, this.initialLines + task.index); | ||
this.stream.write(name.padStart(this.longestNameLength) + ': ' + task.barColorFn(spinner) + ' ' + task.message); | ||
this.stream.clearLine(1); | ||
let progressString = this.indefiniteString(task, spinner); | ||
this.logger.upsertProgress({ | ||
index: task.index + this.initialLines, | ||
data: progressString, | ||
refresh: false, | ||
}); | ||
} | ||
}); | ||
this.logger.refresh(); | ||
this.t = this.t + 1; | ||
@@ -288,0 +453,0 @@ } |
@@ -0,3 +1,39 @@ | ||
export declare const RESET: string; | ||
/** CUrsor Position | ||
* | ||
* @param row (required) 0-index absolute row | ||
* @param column (optional) 0-index absolute column | ||
*/ | ||
export declare const CUP: (row: number, column?: number) => string; | ||
/** Cursor Horizonal Absolute | ||
* | ||
* @param column (optional) 0-index absolute column | ||
*/ | ||
export declare const CHA: (column: number) => string; | ||
/** CUrsor Up | ||
* | ||
* @param number (optional) 0-index rows to move up | ||
*/ | ||
export declare const CUU: (number: number) => string; | ||
/** CUrsor Down | ||
* | ||
* @param number (optional) 0-index rows to move down | ||
*/ | ||
export declare const CUD: (number: number) => string; | ||
/** CUrsor Forward | ||
* | ||
* @param number (optional) 0-index rows to move right | ||
*/ | ||
export declare const CUF: (number: number) => string; | ||
/** CUrsor Back | ||
* | ||
* @param number (optional) 0-index rows to move left | ||
*/ | ||
export declare const CUB: (number: number) => string; | ||
/** | ||
* | ||
* @param rows (required) 0-index rows to move down (can be negative for moving up) | ||
* @param columns (optional) 0-index columns to move to the right (negative for left) | ||
*/ | ||
export declare const MoveCursor: (rows: number, columns?: number) => string; | ||
export declare enum EL_MODE { | ||
@@ -16,1 +52,2 @@ TO_END = 0, | ||
export declare const ED: (mode?: ED_MODE) => string; | ||
export declare const clampString: (message: string, width: number) => string; |
{ | ||
"name": "multi-progress-bars", | ||
"version": "1.1.4", | ||
"version": "2.0.1", | ||
"description": "Multiple progress bars with option for indefinite spinners", | ||
@@ -14,3 +14,3 @@ "main": "dist/multi-progress-bars.cjs.js", | ||
"release": "standard-version", | ||
"prepublishOnly": "yarn build", | ||
"prepublishOnly": "yarn clean && yarn build", | ||
"buildExample": "yarn tsc -b example", | ||
@@ -17,0 +17,0 @@ "clean": "yarn trash 'dist/**/*' 'example/example.js'", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
56196
9
1058