Open Ryux opened 6 years ago
Can you elaborate? Just one progress bar for all the tasks, or as output of a certain task?
+1 I think that an option to enable progress bar for certain tasks would be great
+1 I use progressbar but listr gets a little crazy and begins to duplicate tasks (he executes them once, but he shows them many times)
Hi,
In my case, it would be to have to progress bar for a certain task. Some of my jobs are slow and I need to send a feedback to the user. A progress bar would be perfect.
Thanks
I think what you're looking for could be accomplished using an observable. This is implemented in TypeScript (I wish the listr definitions were in the main project :wink: ).
Example:
import Listr = require("listr");
import rxjs = require("rxjs");
function createBar(progress: number, length: number): string {
// Determine distance finished.
let distance = progress * length;
// Start progress bar.
let bar = "[";
// Add main portion.
bar += "=".repeat(Math.floor(distance));
// Add intermediate porttion.
bar += distance % 1 > 0.5 ? "-" : "";
// Extend empty bar.
bar += " ".repeat(length > bar.length ? length - bar.length : 0);
// Cap progress bar.
bar += "]";
return bar;
}
const listr = new Listr([
{
title: "Placeholder",
task: () =>
new rxjs.Observable(observer => {
let progress = 0;
let timer = setInterval(() => {
// Calculate current progress.
if (progress < 1) {
progress += 0.002;
observer.next(createBar(progress, 40));
} else {
clearInterval(timer);
observer.complete();
}
}, 10);
})
}
]);
listr.run();
// Approximate Output (s=spinner):
// S Placeholder
// > [=====- ]
If this was to be implemented, should there be several presets? @SamVerschueren @Ryux
I'm worried that a progress bar implementation might fit better in a separate package, similar to cli-spinners
.
For example, a simple bar with a format string "<endCap><baseFiller><partial1><partial2>,,,<endCap>"
and a percentage formatter:
/** Function that converts a number [0,1] to a string representation. */
type ProgressView = (progress: number, ...args: any[]) => string;
const regexBarFormat = /^(.)(.+)(.)$/;
interface BarFormat {
caps: { left: string; right: string };
components: string[];
empty: string; // Defaults to a space.
}
type StrBarFormat = string | BarFormat;
/** Produce an easier to use formatting object. */
function genFormat(format: StrBarFormat): BarFormat {
// Check if format is a BarFormat through exclusion.
if (format.constructor !== String) {
return format as BarFormat;
}
// Break up the formatting string.
let [match, capLeft, componentString, capRight] = (format as string).match(
regexBarFormat
) as Array<string>;
return {
caps: { left: capLeft, right: capRight },
components: componentString.split(""),
empty: " "
};
}
/**
* Standard bar format.
*
* Example: [==- ]
*/
function bar(progress: number, length: number, format: StrBarFormat): string {
let barFormat = genFormat(format);
// Account for end caps.
let innerLength =
length - (barFormat.caps.left.length + barFormat.caps.right.length);
let distance = progress * innerLength;
let base = Math.floor(distance);
let increment = Math.floor((distance % 1) * barFormat.components.length);
// Begin the inside.
let inside = barFormat.components[0].repeat(base);
// Append the partial increment.
if (base < distance && increment > 0) {
inside += barFormat.components[increment];
}
// Append missing empty space.
inside += barFormat.empty.repeat(innerLength - inside.length);
return barFormat.caps.left + inside + barFormat.caps.right;
}
/**
* Simple percentage formatter.
*
* Example: 37.6%
*/
function percentage(progress: number, precision: number = 1): string {
return (progress * 100).toFixed(precision) + "%";
}
In case anyone's looking to implement a progress bar without using observables, here's a basic implementation:
const repeat = (n, char) => [...new Array(Math.round(n))].map(() => char);
const progress = (percentage, size = 20) => {
const track = repeat(size, " ");
const completed = repeat((percentage / 100) * size, "=");
return `${completed.join("")}${track.join("")}`.substr(0, size);
};
Then inside your task function somewhere:
const interval = setInterval(() => {
task.output = `[${progress(ctx.percentage)}] ${ctx.percentage.toFixed(1)}%`;
if (ctx.percentage >= 100) clearInterval(interval);
}, 200)
This assumes you're keeping track of progress in ctx.percentage
. Of course you could do this without an interval, it totally depends on what you're tracking progress of. Usually there's an update handler of sorts, in which case you don't need the interval.
Feel free to use and adapt.
Hi,
Is it possible to add a progress bar to see the different part of a process ?
Example: to see the download of a file
Thanks