Console.Lib 1.4.91

This package has a SemVer 2.0.0 package version: 1.4.91+834d04c9b6bea65a89679010e0a02a414fde3e11.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Console.Lib --version 1.4.91
                    
NuGet\Install-Package Console.Lib -Version 1.4.91
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Console.Lib" Version="1.4.91" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Console.Lib" Version="1.4.91" />
                    
Directory.Packages.props
<PackageReference Include="Console.Lib" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Console.Lib --version 1.4.91
                    
#r "nuget: Console.Lib, 1.4.91"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Console.Lib@1.4.91
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Console.Lib&version=1.4.91
                    
Install as a Cake Addin
#tool nuget:?package=Console.Lib&version=1.4.91
                    
Install as a Cake Tool

Console.Lib

A .NET library for building terminal applications with dock-based layouts, widgets, mouse/keyboard input, VT styling, and Sixel graphics rendering. AOT-compatible, targeting .NET 10.

Architecture overview

classDiagram
    direction TB

    class ITerminalViewport {
        <<interface>>
    }
    class IVirtualTerminal {
        <<interface>>
    }
    class VirtualTerminal
    class TerminalViewport

    class TerminalLayout {
        +Dock(style, size) TerminalViewport
        +Recompute() bool
    }
    class Panel {
        +Dock(style, size) ITerminalViewport
        +Fill() ITerminalViewport
        +Add(widget) Panel
        +RenderAll()
        +Recompute() bool
    }

    class Widget {
        <<abstract>>
        +Render()*
        +HitTest(pixelX, pixelY)
    }
    class TextBar
    class ScrollableList~TItem~
    class Canvas~TSurface~ {
        +Render()*
        +Render(clip)
    }
    class IRowFormatter {
        <<interface>>
    }

    class Renderer~TSurface~ {
        <<abstract>>
        +Surface TSurface
    }
    class SixelRenderer~TSurface~ {
        <<abstract>>
        +EncodeSixel(output)*
    }
    class SixelEncoder {
        +Encode()$
    }

    class MenuBase~T~ {
        <<abstract>>
    }
    class VtStyle {
        <<record struct>>
        +Apply(colorMode) string
    }
    class ColorMode {
        <<enum>>
    }
    class ConsoleInputEvent {
        <<record struct>>
    }

    IVirtualTerminal --|> ITerminalViewport
    VirtualTerminal ..|> IVirtualTerminal
    TerminalViewport ..|> ITerminalViewport
    TerminalViewport --> ITerminalViewport : parent

    TerminalLayout --> TerminalViewport : creates
    Panel --> TerminalLayout : uses

    Widget --> ITerminalViewport : viewport
    TextBar --|> Widget
    ScrollableList --|> Widget
    Canvas --|> Widget
    ScrollableList ..> IRowFormatter : TItem

    Panel --> Widget : children

    SixelRenderer --|> Renderer
    Canvas ..> SixelRenderer : Render
    VtStyle --> ColorMode : Apply

    MenuBase --> IVirtualTerminal : terminal
    VirtualTerminal --> ConsoleInputEvent : produces

Terminal abstraction

ITerminalViewport

The core output interface. Represents a rectangular region that supports cursor positioning, text output, and stream access:

public interface ITerminalViewport
{
    (int Column, int Row) Offset { get; }
    (int Width, int Height) Size { get; }
    void SetCursorPosition(int left, int top);
    void Write(string text);
    void WriteLine(string? text = null);
    TermCell CellSize { get; }
    (uint Width, uint Height) PixelSize { get; } // default: Size * CellSize
    void Flush();
    Stream OutputStream { get; }
    ColorMode ColorMode => ColorMode.Sgr16; // default: 16-color SGR
}

TermCell holds the pixel dimensions of a single terminal character cell, queried from the terminal during initialization via the \e[16t control sequence.

TerminalViewportExtensions

Extension methods for ITerminalViewport:

// Overwrite the current line in-place using \r, padding with spaces to erase stale content.
// Does not advance to the next line — ideal for status prompts and progress indicators.
terminal.WriteInPlace("> waiting...");

IVirtualTerminal

Extends ITerminalViewport with full terminal lifecycle: initialization, input reading, alternate screen buffer, and Sixel capability detection.

public interface IVirtualTerminal : ITerminalViewport, IAsyncDisposable
{
    Task InitAsync();
    bool HasSixelSupport { get; }
    bool HasColorSupport { get; }
    void EnterAlternateScreen();
    bool IsAlternateScreen { get; }
    void Clear();
    bool HasInput();
    ConsoleInputEvent TryReadInput();
}

VirtualTerminal is the concrete implementation backed by System.Console. On initialization it:

  1. Sets UTF-8 encoding for stdin/stdout
  2. Sends a Device Attributes request (\e[0c) to detect terminal capabilities (including Sixel support)
  3. Sends a cell size query (\e[16t) to determine pixel dimensions per character cell

When entering the alternate screen, it enables virtual terminal I/O and mouse input via WindowsConsoleInput (Windows only), then enables VT200 mouse tracking with SGR extended coordinates (\e[?1000h, \e[?1006h), parses SGR mouse events from raw stdin, and normalizes cell coordinates to pixel coordinates using the cell size.

In normal (non-alternate) screen mode, TryReadInput() uses Console.ReadKey(intercept: true) — keystrokes are not echoed, giving the caller full control over display feedback.

TerminalViewport

A sub-region of a parent viewport. Translates local coordinates to parent coordinates by adding column/row offsets. Clamps cursor positions to stay within bounds. Viewports can be nested — offsets compose through the parent chain.

var terminal = new VirtualTerminal();
// Create a 30x15 viewport starting at column 10, row 5
var viewport = new TerminalViewport(terminal, 10, 5, 30, 15);
viewport.SetCursorPosition(0, 0); // → terminal position (10, 5)
viewport.SetCursorPosition(3, 7); // → terminal position (13, 12)

Layout system

DockStyle

public enum DockStyle { Top, Bottom, Left, Right, Fill }

TerminalLayout

Computes viewport geometries using a dock-based algorithm. Edge-docked panels are allocated first in registration order, each consuming space from the remaining rectangle. The Fill panel receives whatever space remains.

var layout = new TerminalLayout(terminal);
var statusBar = layout.Dock(DockStyle.Bottom, 1);  // 1 row at bottom
var sidebar   = layout.Dock(DockStyle.Right, 24);  // 24 columns on right
var main      = layout.Dock(DockStyle.Fill);        // remainder

For an 80x24 terminal, this produces:

  • statusBar: 80x1 at (0, 23)
  • sidebar: 24x23 at (56, 0)
  • main: 56x23 at (0, 0)

Recompute() recalculates all geometries after a terminal resize, returning true if the size actually changed.

Panel

Higher-level container that wraps TerminalLayout and manages a collection of widgets:

var panel = new Panel(terminal);

var statusBar = new TextBar(panel.Dock(DockStyle.Bottom, 1));
var history   = new ScrollableList<MyRow>(panel.Dock(DockStyle.Right, 24));
var canvas    = new Canvas(panel.Fill());

panel.Add(statusBar).Add(history).Add(canvas);
panel.RenderAll(); // renders all widgets

The two-step pattern (dock creates the viewport, then pass it to a widget constructor) keeps viewport ownership clear — each widget owns exactly one viewport from construction.

Widgets

All widgets inherit from Widget, which provides:

  • Viewport — the ITerminalViewport this widget renders to
  • Render() — abstract method to draw the widget's current state
  • HitTest(pixelX, pixelY) — converts absolute pixel coordinates to viewport-local cell coordinates, returning null if outside bounds

TextBar

Single-line status bar with left-aligned and right-aligned text, styled with VtStyle:

var bar = new TextBar(viewport);
bar.Text(" Ready")
   .RightText("12.3ms ")
   .Style(new VtStyle(SgrColor.BrightWhite, SgrColor.BrightBlack))
   .Render();

ScrollableList<TItem>

Multi-row scrollable list with an optional header. Items must implement IRowFormatter:

public interface IRowFormatter
{
    string FormatRow(int width, ColorMode colorMode);
}

Each item produces its own styled row string (including VT escape codes and padding to full width). The list handles scrolling and empty-row rendering:

var list = new ScrollableList<MyRow>(viewport)
    .Header(" Items")
    .HeaderStyle(new VtStyle(SgrColor.BrightWhite, SgrColor.BrightBlack))
    .Items(rows)
    .ScrollTo(startOffset)
    .Render();

int visibleRows = list.VisibleRows; // data rows (excludes header)

Canvas<TSurface>

A generic widget that owns a SixelRenderer<TSurface> and renders it to a viewport. Provides full and partial Sixel output:

var canvas = new Canvas<MagickImage>(viewport, renderer);
var (pixelW, pixelH) = canvas.PixelSize;

canvas.Render();       // full Sixel blit
canvas.Render(clip);   // partial blit for dirty region (pixel coordinates)

Render() positions the cursor at (0, 0) and calls renderer.EncodeSixel(stream). Render(RectInt clip) aligns the clip region's Y bounds to cell-height boundaries (since Sixel output must start at a character row), then calls renderer.EncodeSixel(startY, cropHeight, stream). Only vertical clipping is performed — the full image width is always emitted, since the Sixel protocol is a left-to-right band-based format with no horizontal skip.

Sixel graphics

SixelRenderer<TSurface>

Abstract class extending Renderer<TSurface> (from DIR.Lib) with Sixel encoding:

public abstract class SixelRenderer<TSurface>(TSurface surface) : Renderer<TSurface>(surface)
{
    public abstract void EncodeSixel(Stream output);
    public abstract void EncodeSixel(int startY, uint height, Stream output);
}

Concrete implementations (e.g., MagickImageRenderer in Chess.ImageMagick) provide the actual pixel-to-Sixel encoding by extracting raw pixel data and passing it to SixelEncoder.

SixelEncoder

High-performance encoder that converts raw pixel arrays to the Sixel terminal graphics format. Key design decisions:

  • Frequency-based palette: When more than 256 unique colors exist, the most frequent colors get exact palette slots (preserving large solid areas like board tiles). Remaining colors map to their nearest palette entry.
  • Precomputed sixel grid: A single row-major pass builds sixel bits for all colors simultaneously, then each color encodes from a contiguous memory slice. This is cache-friendly and avoids the naive O(colors × rows × width) approach.
  • ArrayPool allocation: All large buffers (index map, sixel grid, palette, output buffer) are rented from ArrayPool<byte>.Shared, eliminating GC pressure from repeated allocations.
  • Partial encoding: Supports vertical slicing without image cloning — the caller extracts the pixel slice and passes the cropped dimensions.

Performance vs ImageMagick's built-in Sixel writer:

Scenario ImageMagick SixelEncoder Speedup
Full 127.3 ms 9.1 ms 14×
Partial 127.9 ms 1.6 ms 79×

Styling

VtStyle, SgrColor, and ColorMode

VtStyle stores foreground/background as RGBAColor32 (from DIR.Lib) and produces escape sequences via Apply(ColorMode):

public enum ColorMode : byte { None, Sgr16, TrueColor }

public readonly record struct VtStyle(RGBAColor32 Foreground, RGBAColor32 Background)
{
    public const string Reset = "\e[0m";
    public VtStyle(SgrColor foreground, SgrColor background); // convenience
    public string Apply(ColorMode colorMode);
}

Apply(ColorMode.Sgr16) emits standard 16-color SGR codes (\e[97;40m). Apply(ColorMode.TrueColor) emits 24-bit sequences (\e[38;2;R;G;B;48;2;R;G;Bm). ToString() defaults to Sgr16 for safe fallback.

The 16 standard SgrColor values have well-known RGB mappings via SgrColor.ToRgba(). Arbitrary RGBAColor32 values are mapped back to the nearest SgrColor when using Sgr16 mode.

// Construct with SgrColor (convenience) or RGBAColor32 (full control)
var style = new VtStyle(SgrColor.BrightYellow, SgrColor.Blue);
var custom = new VtStyle(new RGBAColor32(0x1a, 0x1a, 0x2e, 0xff), new RGBAColor32(0xe0, 0xe0, 0xe0, 0xff));

// Widgets use Apply with the viewport's color mode
terminal.Write($"{style.Apply(terminal.ColorMode)}Highlighted text{VtStyle.Reset}");

ColorMode flows through the viewport chain: VirtualTerminal returns TrueColor when HasColorSupport is true (DA capability code 22), TerminalViewport delegates to its parent, and ITerminalViewport defaults to Sgr16. ColorMode.None suppresses all escape sequences for plain-text output.

Markdown rendering

MarkdownRenderer converts Markdown to VT-styled terminal output using Markdig. Supports headings, bold, italic, links, tables, lists, horizontal rules, and inline colored text.

Colors can be applied to individual words using [text]{color} syntax, where color is either a named SgrColor (e.g. red, BrightCyan) or a #RRGGBB hex literal:

This has a [warning]{red} and a [custom tint]{#FF8800}.

Colors are resolved at render time based on the active ColorMode — in None mode, no escape sequences are emitted. Structural element colors (headings, links, bullets) are configurable via MarkdownTheme.

MarkdownWidget wraps the renderer as a scrollable viewport widget with automatic re-rendering on resize.

Input handling

ConsoleInputEvent

A unified input event that may contain a mouse event, a key press, or both:

public readonly record struct ConsoleInputEvent(MouseEvent? Mouse, ConsoleKey Key, ConsoleModifiers Modifiers);
public readonly record struct MouseEvent(int Button, int X, int Y, bool IsRelease);

Mouse coordinates are in pixels (normalized using TermCell dimensions). Button encoding follows the X11/SGR convention: 0 = left, 1 = middle, 2 = right, 64/65 = scroll up/down.

VirtualTerminal.TryReadInput() parses SGR mouse sequences (\e[<Pb;Px;Py M/m) in alternate screen mode, and falls back to Console.ReadKey in normal mode. It also parses CSI sequences for arrow keys, function keys, Home/End, Delete, PageUp/PageDown, and SS3 sequences for F1-F4.

Abstract base for fullscreen menus with arrow-key navigation, digit shortcuts, and mouse click support. In alternate screen mode, renders a centered menu with resize handling. In normal mode, falls back to a simple numbered list.

public class MyMenu(IVirtualTerminal terminal, TimeProvider timeProvider)
    : MenuBase<string>(terminal, timeProvider)
{
    protected override async Task<string> ShowAsyncCore(CancellationToken ct)
    {
        var choice = await ShowMenuAsync("Title", "Pick one:", ["A", "B", "C"], ct);
        return choice switch { 0 => "A", 1 => "B", _ => "C" };
    }
}

Platform support

  • Windows: WindowsConsoleInput enables virtual terminal I/O and mouse tracking via Win32 SetConsoleMode. Restores original console mode on dispose.
  • Unix/macOS: VT100 escape sequences work natively. Mouse tracking uses the same SGR extended format.

Terminal capability detection

During InitAsync(), VirtualTerminal sends a Primary Device Attributes request (\e[0c). The response contains capability codes parsed into the TerminalCapability enum:

Code Capability Effect
4 Sixel graphics Enables HasSixelSupport
22 Color Enables HasColorSupport, sets ColorMode to TrueColor
18 Windowing
1 132 columns

Unknown capability codes are silently ignored.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.4.92 28 3/19/2026
1.4.91 41 3/19/2026
1.3.81 63 3/19/2026
1.3.71 83 3/19/2026
1.2.61 39 3/19/2026
1.1.51 21 3/19/2026
1.1.41 22 3/19/2026
1.1.31 47 3/18/2026
1.0.21 49 3/18/2026