Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support to Terminal progress indicator to the the spectre console progress widget #1402

Open
rafaelsc opened this issue Dec 5, 2023 · 1 comment

Comments

@rafaelsc
Copy link

rafaelsc commented Dec 5, 2023

Spectre console is lacking integration with Operation System and Terminal progress Indication/feedback. And would be a nice to have feature, improving the feedback of the progress to the User.

Describe the solution you'd like
Any Progress or group of Progress update in Spectre console, update the Terminal Progress too.

Additional context


Please upvote 👍 this issue if you are interested in it.

@rafaelsc
Copy link
Author

rafaelsc commented Dec 7, 2023

Proof of concept. (Only for the progress bar, title bar update still need some work)

image

Windows.PowerShell.2023-12-06.22-49-40.mp4

Code:

using Spectre.Console;
using Spectre.Console.Rendering;

Console.CancelKeyPress += (_, _) => TerminalProgressUpdater.Clear();

var progress = AnsiConsole.Progress();
progress.RenderHook = Render;

await progress.StartAsync(async ctx =>
{
    var task1 = ctx.AddTask("[green]Reticulating splines[/]");
    var task2 = ctx.AddTask("[green]Folding space[/]");

    while (task1.Percentage < 50)
    {
        await Task.Delay(200);
        task1.Increment(1.5);
        task2.Increment(1);
    }
    
    var task3 = ctx.AddTask("[red]New Challenger[/]");

    while (!ctx.IsFinished)
    {
        await Task.Delay(100);
        task1.Increment(1.75);
        task2.Increment(1);
        task3.Increment(2);
    }
});

static IRenderable Render(IRenderable renderable, IReadOnlyList<ProgressTask> tasks)
{
    TerminalProgressUpdater.Update(tasks);
    return renderable;
}

internal static class TerminalProgressUpdater
{
    public static void Start() => AnsiConsole.Write(Ansi.Progress(State.Indeterminate, 0));
    public static void Indeterminate() => AnsiConsole.Write(Ansi.Progress(State.Indeterminate, 0));
    
    public static void Update(byte progress)
    {
        ArgumentOutOfRangeException.ThrowIfGreaterThan(progress, 100);
        AnsiConsole.Write(Ansi.Progress(State.Default, progress));
    }
    public static void Update(IReadOnlyList<ProgressTask> tasks)
    {
        ArgumentNullException.ThrowIfNull(tasks);
        if (tasks.Count == 0)
        {
            Finished();
            return;
        }
        var IsFinished = tasks.Where(x => x.IsStarted).All(task => task.IsFinished);
        if (IsFinished)
        {
            Finished();
            return;
        }
        var isIndeterminate = tasks.Any(x => x.IsIndeterminate);
        if (isIndeterminate)
        {
            Indeterminate();
            return;
        }
        var allProgress = tasks.Sum(x => x.Percentage);
        var progress = (byte)(allProgress / tasks.Count);
        Update(progress);
    }

    public static void Finished() => AnsiConsole.Write(Ansi.Progress(State.Hidden, 0));
    public static void Clear() => AnsiConsole.Write(Ansi.Progress(State.Hidden, 0));

    private static class Ansi
    {
        public const string ESC = "\u001b";
        public const string BEL = "\u0007";
        public static string Progress(State state, byte progress) => $"{ESC}]9;4;{(byte)state};{progress}{BEL}";
    }

    private enum State
    {
        Hidden = 0,
        Default = 1,
        Error = 2,
        Indeterminate = 3,
        Warning = 4,
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Todo 🕑
Development

No branches or pull requests

1 participant