Spectre.Console
Repl.Spectre integrates Spectre.Console into Repl. Your handlers return plain .NET objects; Spectre.Console renders them as rich terminal output.
dotnet add package Repl.Spectrevar app = ReplApp.Create().UseDefaultInteractive();app.UseSpectreConsole(); // rich prompts + renderables in one callRich output renderables
Section titled “Rich output renderables”Return a Spectre.Console IRenderable directly from your handler:
app.Map("contacts table", static (IContactStore store) =>{ var table = new Table() .AddColumn("Id") .AddColumn("Name") .AddColumn("Email");
foreach (var c in store.All()) table.AddRow(c.Id.ToString(), c.Name, c.Email);
return table;});┌────┬───────┬──────────────────────┐│ Id │ Name │ Email │├────┼───────┼──────────────────────┤│ 1 │ Alice │ alice@example.com ││ 2 │ Bob │ bob@example.com │└────┴───────┴──────────────────────┘Charts
Section titled “Charts”app.Map("stats chart", static (IContactStore store) =>{ var chart = new BarChart() .Width(60) .Label("Contacts by domain");
foreach (var g in store.All().GroupBy(c => c.Email.Split('@')[1])) chart.AddItem(g.Key, g.Count(), Color.Blue);
return chart;});Progress bars
Section titled “Progress bars”using Repl.Interaction;
app.Map("import {file}", static async (string file, IReplInteractionChannel ui, CancellationToken ct) =>{ var lines = await File.ReadAllLinesAsync(file, ct);
for (int i = 0; i < lines.Length; i++) { await ImportLine(lines[i], ct); await ui.WriteProgressAsync($"Importing… {i + 1}/{lines.Length}", (double)(i + 1) / lines.Length, ct); }
return Results.Success($"Imported {lines.Length} records.");});With UseSpectreConsole(), progress renders as a live Spectre.Console Progress bar.
Rich prompts
Section titled “Rich prompts”UseSpectreConsole() upgrades all IReplInteractionChannel prompts to Spectre.Console widgets automatically:
using Repl.Interaction;
app.Map("add", static async (IReplInteractionChannel ui, IContactStore store, CancellationToken ct) =>{ var name = await ui.AskTextAsync("name", "Full name:", ct: ct); var email = await ui.AskTextAsync("email", "Email address:", ct: ct); store.Add(name, email); return Results.Success($"Added {name}.");});No handler changes are needed when switching between terminals — UseSpectreConsole() upgrades the rendering layer and all prompts degrade gracefully on plain terminals.
Available renderables
Section titled “Available renderables”Any Spectre.Console renderable works as a return value: Table, Panel, Tree, BarChart, BreakdownChart, Calendar, JsonText, Markup, FigletText, TextPath, Grid, Columns, Rule.
Capability detection
Section titled “Capability detection”Spectre.Console detects terminal capabilities automatically. On terminals without color support, output degrades gracefully to plain text.
Further reading
Section titled “Further reading”- Interactivity — the
IReplInteractionChannelAPI and how Spectre upgrades each prompt type. - Best Practices — output routing, why
AnsiConsolestatic must be avoided, andIAnsiConsoleinjection.