Beautiful console applications with Spectre.Console

Spectre Console

Using the new NuGet package, we can create beautiful console applications with Spectre.Console that are not boring or monochrome. The repository of this project is on GitHub.

The repository of this demo is on GitHub too.

Sure, you can use Console.WriteLine and Console.ReadLine to output some text and get some input, but that’s pretty boring and limited. What if you want to display some colors, styles, tables, trees, progress bars, or even ASCII images?

Beautiful console applications with Spectre.Console - Demo

What is Spectre.Console?

Spectre.Console is a .NET library that makes it easier to create beautiful console applications. It is heavily inspired by the excellent Rich library for Python written by Will McGugan. Also, ut supports 3/4/8/24-bit colors in the terminal with auto-detection of the current terminal’s capabilities. This library provides a rich markup language that lets you easily output text with different colors and styles such as bold, italic, and blinking. So grab a cup of coffee and let’s dive in!

Installing Spectre.Console

To install Spectre.Console, you need to use NuGet Package Manager. You can either use Visual Studio or the dotnet CLI tool.

If you are using Visual Studio, right-click on your project and select Manage NuGet Packages. Then search for Spectre.Console and install it.

If you are using dotnet CLI tool, run this command in your project folder:

dotnet add package Spectre.Console

Using Spectre.Console

To use Spectre.Console, you need to import its namespace:

using Spectre.Console;

Then you can access its main class AnsiConsole, which provides various methods for outputting text and rendering widgets.

For example, here is how you can output some text with different colors and styles:

AnsiConsole.MarkupLine("[bold green]Hello[/] [italic blue]World[/]!");

Also, you can use RGB or HEX values for specifying colors:

AnsiConsole.MarkupLine("This is [rgb(255;0;0)]red[/], this is [rgb(0;255;0)]green[/], this is [rgb(0;0;255)]blue[/].");
AnsiConsole.MarkupLine("This is [#ff0000]red[/], this is [#00ff00]green[/], this is [#0000ff]blue[/].");

You can also nest tags for combining colors and styles:

AnsiConsole.MarkupLine("[bold red on yellow blink underline]Warning![/] This is very [italic green on black strikethrough]important[/].");

Rendering widgets

Spectre.Console also provides various widgets that you can render in your console applications. Some of them are:

  • Tables: Display tabular data with customizable headers, footers, borders, and alignment.
  • Trees: Display hierarchical data with expandable nodes and icons.
  • Bar Chart: Display a horizontal bar chart on the console.
  • Progress: Display progress for long-running tasks with live updates of progress bars and status controls.
  • and many others

Here are some examples of how to render these widgets:

Tables

To render a table, you need to create an instance of Table class and add some columns and rows. You can also customize its appearance by setting properties such as BorderStyleBorderTitleCaption, etc.

For example,

var table = new Table();
table.AddColumn("Name");
table.AddColumn("Age");
table.AddColumn("Occupation");

table.AddRow("Alice", "23", "Software Engineer");
table.AddRow("Bob", "32", "Accountant");
table.AddRow("Charlie", "28", "Teacher");

table.Title = new TableTitle("[underline yellow]People[/]");
table.Caption = new TableTitle("[grey]Some random people[/]");

AnsiConsole.Write(table);

You can find more details about the table widget here: https://spectreconsole.net/widgets/table

Trees

To render a tree, you need to create an instance of Tree class and add some nodes. You can also customize its appearance by setting properties such as StyleGuideExpand, etc.

For example,

var tree = new Tree("[yellow]Root[/]");

var child1 = tree.AddNode(new Markup("[green]Child 1[/]"));
var child2 = tree.AddNode(new Markup("[green]Child 2[/]"));
var child3 = tree.AddNode(new Markup("[green]Child 3[/]"));

child1.AddNode("[blue]Grandchild 1-1[/]");
child1.AddNode("[blue]Grandchild 1-2[/]");

child2.AddNode("[green]Grandchild 2-1[/]");

var grandchild3 = child3.AddNode("[green]Grandchild 3-1[/]");
child3.AddNode("[green]Grandchild 3-2[/]");
grandchild3.AddNode("[yellow]Great Grandchild 3-1-1[/]");
grandchild3.AddNode("[yellow]Great Grandchild 3-1-2[/]");

AnsiConsole.Write(tree);

You can find more details about the tree widget here: https://spectreconsole.net/widgets/tree

Progress

To render progress, you need to create an instance of Progress class and add some tasks. You can also customize its appearance by setting properties such as AutoClearAutoRefreshColumns, etc.

For example,

await AnsiConsole.Progress()
    .StartAsync(async ctx =>
    {
        // Define tasks
        var task1 = ctx.AddTask("[green]Chrome RAM usage[/]");
        var task2 = ctx.AddTask("[yellow]VS Code RAM usage[/]");

        while (!ctx.IsFinished)
        {
            // Simulate some work
            await Task.Delay(100);

            // Increment
            task1.Increment(4.5);
            task2.Increment(2);
        }
    });

You can find more details about the progress widget here: https://spectreconsole.net/live/progress

Bar Chart

To render a bar chart, you need to create an instance of BarChart class and add some items with labels, values, and colors. You can also customize its appearance by setting properties such as Width, Label, CenterLabel, etc.

// Create a bar chart
AnsiConsole.Write(new BarChart()
    .Width(60)
     // Set the label of the chart
    .Label("[green bold underline]Global Smartphone Shipments Market Share (%)[/]")
     //And center it
    .CenterLabel()
    // Add the items with lables, values, and colors
    .AddItem("Apple", 23, Color.Yellow) 
    .AddItem("Samsung", 19, Color.Green)
    .AddItem("Xiaomi", 11, Color.Red)
    .AddItem("OPPO", 10, Color.Blue)
    .AddItem("Vivo", 8, Color.DarkMagenta)
    .AddItem("Others", 29, Color.Orange1));

Using Live Display

Spectre.Console can update arbitrary widgets in place using the Live Display widget.

This can be useful for creating dynamic tables that show changing data over time. The live display is not thread-safe, and using it together with other interactive components such as prompts, status displays, or other progress displays is not supported.

To render a live table, you need to create a Table instance and add some columns and rows. Then you need to pass the table to AnsiConsole.Live() method and call Start() or StartAsync() with an action or a function that updates the table content. You can use ctx.Refresh() to refresh the display after each update.

// Create a table
var table = new Table()
    .Border(TableBorder.Rounded)
    .AddColumn("Id")
    .AddColumn("Name")
    .AddColumn("Age");

// Add some initial rows
table.AddRow(Faker.Identification.SocialSecurityNumber(), Faker.Name.First(),
    Faker.RandomNumber.Next(18, 99).ToString());
table.AddRow(Faker.Identification.SocialSecurityNumber(), Faker.Name.First(),
    Faker.RandomNumber.Next(18, 99).ToString());

// Use LiveDisplay to update the table
await AnsiConsole.Live(table)
    .StartAsync(async ctx =>
    {
        // Loop until we are done
        for (int i = 0; i < 5; i++)
        {
            var id = Faker.Identification.SocialSecurityNumber();
            var name = Faker.Name.First();
            var age = Faker.RandomNumber.Next(18, 99);
            table.AddRow(id, name, age.ToString());

            ctx.Refresh();

            // Simulate doing the work
            await Task.Delay(1000);
        }
    });

Faker.net

In this piece of code, I use Faker.net for generating random values. Available as a NuGet package.

C# port of the Ruby Faker gem (https://faker.rubyforge.org/) and is used to easily generate fake data:

  • addresses (UK, US),
  • boolean,
  • companies,
  • countries,
  • currencies,
  • enums,
  • finance (isin, ticker, coupon, maturity, bond name),
  • identification (social security number (US), MBI (US), national insurance number (UK), passport number (UK & US), Bulgarian Person Identification Number(PIN/ENG))
  • internet (email, domain names, user names),
  • lorem ipsum,
  • names,
  • phone numbers

Here an example of what this library can generate:

var name = Faker.Name.FullName(); // Tod Yundt
var firstName = Faker.Name.First(); // Orlando
var lastName = Faker.Name.Last(); // Brekke
var address = Faker.Address.StreetAddress(); // 713 Pfeffer Bridge
var city = Faker.Address.City(); // Reynaton
var number = Faker.RandomNumber.Next(100); // 30
var dob = Faker.Identification.DateOfBirth(); // 1971-11-16T00:00:00.0000000Z

// US - United States
var ssn = Faker.Identification.SocialSecurityNumber(); // 249-17-9666
var mbi = Faker.Identification.MedicareBeneficiaryIdentifier(); // 8NK0Q74KT53
var usPassport = Faker.Identification.UsPassportNumber(); // 335587506

// UK - United Kingdom
var nin = Faker.Identification.UkNationalInsuranceNumber(); // YA171053Y
var ninFormatted = Faker.Identification.UkNationalInsuranceNumber(true); // YA 17 10 53 Y
var ukPassport = Faker.Identification.UkPassportNumber(); // 496675685
var ukNhs = Faker.Identification.UkNhsNumber(); // 6584168301
var ukNhsFormatted = Faker.Identification.UkNhsNumber(true); // 658 416 8301

// BG - Bulgaria
var bulgarianPin = Faker.Identification.BulgarianPin(); //6402142606

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.