multi-progress-bars
Advanced tools
Comparing version 3.2.4 to 4.0.0-alpha.0
@@ -5,2 +5,18 @@ # Changelog | ||
## [4.0.0-alpha.0](https://github.com/kamiyo/multi-progress-bars/compare/v3.2.4...v4.0.0-alpha.0) (2021-05-18) | ||
### ⚠ BREAKING CHANGES | ||
* **core:** When number of bars overflow console height, only the last N are shown. Upon closing mpb, it will dump the entire progress buffer, so everything will be visible in scrollback. | ||
### Features | ||
* **core:** When number of bars overflow console height, only the last N are shown. Upon closing mpb, it will dump the entire progress buffer, so everything will be visible in scrollback. ([619132a](https://github.com/kamiyo/multi-progress-bars/commit/619132a6e553330b93087ae5d9dd615e7a8d806f)) | ||
### Bug Fixes | ||
* **core:** Fix issue [#11](https://github.com/kamiyo/multi-progress-bars/issues/11) ([5cdac93](https://github.com/kamiyo/multi-progress-bars/commit/5cdac935f16be1249ed00c43255acf14693207d7)) | ||
### [3.2.4](https://github.com/kamiyo/multi-progress-bars/compare/v3.2.3...v3.2.4) (2021-04-16) | ||
@@ -7,0 +23,0 @@ |
@@ -84,2 +84,19 @@ 'use strict'; | ||
}; | ||
// Split by newlines, and then split the resulting lines if they run longer than width. | ||
const splitLinesAndClamp = (writeString, maxWidth) => { | ||
return writeString.split('\n').reduce((prev, curr) => { | ||
const clamped = []; | ||
do { | ||
let width = curr.length; | ||
let front = curr; | ||
while (stringWidth__default['default'](front) > maxWidth) { | ||
front = curr.slice(0, width); | ||
width--; | ||
} | ||
curr = curr.slice(width); | ||
clamped.push(front); | ||
} while (curr.length > 0); | ||
return [...prev, ...clamped]; | ||
}, []); | ||
}; | ||
@@ -90,30 +107,13 @@ class VirtualConsole { | ||
this.stream = options.stream; | ||
this.resize(); | ||
this.anchor = options.anchor; | ||
// These members are only needed for top-anchored progresses | ||
if (this.anchor === 'top') { | ||
this.consoleBuffer = []; | ||
this.consoleHeight = this.height; | ||
} | ||
this.width = this.stream.columns; | ||
this.height = this.stream.rows; | ||
this.stream.on('resize', this.resize); | ||
this.progressHeight = 0; | ||
this.progressBuffer = []; | ||
if (this.anchor === 'top') { | ||
this.upsertProgress = this.upsertProgressTop; | ||
this.writeLines = this.writeLinesTop; | ||
this.refresh = this.refreshTop; | ||
this.log = this.logTop; | ||
this.done = this.cleanup; | ||
if (!process.stdout.isTTY) { | ||
this.log = console.log; | ||
} | ||
else { | ||
this.upsertProgress = this.upsertProgressBottom; | ||
this.writeLines = this.writeLinesBottom; | ||
this.refresh = this.refreshBottom; | ||
this.log = this.logBottom; | ||
this.done = this.gotoBottom; | ||
} | ||
this.warn = this.log; | ||
this.error = this.log; | ||
console = this; | ||
this.init(); | ||
} | ||
@@ -126,27 +126,79 @@ checkConsoleIntercept() { | ||
} | ||
// height is one less than rows, because if you print to the last line, the console usually adds a newline | ||
resize() { | ||
// see https://github.com/kamiyo/multi-progress-bars/issues/7 | ||
const stdout = process.stdout.isTTY ? process.stdout : process.stderr; | ||
this.width = stdout.columns; | ||
this.height = stdout.rows - 1; | ||
var _a; | ||
this.width = this.stream.columns; | ||
this.height = this.stream.rows; | ||
(_a = this.refresh) === null || _a === void 0 ? void 0 : _a.call(this); | ||
} | ||
gotoBottom() { | ||
done() { | ||
throw new Error('Must Implement in Derived Class!'); | ||
} | ||
refresh() { | ||
throw new Error('Must Implement in Derived Class'); | ||
} | ||
log(..._) { | ||
throw new Error('Must Implement in Derived Class'); | ||
} | ||
upsertProgress(_) { | ||
throw new Error('Must Implement in Dervied Class'); | ||
} | ||
init() { | ||
var _a; | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(CUP(this.height + 1) + '\x1b[0m'); | ||
console = this.originalConsole; | ||
this.originalConsole = null; | ||
const blank = '\n'.repeat(this.stream.rows) + CUP(0) + ED(ED_MODE.TO_END); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(blank); | ||
} | ||
cleanup() { | ||
setTopBorder(border) { | ||
this.topBorder = border; | ||
this.progressHeight += 1; | ||
} | ||
setBottomBorder(border) { | ||
this.bottomBorder = border; | ||
this.progressHeight += 1; | ||
} | ||
currentHeightMinusBorders() { | ||
return this.progressHeight - (this.topBorder === undefined ? 0 : 1) - (this.bottomBorder === undefined ? 0 : 1); | ||
} | ||
dumpBuffer() { | ||
var _a; | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write('\x1b[0m'); | ||
console = this.originalConsole; | ||
this.originalConsole = null; | ||
const outString = '' | ||
+ CUP(0) | ||
+ ED(0) | ||
+ '\x1b[0m' | ||
+ ((this.topBorder === undefined) ? '' : (this.topBorder + '\n')) | ||
+ this.progressBuffer | ||
.join('\n') | ||
+ ((this.bottomBorder === undefined) ? '' : ('\n' + this.bottomBorder)); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString); | ||
} | ||
init() { | ||
getBuffer() { | ||
return this.progressBuffer; | ||
} | ||
} | ||
class VirtualConsoleTop extends VirtualConsole { | ||
constructor(options) { | ||
var _a; | ||
if (this.anchor === 'top') { | ||
const blank = '\n'.repeat(this.stream.rows) + CUP(0) + ED(ED_MODE.TO_END); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(blank); | ||
super(options); | ||
this.consoleBuffer = []; | ||
this.consoleHeight = this.height; | ||
this.init(); | ||
(_a = this.refresh) === null || _a === void 0 ? void 0 : _a.call(this); | ||
} | ||
setTopBorder(border) { | ||
super.setTopBorder(border); | ||
this.consoleHeight -= 1; | ||
} | ||
setBottomBorder(border) { | ||
super.setBottomBorder(border); | ||
this.consoleHeight -= 1; | ||
} | ||
done() { | ||
var _a; | ||
if (this.progressBuffer.length > this.height) { | ||
this.dumpBuffer(); | ||
} | ||
else { | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write('\x1b[0m\n'); | ||
} | ||
console = this.originalConsole; | ||
this.originalConsole = null; | ||
} | ||
@@ -159,128 +211,154 @@ /** Add or Update Progress Entry | ||
*/ | ||
upsertProgressTop(options) { | ||
upsertProgress(options) { | ||
// Reactivate console intercepting | ||
this.checkConsoleIntercept(); | ||
// If the progress we're upserting exists already, just update. | ||
if (options.index < this.progressHeight) { | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
if (options.refresh === undefined || options.refresh) | ||
this.refresh(); | ||
const numToExtend = 1 + options.index - this.progressBuffer.length; | ||
// Truncate progress line to console width. | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
// If we're not increasing the progress bars section, we're done. | ||
if (numToExtend <= 0) { | ||
return; | ||
} | ||
// Truncate progress line to console width. | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
// Extend the progress bars section, and reduce the corresponding console buffer height. | ||
const numToExtend = 1 + options.index - this.progressHeight; | ||
this.progressHeight = Math.max(options.index + 1, this.progressHeight); | ||
if (numToExtend > 0) { | ||
this.consoleHeight -= numToExtend; | ||
const topLines = this.consoleBuffer.splice(0, numToExtend); | ||
if (topLines.length) { | ||
this.log(...topLines); | ||
} | ||
else { | ||
this.refresh(); | ||
} | ||
this.progressHeight = Math.min(this.progressHeight + numToExtend, this.height); | ||
this.consoleHeight = Math.max(this.consoleHeight - numToExtend, 0); | ||
} | ||
getOutString(bufferStartIndex, topLines) { | ||
return [ | ||
topLines.map((val) => val + EL(EL_MODE.TO_END)).join('\n'), | ||
((this.topBorder === undefined) ? // Top border or null | ||
null | ||
: (this.topBorder + EL(EL_MODE.TO_END))), | ||
this.progressBuffer // Progress bars | ||
.slice(bufferStartIndex) | ||
.map((val) => val + EL(EL_MODE.TO_END)) | ||
.join('\n'), | ||
((this.bottomBorder === undefined) ? // Bottom border or null | ||
null | ||
: (this.bottomBorder + EL(EL_MODE.TO_END))), | ||
this.consoleBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') // Logs | ||
].filter((v) => { | ||
return (v !== undefined) && (v !== '') && (v !== null); // Remove falsey/empty values | ||
}).join('\n'); // Join with newlines | ||
} | ||
/* Prints out the buffers as they are */ | ||
refresh() { | ||
var _a; | ||
// pop top of consoleBuffer if longer than consoleHeight | ||
const topLines = (this.consoleBuffer.length > this.consoleHeight) ? | ||
this.consoleBuffer.splice(0, this.consoleBuffer.length - this.consoleHeight) : []; | ||
const bufferStartIndex = Math.max(this.progressBuffer.length - this.currentHeightMinusBorders(), 0); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(CUP(0) + this.getOutString(bufferStartIndex, topLines)); | ||
} | ||
log(...data) { | ||
var _a; | ||
if (data.length !== 0) { | ||
const writeString = util.format.apply(null, data); | ||
const clampedLines = splitLinesAndClamp(writeString, this.width); | ||
this.consoleBuffer.push(...clampedLines); | ||
} | ||
// If the console buffer is higher than console height, remove the top, and print them first. | ||
const topLines = (this.consoleBuffer.length > this.consoleHeight) ? | ||
this.consoleBuffer.splice(0, this.consoleBuffer.length - this.consoleHeight) | ||
: []; | ||
const bufferStartIndex = Math.max(this.progressBuffer.length - this.currentHeightMinusBorders(), 0); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(CUP(0) + this.getOutString(bufferStartIndex, topLines)); | ||
} | ||
/** STUB | ||
* | ||
*/ | ||
removeProgressSlot() { | ||
this.progressHeight = Math.max(0, this.progressHeight - 1); | ||
// this.consoleHeight = Math.min(this.height, this.consoleHeight + 1); | ||
// KEEP DOING | ||
} | ||
} | ||
class VirtualConsoleBottom extends VirtualConsole { | ||
constructor(options) { | ||
super(options); | ||
this.init(); | ||
} | ||
init() { | ||
var _a, _b; | ||
super.init(); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(CUP(this.height - 1)); | ||
(_b = this.refresh) === null || _b === void 0 ? void 0 : _b.call(this); | ||
} | ||
done() { | ||
var _a; | ||
if (this.progressBuffer.length > this.height) { | ||
this.dumpBuffer(); | ||
} | ||
else { | ||
this.refresh(); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(CUP(this.height, this.width) + '\x1b[0m\n'); | ||
} | ||
console = this.originalConsole; | ||
this.originalConsole = null; | ||
} | ||
upsertProgressBottom(options) { | ||
/** Add or Update Progress Entry | ||
* | ||
* @param options | ||
* index: number | ||
* data: string | ||
*/ | ||
upsertProgress(options) { | ||
// Reactivate console intercepting | ||
this.checkConsoleIntercept(); | ||
// If the progress we're upserting exists already, just update. | ||
if (options.index < this.progressHeight) { | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
if (options.refresh === undefined || options.refresh) | ||
this.refresh(); | ||
return; | ||
} | ||
const numToExtend = 1 + options.index - this.progressBuffer.length; | ||
// Truncate progress line to console width. | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
// Extend the progress bars section. | ||
this.progressHeight = Math.max(options.index + 1, this.progressHeight); | ||
this.refresh(); | ||
} | ||
updateProgress(options) { | ||
this.checkConsoleIntercept(); | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
if (options.refresh) { | ||
this.refresh(); | ||
// If we're not increasing the progress bars section, we're done. | ||
if (numToExtend <= 0) { | ||
return; | ||
} | ||
// Extend the progress bars section | ||
this.progressHeight = Math.min(this.progressHeight + numToExtend, this.height); | ||
} | ||
writeLinesTop(...indexes) { | ||
var _a; | ||
let writeString = indexes.reduce((prev, index) => { | ||
return prev += CUP(index) + this.progressBuffer[index]; | ||
}, ''); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(writeString); | ||
} | ||
writeLinesBottom(...indexes) { | ||
var _a; | ||
const firstProgressLine = this.height - this.progressHeight; | ||
let writeString = indexes.reduce((prev, index) => { | ||
return prev += CUP(firstProgressLine + index) + this.progressBuffer[index]; | ||
}, ''); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(writeString); | ||
} | ||
/* Prints out the buffers as they are */ | ||
refreshTop() { | ||
refresh() { | ||
var _a; | ||
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'; | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString); | ||
} | ||
refreshBottom() { | ||
var _a; | ||
const bufferStartIndex = Math.max(this.progressBuffer.length - this.currentHeightMinusBorders(), 0); | ||
const firstProgressLine = this.height - this.progressHeight; | ||
const outString = this.progressBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ (this.progressBuffer.length ? '\n' : '') | ||
+ CUP(firstProgressLine); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString); | ||
const outString = [ | ||
((this.topBorder === undefined) ? // Top border or null | ||
null | ||
: (this.topBorder + EL(EL_MODE.TO_END))), | ||
this.progressBuffer // Progress bars or [] | ||
.slice(bufferStartIndex) | ||
.map((val) => val + EL(EL_MODE.TO_END)) | ||
.join('\n'), | ||
((this.bottomBorder === undefined) ? // Bottom border or null | ||
null | ||
: (this.bottomBorder + EL(EL_MODE.TO_END))), | ||
].filter((v) => { | ||
return (v !== undefined) && (v !== '') && (v !== null); // Remove falsey/empty values | ||
}).join('\n'); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString + CUP(firstProgressLine)); | ||
} | ||
logTop(...data) { | ||
log(...data) { | ||
var _a; | ||
const writeString = util.format.apply(null, data); | ||
// Split by newlines, and then split the resulting lines if they run longer than width. | ||
const clampedLines = writeString.split('\n').reduce((prev, curr) => { | ||
const clamped = []; | ||
do { | ||
let width = curr.length; | ||
let front = curr; | ||
while (stringWidth__default['default'](front) > this.width) { | ||
front = curr.slice(0, width); | ||
width--; | ||
} | ||
curr = curr.slice(width); | ||
clamped.push(front); | ||
} while (curr.length > 0); | ||
return [...prev, ...clamped]; | ||
}, []); | ||
this.consoleBuffer.push(...clampedLines); | ||
// If the console buffer is higher than console height, remove the top, and print them first. | ||
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') // print the previously removed top lines | ||
+ (topLines.length ? '\n' : '') // separator | ||
+ this.progressBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') // progress bars | ||
+ (this.progressBuffer.length ? '\n' : '') | ||
+ this.consoleBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') // rest of the console log. | ||
+ '\n'; | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString); | ||
} | ||
logBottom(...data) { | ||
var _a; | ||
const writeString = util.format.apply(null, data); | ||
let clampedLines = []; | ||
if (data.length !== 0) { | ||
const writeString = util.format.apply(null, data); | ||
// Split by newlines, and then split the resulting lines if they run longer than width. | ||
clampedLines = splitLinesAndClamp(writeString, this.width); | ||
} | ||
const firstProgressLine = this.height - this.progressHeight; | ||
const outString = this.progressBuffer.map((_) => EL(EL_MODE.ENTIRE_LINE)).join('\n') | ||
+ CUP(firstProgressLine) | ||
+ writeString | ||
+ '\n' | ||
+ this.progressBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ (this.progressBuffer.length ? '\n' : '') | ||
+ CUP(firstProgressLine); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString); | ||
const bufferStartIndex = Math.max(this.progressBuffer.length - this.currentHeightMinusBorders(), 0); | ||
const outString = [ | ||
clampedLines.map((val) => val + EL(EL_MODE.TO_END)).join('\n'), | ||
((this.topBorder === undefined) ? // Top border or null | ||
null | ||
: (this.topBorder + EL(EL_MODE.TO_END))), | ||
this.progressBuffer // Progress bars or [] | ||
.slice(bufferStartIndex) | ||
.map((val) => val + EL(EL_MODE.TO_END)) | ||
.join('\n'), | ||
((this.bottomBorder === undefined) ? // Bottom border or null | ||
null | ||
: (this.bottomBorder + EL(EL_MODE.TO_END))), | ||
].filter((v) => { | ||
return (v !== undefined) && (v !== '') && (v !== null); // Remove falsey/empty values | ||
}).join('\n'); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString + CUP(firstProgressLine)); | ||
} | ||
@@ -307,2 +385,3 @@ } | ||
this.endIdx = 0; // 1 past the last index | ||
this.allFinished = false; | ||
this.cleanup = () => { | ||
@@ -318,5 +397,9 @@ var _a; | ||
}; | ||
const { stream = process.stdout, spinnerFPS = 10, spinnerGenerator = this.hilbertSpinner, anchor = 'bottom', persist = false, border = false, } = options || {}; | ||
const { | ||
// see https://github.com/kamiyo/multi-progress-bars/issues/7 | ||
stream = process.stdout.isTTY ? process.stdout : process.stderr, spinnerFPS = 10, spinnerGenerator = this.hilbertSpinner, anchor = 'bottom', persist = false, border = false, } = options || {}; | ||
let { progressWidth = 40, numCrawlers = 4, initMessage, } = options || {}; | ||
this.logger = new VirtualConsole({ stream, anchor }); | ||
this.logger = (anchor === 'top') ? | ||
new VirtualConsoleTop({ stream }) | ||
: new VirtualConsoleBottom({ stream }); | ||
this.persist = persist; | ||
@@ -326,3 +409,4 @@ this.spinnerFPS = Math.min(spinnerFPS, 60); | ||
this.border = (typeof border === 'boolean') | ||
? (!border) ? null : '\u2500' | ||
? (!border) | ||
? null : '\u2500' | ||
: border; | ||
@@ -363,3 +447,3 @@ if (progressWidth % 2 !== 0) { | ||
} | ||
if (this.border && this.logger.anchor === 'top') { | ||
if (this.border && anchor === 'top') { | ||
this.bottomBorder = | ||
@@ -373,10 +457,9 @@ clampString(this.border.repeat(Math.ceil(this.logger.width / this.border.length)), this.logger.width); | ||
process.on('SIGINT', this.cleanup); | ||
this.logger.upsertProgress({ | ||
index: 0, | ||
data: message, | ||
}); | ||
this.initialLines = 1; | ||
message && this.logger.setTopBorder(message); | ||
(this.bottomBorder !== undefined) && this.logger.setBottomBorder(this.bottomBorder); | ||
this.promise = new Promise((res, _) => this.resolve = res); | ||
} | ||
addTask(name, _a) { | ||
var { index } = _a, options = __rest(_a, ["index"]); | ||
this.restartPromiseIfNeeded(); | ||
// if task exists, update fields | ||
@@ -418,12 +501,39 @@ if (this.tasks[name] !== undefined) { | ||
this.longestNameLength = Math.max(this.longestNameLength, stringWidth__default['default'](name)); | ||
// Reset promise for end hook | ||
this.promise = new Promise((res, _) => this.resolve = res); | ||
// Rerender previously finished tasks so that the task names are padded correctly. | ||
// Do this by calling done() again. | ||
Object.entries(this.tasks).forEach(([name, { done, type, message }]) => { | ||
if (done && type === 'indefinite') { | ||
this.done(name, { message }); | ||
} | ||
// Rerender other tasks so that task names are padded correctly. | ||
Object.values(this.tasks).forEach((task) => { | ||
this.writeTask(task); | ||
}); | ||
this.logger.refresh(); | ||
} | ||
// Call this BEFORE you add a new task to the list | ||
restartPromiseIfNeeded() { | ||
// check if allFinished previously | ||
if (this.allFinished) { | ||
this.allFinished = false; | ||
this.promise = new Promise((res) => this.resolve = res); | ||
} | ||
} | ||
isDone(name) { | ||
return this.tasks[name].done; | ||
} | ||
// public removeTask(name: string) { | ||
// const idxToRemove = this.tasks[name].index; | ||
// delete this.tasks[name]; | ||
// this.longestNameLength = Object.entries(this.tasks).reduce((prev, [taskName, { index }]) => { | ||
// // What?! Side-effects in reduce?! | ||
// // Don't worry, we're not functional purists here. | ||
// // Decrement all indexes after the one to remove. | ||
// if (index > idxToRemove) { | ||
// this.tasks[taskName].index--; | ||
// } | ||
// return Math.max(prev, stringWidth(taskName)); | ||
// }, 0); | ||
// // Rerender previously finished tasks so that the task names are padded correctly. | ||
// // Do this by calling done() again. | ||
// Object.entries(this.tasks).forEach(([name, { done, message }]) => { | ||
// if (done) { | ||
// this.done(name, { message }); | ||
// } | ||
// }); | ||
// } | ||
progressString(task) { | ||
@@ -467,11 +577,5 @@ const { name, barColorFn, message, percentage, } = task; | ||
this.logger.upsertProgress({ | ||
index: task.index + this.initialLines, | ||
index: task.index, | ||
data: this.progressString(task), | ||
}); | ||
if (this.bottomBorder) { | ||
this.logger.upsertProgress({ | ||
index: Object.keys(this.tasks).length + this.initialLines, | ||
data: this.bottomBorder, | ||
}); | ||
} | ||
} | ||
@@ -493,4 +597,6 @@ incrementTask(name, _a = {}) { | ||
updateTask(name, options = {}) { | ||
if (this.tasks[name] === undefined) | ||
if (this.tasks[name] === undefined) { | ||
throw new ReferenceError('Task does not exist.'); | ||
} | ||
this.restartPromiseIfNeeded(); | ||
const task = this.tasks[name]; | ||
@@ -507,3 +613,2 @@ // Going over 1(00%) calls done | ||
this.intervalID = setInterval(() => this.renderIndefinite(), 1000 / this.spinnerFPS); | ||
this.promise = new Promise((res, _) => this.resolve = res); | ||
} | ||
@@ -513,2 +618,3 @@ return; | ||
this.writeTask(this.tasks[name]); | ||
this.logger.refresh(); | ||
} | ||
@@ -521,9 +627,6 @@ done(name, _a = {}) { | ||
const task = this.tasks[name]; | ||
const bar = task.barColorFn(this.FULL_CHAR.repeat(this.progressWidth)); | ||
this.logger.upsertProgress({ | ||
index: task.index + this.initialLines, | ||
data: name.padStart(this.longestNameLength) + ': ' + bar + ' ' + message, | ||
}); | ||
this.writeTask(task); | ||
this.logger.refresh(); | ||
// Stop animation if all tasks are done, and resolve the promise. | ||
if (Object.entries(this.tasks).reduce((prev, [_, curr]) => { | ||
if (Object.values(this.tasks).reduce((prev, curr) => { | ||
return prev && curr.done; | ||
@@ -533,2 +636,3 @@ }, true)) { | ||
this.intervalID = null; | ||
this.allFinished = true; | ||
this.resolve(); | ||
@@ -541,4 +645,6 @@ if (!this.persist) { | ||
restart(name, options) { | ||
if (this.tasks[name] === undefined) | ||
this.restartPromiseIfNeeded(); | ||
if (this.tasks[name] === undefined) { | ||
throw new ReferenceError('Task does not exist.'); | ||
} | ||
this.tasks[name] = Object.assign(Object.assign(Object.assign({}, this.tasks[name]), options), { percentage: 0, done: false }); | ||
@@ -552,7 +658,8 @@ if (this.tasks[name].type === 'indefinite' && !this.intervalID) { | ||
this.writeTask(this.tasks[name]); | ||
this.logger.refresh(); | ||
} | ||
this.promise = new Promise((res, _) => this.resolve = res); | ||
} | ||
close() { | ||
this.logger.done(); | ||
var _a; | ||
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.done(); | ||
} | ||
@@ -602,5 +709,4 @@ // Returns the index of task with supplied name. Returns undefined if name not found. | ||
this.logger.upsertProgress({ | ||
index: task.index + this.initialLines, | ||
index: task.index, | ||
data: progressString, | ||
refresh: false, | ||
}); | ||
@@ -607,0 +713,0 @@ } |
@@ -57,3 +57,2 @@ /// <reference types="node" /> | ||
private spinnerFPS; | ||
private initialLines; | ||
private progressWidth; | ||
@@ -76,2 +75,3 @@ private CHARS; | ||
private endIdx; | ||
private allFinished; | ||
promise: Promise<void>; | ||
@@ -86,2 +86,4 @@ /** | ||
addTask(name: string, { index, ...options }: AddOptions): void; | ||
private restartPromiseIfNeeded; | ||
isDone(name: string): boolean; | ||
private progressString; | ||
@@ -88,0 +90,0 @@ private indefiniteString; |
@@ -76,2 +76,19 @@ import { green } from 'chalk'; | ||
}; | ||
// Split by newlines, and then split the resulting lines if they run longer than width. | ||
const splitLinesAndClamp = (writeString, maxWidth) => { | ||
return writeString.split('\n').reduce((prev, curr) => { | ||
const clamped = []; | ||
do { | ||
let width = curr.length; | ||
let front = curr; | ||
while (stringWidth(front) > maxWidth) { | ||
front = curr.slice(0, width); | ||
width--; | ||
} | ||
curr = curr.slice(width); | ||
clamped.push(front); | ||
} while (curr.length > 0); | ||
return [...prev, ...clamped]; | ||
}, []); | ||
}; | ||
@@ -82,30 +99,13 @@ class VirtualConsole { | ||
this.stream = options.stream; | ||
this.resize(); | ||
this.anchor = options.anchor; | ||
// These members are only needed for top-anchored progresses | ||
if (this.anchor === 'top') { | ||
this.consoleBuffer = []; | ||
this.consoleHeight = this.height; | ||
} | ||
this.width = this.stream.columns; | ||
this.height = this.stream.rows; | ||
this.stream.on('resize', this.resize); | ||
this.progressHeight = 0; | ||
this.progressBuffer = []; | ||
if (this.anchor === 'top') { | ||
this.upsertProgress = this.upsertProgressTop; | ||
this.writeLines = this.writeLinesTop; | ||
this.refresh = this.refreshTop; | ||
this.log = this.logTop; | ||
this.done = this.cleanup; | ||
if (!process.stdout.isTTY) { | ||
this.log = console.log; | ||
} | ||
else { | ||
this.upsertProgress = this.upsertProgressBottom; | ||
this.writeLines = this.writeLinesBottom; | ||
this.refresh = this.refreshBottom; | ||
this.log = this.logBottom; | ||
this.done = this.gotoBottom; | ||
} | ||
this.warn = this.log; | ||
this.error = this.log; | ||
console = this; | ||
this.init(); | ||
} | ||
@@ -118,27 +118,79 @@ checkConsoleIntercept() { | ||
} | ||
// height is one less than rows, because if you print to the last line, the console usually adds a newline | ||
resize() { | ||
// see https://github.com/kamiyo/multi-progress-bars/issues/7 | ||
const stdout = process.stdout.isTTY ? process.stdout : process.stderr; | ||
this.width = stdout.columns; | ||
this.height = stdout.rows - 1; | ||
var _a; | ||
this.width = this.stream.columns; | ||
this.height = this.stream.rows; | ||
(_a = this.refresh) === null || _a === void 0 ? void 0 : _a.call(this); | ||
} | ||
gotoBottom() { | ||
done() { | ||
throw new Error('Must Implement in Derived Class!'); | ||
} | ||
refresh() { | ||
throw new Error('Must Implement in Derived Class'); | ||
} | ||
log(..._) { | ||
throw new Error('Must Implement in Derived Class'); | ||
} | ||
upsertProgress(_) { | ||
throw new Error('Must Implement in Dervied Class'); | ||
} | ||
init() { | ||
var _a; | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(CUP(this.height + 1) + '\x1b[0m'); | ||
console = this.originalConsole; | ||
this.originalConsole = null; | ||
const blank = '\n'.repeat(this.stream.rows) + CUP(0) + ED(ED_MODE.TO_END); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(blank); | ||
} | ||
cleanup() { | ||
setTopBorder(border) { | ||
this.topBorder = border; | ||
this.progressHeight += 1; | ||
} | ||
setBottomBorder(border) { | ||
this.bottomBorder = border; | ||
this.progressHeight += 1; | ||
} | ||
currentHeightMinusBorders() { | ||
return this.progressHeight - (this.topBorder === undefined ? 0 : 1) - (this.bottomBorder === undefined ? 0 : 1); | ||
} | ||
dumpBuffer() { | ||
var _a; | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write('\x1b[0m'); | ||
console = this.originalConsole; | ||
this.originalConsole = null; | ||
const outString = '' | ||
+ CUP(0) | ||
+ ED(0) | ||
+ '\x1b[0m' | ||
+ ((this.topBorder === undefined) ? '' : (this.topBorder + '\n')) | ||
+ this.progressBuffer | ||
.join('\n') | ||
+ ((this.bottomBorder === undefined) ? '' : ('\n' + this.bottomBorder)); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString); | ||
} | ||
init() { | ||
getBuffer() { | ||
return this.progressBuffer; | ||
} | ||
} | ||
class VirtualConsoleTop extends VirtualConsole { | ||
constructor(options) { | ||
var _a; | ||
if (this.anchor === 'top') { | ||
const blank = '\n'.repeat(this.stream.rows) + CUP(0) + ED(ED_MODE.TO_END); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(blank); | ||
super(options); | ||
this.consoleBuffer = []; | ||
this.consoleHeight = this.height; | ||
this.init(); | ||
(_a = this.refresh) === null || _a === void 0 ? void 0 : _a.call(this); | ||
} | ||
setTopBorder(border) { | ||
super.setTopBorder(border); | ||
this.consoleHeight -= 1; | ||
} | ||
setBottomBorder(border) { | ||
super.setBottomBorder(border); | ||
this.consoleHeight -= 1; | ||
} | ||
done() { | ||
var _a; | ||
if (this.progressBuffer.length > this.height) { | ||
this.dumpBuffer(); | ||
} | ||
else { | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write('\x1b[0m\n'); | ||
} | ||
console = this.originalConsole; | ||
this.originalConsole = null; | ||
} | ||
@@ -151,128 +203,154 @@ /** Add or Update Progress Entry | ||
*/ | ||
upsertProgressTop(options) { | ||
upsertProgress(options) { | ||
// Reactivate console intercepting | ||
this.checkConsoleIntercept(); | ||
// If the progress we're upserting exists already, just update. | ||
if (options.index < this.progressHeight) { | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
if (options.refresh === undefined || options.refresh) | ||
this.refresh(); | ||
const numToExtend = 1 + options.index - this.progressBuffer.length; | ||
// Truncate progress line to console width. | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
// If we're not increasing the progress bars section, we're done. | ||
if (numToExtend <= 0) { | ||
return; | ||
} | ||
// Truncate progress line to console width. | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
// Extend the progress bars section, and reduce the corresponding console buffer height. | ||
const numToExtend = 1 + options.index - this.progressHeight; | ||
this.progressHeight = Math.max(options.index + 1, this.progressHeight); | ||
if (numToExtend > 0) { | ||
this.consoleHeight -= numToExtend; | ||
const topLines = this.consoleBuffer.splice(0, numToExtend); | ||
if (topLines.length) { | ||
this.log(...topLines); | ||
} | ||
else { | ||
this.refresh(); | ||
} | ||
this.progressHeight = Math.min(this.progressHeight + numToExtend, this.height); | ||
this.consoleHeight = Math.max(this.consoleHeight - numToExtend, 0); | ||
} | ||
getOutString(bufferStartIndex, topLines) { | ||
return [ | ||
topLines.map((val) => val + EL(EL_MODE.TO_END)).join('\n'), | ||
((this.topBorder === undefined) ? // Top border or null | ||
null | ||
: (this.topBorder + EL(EL_MODE.TO_END))), | ||
this.progressBuffer // Progress bars | ||
.slice(bufferStartIndex) | ||
.map((val) => val + EL(EL_MODE.TO_END)) | ||
.join('\n'), | ||
((this.bottomBorder === undefined) ? // Bottom border or null | ||
null | ||
: (this.bottomBorder + EL(EL_MODE.TO_END))), | ||
this.consoleBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') // Logs | ||
].filter((v) => { | ||
return (v !== undefined) && (v !== '') && (v !== null); // Remove falsey/empty values | ||
}).join('\n'); // Join with newlines | ||
} | ||
/* Prints out the buffers as they are */ | ||
refresh() { | ||
var _a; | ||
// pop top of consoleBuffer if longer than consoleHeight | ||
const topLines = (this.consoleBuffer.length > this.consoleHeight) ? | ||
this.consoleBuffer.splice(0, this.consoleBuffer.length - this.consoleHeight) : []; | ||
const bufferStartIndex = Math.max(this.progressBuffer.length - this.currentHeightMinusBorders(), 0); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(CUP(0) + this.getOutString(bufferStartIndex, topLines)); | ||
} | ||
log(...data) { | ||
var _a; | ||
if (data.length !== 0) { | ||
const writeString = format.apply(null, data); | ||
const clampedLines = splitLinesAndClamp(writeString, this.width); | ||
this.consoleBuffer.push(...clampedLines); | ||
} | ||
// If the console buffer is higher than console height, remove the top, and print them first. | ||
const topLines = (this.consoleBuffer.length > this.consoleHeight) ? | ||
this.consoleBuffer.splice(0, this.consoleBuffer.length - this.consoleHeight) | ||
: []; | ||
const bufferStartIndex = Math.max(this.progressBuffer.length - this.currentHeightMinusBorders(), 0); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(CUP(0) + this.getOutString(bufferStartIndex, topLines)); | ||
} | ||
/** STUB | ||
* | ||
*/ | ||
removeProgressSlot() { | ||
this.progressHeight = Math.max(0, this.progressHeight - 1); | ||
// this.consoleHeight = Math.min(this.height, this.consoleHeight + 1); | ||
// KEEP DOING | ||
} | ||
} | ||
class VirtualConsoleBottom extends VirtualConsole { | ||
constructor(options) { | ||
super(options); | ||
this.init(); | ||
} | ||
init() { | ||
var _a, _b; | ||
super.init(); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(CUP(this.height - 1)); | ||
(_b = this.refresh) === null || _b === void 0 ? void 0 : _b.call(this); | ||
} | ||
done() { | ||
var _a; | ||
if (this.progressBuffer.length > this.height) { | ||
this.dumpBuffer(); | ||
} | ||
else { | ||
this.refresh(); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(CUP(this.height, this.width) + '\x1b[0m\n'); | ||
} | ||
console = this.originalConsole; | ||
this.originalConsole = null; | ||
} | ||
upsertProgressBottom(options) { | ||
/** Add or Update Progress Entry | ||
* | ||
* @param options | ||
* index: number | ||
* data: string | ||
*/ | ||
upsertProgress(options) { | ||
// Reactivate console intercepting | ||
this.checkConsoleIntercept(); | ||
// If the progress we're upserting exists already, just update. | ||
if (options.index < this.progressHeight) { | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
if (options.refresh === undefined || options.refresh) | ||
this.refresh(); | ||
return; | ||
} | ||
const numToExtend = 1 + options.index - this.progressBuffer.length; | ||
// Truncate progress line to console width. | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
// Extend the progress bars section. | ||
this.progressHeight = Math.max(options.index + 1, this.progressHeight); | ||
this.refresh(); | ||
} | ||
updateProgress(options) { | ||
this.checkConsoleIntercept(); | ||
this.progressBuffer[options.index] = clampString(options.data, this.width); | ||
if (options.refresh) { | ||
this.refresh(); | ||
// If we're not increasing the progress bars section, we're done. | ||
if (numToExtend <= 0) { | ||
return; | ||
} | ||
// Extend the progress bars section | ||
this.progressHeight = Math.min(this.progressHeight + numToExtend, this.height); | ||
} | ||
writeLinesTop(...indexes) { | ||
var _a; | ||
let writeString = indexes.reduce((prev, index) => { | ||
return prev += CUP(index) + this.progressBuffer[index]; | ||
}, ''); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(writeString); | ||
} | ||
writeLinesBottom(...indexes) { | ||
var _a; | ||
const firstProgressLine = this.height - this.progressHeight; | ||
let writeString = indexes.reduce((prev, index) => { | ||
return prev += CUP(firstProgressLine + index) + this.progressBuffer[index]; | ||
}, ''); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(writeString); | ||
} | ||
/* Prints out the buffers as they are */ | ||
refreshTop() { | ||
refresh() { | ||
var _a; | ||
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'; | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString); | ||
} | ||
refreshBottom() { | ||
var _a; | ||
const bufferStartIndex = Math.max(this.progressBuffer.length - this.currentHeightMinusBorders(), 0); | ||
const firstProgressLine = this.height - this.progressHeight; | ||
const outString = this.progressBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ (this.progressBuffer.length ? '\n' : '') | ||
+ CUP(firstProgressLine); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString); | ||
const outString = [ | ||
((this.topBorder === undefined) ? // Top border or null | ||
null | ||
: (this.topBorder + EL(EL_MODE.TO_END))), | ||
this.progressBuffer // Progress bars or [] | ||
.slice(bufferStartIndex) | ||
.map((val) => val + EL(EL_MODE.TO_END)) | ||
.join('\n'), | ||
((this.bottomBorder === undefined) ? // Bottom border or null | ||
null | ||
: (this.bottomBorder + EL(EL_MODE.TO_END))), | ||
].filter((v) => { | ||
return (v !== undefined) && (v !== '') && (v !== null); // Remove falsey/empty values | ||
}).join('\n'); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString + CUP(firstProgressLine)); | ||
} | ||
logTop(...data) { | ||
log(...data) { | ||
var _a; | ||
const writeString = format.apply(null, data); | ||
// Split by newlines, and then split the resulting lines if they run longer than width. | ||
const clampedLines = writeString.split('\n').reduce((prev, curr) => { | ||
const clamped = []; | ||
do { | ||
let width = curr.length; | ||
let front = curr; | ||
while (stringWidth(front) > this.width) { | ||
front = curr.slice(0, width); | ||
width--; | ||
} | ||
curr = curr.slice(width); | ||
clamped.push(front); | ||
} while (curr.length > 0); | ||
return [...prev, ...clamped]; | ||
}, []); | ||
this.consoleBuffer.push(...clampedLines); | ||
// If the console buffer is higher than console height, remove the top, and print them first. | ||
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') // print the previously removed top lines | ||
+ (topLines.length ? '\n' : '') // separator | ||
+ this.progressBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') // progress bars | ||
+ (this.progressBuffer.length ? '\n' : '') | ||
+ this.consoleBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') // rest of the console log. | ||
+ '\n'; | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString); | ||
} | ||
logBottom(...data) { | ||
var _a; | ||
const writeString = format.apply(null, data); | ||
let clampedLines = []; | ||
if (data.length !== 0) { | ||
const writeString = format.apply(null, data); | ||
// Split by newlines, and then split the resulting lines if they run longer than width. | ||
clampedLines = splitLinesAndClamp(writeString, this.width); | ||
} | ||
const firstProgressLine = this.height - this.progressHeight; | ||
const outString = this.progressBuffer.map((_) => EL(EL_MODE.ENTIRE_LINE)).join('\n') | ||
+ CUP(firstProgressLine) | ||
+ writeString | ||
+ '\n' | ||
+ this.progressBuffer.map((val) => val + EL(EL_MODE.TO_END)).join('\n') | ||
+ (this.progressBuffer.length ? '\n' : '') | ||
+ CUP(firstProgressLine); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString); | ||
const bufferStartIndex = Math.max(this.progressBuffer.length - this.currentHeightMinusBorders(), 0); | ||
const outString = [ | ||
clampedLines.map((val) => val + EL(EL_MODE.TO_END)).join('\n'), | ||
((this.topBorder === undefined) ? // Top border or null | ||
null | ||
: (this.topBorder + EL(EL_MODE.TO_END))), | ||
this.progressBuffer // Progress bars or [] | ||
.slice(bufferStartIndex) | ||
.map((val) => val + EL(EL_MODE.TO_END)) | ||
.join('\n'), | ||
((this.bottomBorder === undefined) ? // Bottom border or null | ||
null | ||
: (this.bottomBorder + EL(EL_MODE.TO_END))), | ||
].filter((v) => { | ||
return (v !== undefined) && (v !== '') && (v !== null); // Remove falsey/empty values | ||
}).join('\n'); | ||
(_a = this.stream) === null || _a === void 0 ? void 0 : _a.write(outString + CUP(firstProgressLine)); | ||
} | ||
@@ -299,2 +377,3 @@ } | ||
this.endIdx = 0; // 1 past the last index | ||
this.allFinished = false; | ||
this.cleanup = () => { | ||
@@ -310,5 +389,9 @@ var _a; | ||
}; | ||
const { stream = process.stdout, spinnerFPS = 10, spinnerGenerator = this.hilbertSpinner, anchor = 'bottom', persist = false, border = false, } = options || {}; | ||
const { | ||
// see https://github.com/kamiyo/multi-progress-bars/issues/7 | ||
stream = process.stdout.isTTY ? process.stdout : process.stderr, spinnerFPS = 10, spinnerGenerator = this.hilbertSpinner, anchor = 'bottom', persist = false, border = false, } = options || {}; | ||
let { progressWidth = 40, numCrawlers = 4, initMessage, } = options || {}; | ||
this.logger = new VirtualConsole({ stream, anchor }); | ||
this.logger = (anchor === 'top') ? | ||
new VirtualConsoleTop({ stream }) | ||
: new VirtualConsoleBottom({ stream }); | ||
this.persist = persist; | ||
@@ -318,3 +401,4 @@ this.spinnerFPS = Math.min(spinnerFPS, 60); | ||
this.border = (typeof border === 'boolean') | ||
? (!border) ? null : '\u2500' | ||
? (!border) | ||
? null : '\u2500' | ||
: border; | ||
@@ -355,3 +439,3 @@ if (progressWidth % 2 !== 0) { | ||
} | ||
if (this.border && this.logger.anchor === 'top') { | ||
if (this.border && anchor === 'top') { | ||
this.bottomBorder = | ||
@@ -365,10 +449,9 @@ clampString(this.border.repeat(Math.ceil(this.logger.width / this.border.length)), this.logger.width); | ||
process.on('SIGINT', this.cleanup); | ||
this.logger.upsertProgress({ | ||
index: 0, | ||
data: message, | ||
}); | ||
this.initialLines = 1; | ||
message && this.logger.setTopBorder(message); | ||
(this.bottomBorder !== undefined) && this.logger.setBottomBorder(this.bottomBorder); | ||
this.promise = new Promise((res, _) => this.resolve = res); | ||
} | ||
addTask(name, _a) { | ||
var { index } = _a, options = __rest(_a, ["index"]); | ||
this.restartPromiseIfNeeded(); | ||
// if task exists, update fields | ||
@@ -410,12 +493,39 @@ if (this.tasks[name] !== undefined) { | ||
this.longestNameLength = Math.max(this.longestNameLength, stringWidth(name)); | ||
// Reset promise for end hook | ||
this.promise = new Promise((res, _) => this.resolve = res); | ||
// Rerender previously finished tasks so that the task names are padded correctly. | ||
// Do this by calling done() again. | ||
Object.entries(this.tasks).forEach(([name, { done, type, message }]) => { | ||
if (done && type === 'indefinite') { | ||
this.done(name, { message }); | ||
} | ||
// Rerender other tasks so that task names are padded correctly. | ||
Object.values(this.tasks).forEach((task) => { | ||
this.writeTask(task); | ||
}); | ||
this.logger.refresh(); | ||
} | ||
// Call this BEFORE you add a new task to the list | ||
restartPromiseIfNeeded() { | ||
// check if allFinished previously | ||
if (this.allFinished) { | ||
this.allFinished = false; | ||
this.promise = new Promise((res) => this.resolve = res); | ||
} | ||
} | ||
isDone(name) { | ||
return this.tasks[name].done; | ||
} | ||
// public removeTask(name: string) { | ||
// const idxToRemove = this.tasks[name].index; | ||
// delete this.tasks[name]; | ||
// this.longestNameLength = Object.entries(this.tasks).reduce((prev, [taskName, { index }]) => { | ||
// // What?! Side-effects in reduce?! | ||
// // Don't worry, we're not functional purists here. | ||
// // Decrement all indexes after the one to remove. | ||
// if (index > idxToRemove) { | ||
// this.tasks[taskName].index--; | ||
// } | ||
// return Math.max(prev, stringWidth(taskName)); | ||
// }, 0); | ||
// // Rerender previously finished tasks so that the task names are padded correctly. | ||
// // Do this by calling done() again. | ||
// Object.entries(this.tasks).forEach(([name, { done, message }]) => { | ||
// if (done) { | ||
// this.done(name, { message }); | ||
// } | ||
// }); | ||
// } | ||
progressString(task) { | ||
@@ -459,11 +569,5 @@ const { name, barColorFn, message, percentage, } = task; | ||
this.logger.upsertProgress({ | ||
index: task.index + this.initialLines, | ||
index: task.index, | ||
data: this.progressString(task), | ||
}); | ||
if (this.bottomBorder) { | ||
this.logger.upsertProgress({ | ||
index: Object.keys(this.tasks).length + this.initialLines, | ||
data: this.bottomBorder, | ||
}); | ||
} | ||
} | ||
@@ -485,4 +589,6 @@ incrementTask(name, _a = {}) { | ||
updateTask(name, options = {}) { | ||
if (this.tasks[name] === undefined) | ||
if (this.tasks[name] === undefined) { | ||
throw new ReferenceError('Task does not exist.'); | ||
} | ||
this.restartPromiseIfNeeded(); | ||
const task = this.tasks[name]; | ||
@@ -499,3 +605,2 @@ // Going over 1(00%) calls done | ||
this.intervalID = setInterval(() => this.renderIndefinite(), 1000 / this.spinnerFPS); | ||
this.promise = new Promise((res, _) => this.resolve = res); | ||
} | ||
@@ -505,2 +610,3 @@ return; | ||
this.writeTask(this.tasks[name]); | ||
this.logger.refresh(); | ||
} | ||
@@ -513,9 +619,6 @@ done(name, _a = {}) { | ||
const task = this.tasks[name]; | ||
const bar = task.barColorFn(this.FULL_CHAR.repeat(this.progressWidth)); | ||
this.logger.upsertProgress({ | ||
index: task.index + this.initialLines, | ||
data: name.padStart(this.longestNameLength) + ': ' + bar + ' ' + message, | ||
}); | ||
this.writeTask(task); | ||
this.logger.refresh(); | ||
// Stop animation if all tasks are done, and resolve the promise. | ||
if (Object.entries(this.tasks).reduce((prev, [_, curr]) => { | ||
if (Object.values(this.tasks).reduce((prev, curr) => { | ||
return prev && curr.done; | ||
@@ -525,2 +628,3 @@ }, true)) { | ||
this.intervalID = null; | ||
this.allFinished = true; | ||
this.resolve(); | ||
@@ -533,4 +637,6 @@ if (!this.persist) { | ||
restart(name, options) { | ||
if (this.tasks[name] === undefined) | ||
this.restartPromiseIfNeeded(); | ||
if (this.tasks[name] === undefined) { | ||
throw new ReferenceError('Task does not exist.'); | ||
} | ||
this.tasks[name] = Object.assign(Object.assign(Object.assign({}, this.tasks[name]), options), { percentage: 0, done: false }); | ||
@@ -544,7 +650,8 @@ if (this.tasks[name].type === 'indefinite' && !this.intervalID) { | ||
this.writeTask(this.tasks[name]); | ||
this.logger.refresh(); | ||
} | ||
this.promise = new Promise((res, _) => this.resolve = res); | ||
} | ||
close() { | ||
this.logger.done(); | ||
var _a; | ||
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.done(); | ||
} | ||
@@ -594,5 +701,4 @@ // Returns the index of task with supplied name. Returns undefined if name not found. | ||
this.logger.upsertProgress({ | ||
index: task.index + this.initialLines, | ||
index: task.index, | ||
data: progressString, | ||
refresh: false, | ||
}); | ||
@@ -599,0 +705,0 @@ } |
@@ -53,1 +53,2 @@ export declare const RESET: string; | ||
export declare const clampString: (message: string, width: number) => string; | ||
export declare const splitLinesAndClamp: (writeString: string, maxWidth: number) => string[]; |
@@ -5,3 +5,2 @@ /// <reference types="node" /> | ||
stream: WriteStream; | ||
anchor: 'top' | 'bottom'; | ||
} | ||
@@ -16,24 +15,36 @@ export interface AddProgressOptions { | ||
export declare class VirtualConsole { | ||
private progressBuffer; | ||
private consoleBuffer; | ||
private height; | ||
private progressHeight; | ||
private consoleHeight; | ||
private originalConsole; | ||
private stream; | ||
protected progressBuffer: string[]; | ||
protected height: number; | ||
/** Progress section height, will not exceed total terminal height | ||
* As opposed to progressBuffer.length, which is unbounded | ||
*/ | ||
protected progressHeight: number; | ||
protected topBorder: string; | ||
protected bottomBorder: string; | ||
protected originalConsole: Console; | ||
protected stream: WriteStream; | ||
width: number; | ||
anchor: 'top' | 'bottom'; | ||
done: () => void; | ||
warn: Console['warn']; | ||
error: Console['error']; | ||
upsertProgress: (options: UpsertProgressOptions) => void; | ||
writeLines: (...indexes: number[]) => void; | ||
refresh: () => void; | ||
log: (...data: any[]) => void; | ||
constructor(options: VirtualConsoleCtorOptions); | ||
checkConsoleIntercept(): void; | ||
resize(): void; | ||
gotoBottom(): void; | ||
cleanup(): void; | ||
done(): void; | ||
refresh(): void; | ||
log(..._: any[]): void; | ||
upsertProgress(_: AddProgressOptions): void; | ||
init(): void; | ||
setTopBorder(border: string): void; | ||
setBottomBorder(border: string): void; | ||
protected currentHeightMinusBorders(): number; | ||
dumpBuffer(): void; | ||
getBuffer(): string[]; | ||
} | ||
export declare class VirtualConsoleTop extends VirtualConsole { | ||
private consoleBuffer; | ||
private consoleHeight; | ||
constructor(options: VirtualConsoleCtorOptions); | ||
setTopBorder(border: string): void; | ||
setBottomBorder(border: string): void; | ||
done(): void; | ||
/** Add or Update Progress Entry | ||
@@ -45,11 +56,24 @@ * | ||
*/ | ||
upsertProgressTop(options: UpsertProgressOptions): void; | ||
upsertProgressBottom(options: UpsertProgressOptions): void; | ||
updateProgress(options: UpsertProgressOptions): void; | ||
writeLinesTop(...indexes: number[]): void; | ||
writeLinesBottom(...indexes: number[]): void; | ||
refreshTop(): void; | ||
refreshBottom(): void; | ||
logTop(...data: any[]): void; | ||
logBottom(...data: any[]): void; | ||
upsertProgress(options: AddProgressOptions): void; | ||
getOutString(bufferStartIndex: number, topLines: string[]): string; | ||
refresh(): void; | ||
log(...data: any[]): void; | ||
/** STUB | ||
* | ||
*/ | ||
removeProgressSlot(): void; | ||
} | ||
export declare class VirtualConsoleBottom extends VirtualConsole { | ||
constructor(options: VirtualConsoleCtorOptions); | ||
init(): void; | ||
done(): void; | ||
/** Add or Update Progress Entry | ||
* | ||
* @param options | ||
* index: number | ||
* data: string | ||
*/ | ||
upsertProgress(options: AddProgressOptions): void; | ||
refresh(): void; | ||
log(...data: any[]): void; | ||
} |
{ | ||
"name": "multi-progress-bars", | ||
"version": "3.2.4", | ||
"version": "4.0.0-alpha.0", | ||
"description": "Multiple progress bars with option for indefinite spinners", | ||
@@ -5,0 +5,0 @@ "main": "dist/multi-progress-bars.cjs.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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
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
86716
1603
1