A modern, declarative terminal UI library for .NET 9.0 and .NET 10.0 that brings XML-based UI definition to the console.
TermuiX offers a unique approach to terminal UI development:
- Zero Dependencies - Pure .NET implementation with no external dependencies or native bindings
- Declarative UI - Define your interface in XML, similar to web development or XAML
- Simple Rendering Model - Straightforward render loop - call
Render()when ready, no complex state management - Pure C# - No need to learn platform-specific APIs or deal with interop complexities
- Cross-Platform - Works on any platform that supports .NET 9.0/10.0 and ANSI terminals
- AOT Compatible - Full support for Native AOT compilation for minimal deployment size and fast startup
- Extensible - Register custom widgets and components for reusable UI patterns
Perfect for building dashboards, TUI tools, interactive CLIs, and terminal-based applications.
- Declarative XML-based UI - Define your terminal UI using familiar XML syntax
- Rich Widget Set - Comprehensive collection of interactive widgets with disabled state support
- Event-Driven Architecture - Clean event handling for user interactions
- Flexible Styling - Support for colors, borders, text styles (bold, italic, underline, strikethrough)
- Advanced Layout - Positioned and scrollable containers with padding support
- Keyboard Navigation - Built-in Tab/Shift+Tab navigation and custom keyboard shortcuts
- Multi-line Support - Tables and inputs with multi-line text capabilities
- Unicode & Emoji Support - Full support for emojis, East Asian characters, and wide Unicode characters
- Custom Widgets - Register your own widget types and reusable components in XML
- Widget Cloning - Deep and shallow cloning support for all widgets
- Button - Clickable buttons with customizable styling and focus states
- Input - Single-line and multi-line text input with password mode
- Checkbox - Toggle checkboxes with checked/unchecked states
- RadioButton - Mutually exclusive radio button groups
- Slider - Adjustable value slider with min/max/step configuration
- Text - Static or dynamic text with various alignments and styles
- Table - Multi-row, multi-column tables with borders and text styling
- Chart - Line charts with multiple data series and legends
- ProgressBar - Progress indicators and marquee animations
- Line - Horizontal and vertical separator lines
- Container - Layout containers with optional borders and scrolling
dotnet add package TermuiXusing System.Threading;
using TermuiX.Widgets;
var xml = """
<Container Width="100%" Height="100%" BackgroundColor="DarkBlue">
<Text PositionX="2ch" PositionY="1ch"
ForegroundColor="Yellow"
Style="Bold">
Welcome to TermuiX!
</Text>
<Button Name="myButton" PositionX="2ch" PositionY="3ch"
BorderColor="White" TextColor="Cyan"
RoundedCorners="true">
Click Me
</Button>
</Container>
""";
// Initialize and load UI
var termui = TermuiX.TermuiX.Init();
termui.LoadXml(xml);
// Get widget reference and attach event
var button = termui.GetWidget<Button>("myButton");
if (button is not null)
{
bool running = true;
button.Click += (sender, e) => running = false;
try
{
while (running)
{
termui.Render();
Thread.Sleep(16); // 60 FPS
}
}
finally
{
TermuiX.TermuiX.DeInit();
}
}<Container Width="100%" Height="100%" BackgroundColor="DarkBlue">
<Container PositionX="5ch" PositionY="5ch" Width="32ch" Height="3ch"
BorderStyle="Single" RoundedCorners="true">
<Input Name="nameInput" PositionX="0ch" PositionY="0ch"
Width="30ch" Height="1ch"
Placeholder="Enter your name..."
FocusBackgroundColor="DarkBlue"
FocusForegroundColor="White" />
</Container>
<Checkbox Name="agreeCheckbox" PositionX="5ch" PositionY="9ch" />
<Text PositionX="7ch" PositionY="9ch">I agree to the terms</Text>
<Button Name="submitButton" PositionX="5ch" PositionY="11ch"
RoundedCorners="true">Submit</Button>
</Container><Table Name="dataTable" PositionX="5ch" PositionY="5ch"
BorderStyle="Single" BorderColor="White"
RoundedCorners="true">
<TableRow>
<TableCell Style="Bold">Name</TableCell>
<TableCell Style="Bold">Age</TableCell>
<TableCell Style="Bold">City</TableCell>
</TableRow>
<TableRow>
<TableCell>Alice</TableCell>
<TableCell>25</TableCell>
<TableCell>New York</TableCell>
</TableRow>
<TableRow>
<TableCell>Bob</TableCell>
<TableCell>30</TableCell>
<TableCell>London</TableCell>
</TableRow>
</Table>var chart = termui.GetWidget<Chart>("salesChart");
if (chart is not null)
{
var series1 = new ChartDataSeries
{
Label = "Product A",
Color = ConsoleColor.Green,
Data = [10, 15, 13, 17, 22, 28, 35, 42]
};
chart.AddSeries(series1);
chart.XLabels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug"];
}All widgets support these common properties:
Name- Unique identifier for retrieving widgets viaGetWidget<T>()PositionX,PositionY- Position in characters (e.g., "10ch")Width,Height- Dimensions in characters or percentage (e.g., "50ch", "100%")PaddingLeft,PaddingRight,PaddingTop,PaddingBottom- Padding in charactersForegroundColor,BackgroundColor- Standard console colorsFocusForegroundColor,FocusBackgroundColor- Colors when focusedVisible- Show/hide widgetCanFocus- Enable/disable focus capability
BorderStyle-Single,Double, orNoneBorderColor- Border colorRoundedCorners- Enable rounded corners (single border only)Scrollable- Enable scrolling for overflow content
Placeholder- Placeholder textIsPassword- Hide input charactersMultiline- Enable multi-line inputValue- Current text value
Normal- Regular textBold- Bold text (Unicode mathematical alphanumeric symbols)Italic- Italic text (Unicode mathematical alphanumeric symbols)BoldItalic- Combined bold and italicUnderline- Underlined textStrikethrough- Text with strikethrough
button.Click += (sender, e) =>
{
Console.WriteLine("Button clicked!");
};input.TextChanged += (sender, e) =>
{
Console.WriteLine($"New value: {input.Value}");
};
input.EnterPressed += (sender, e) =>
{
Console.WriteLine("Enter key pressed!");
};checkbox.CheckedChanged += (sender, e) =>
{
Console.WriteLine($"Checked: {checkbox.Checked}");
};radioButton.SelectionChanged += (sender, e) =>
{
Console.WriteLine($"Selected: {radioButton.Selected}");
};slider.ValueChanged += (sender, newValue) =>
{
Console.WriteLine($"Value: {newValue}");
};termui.Shortcut += (sender, key) =>
{
if (key.Key == ConsoleKey.S) // Ctrl+S
{
// Handle save shortcut
}
};Create modal dialogs by toggling container visibility:
var mainPage = termui.GetWidget<Container>("mainPage");
var confirmModal = termui.GetWidget<Container>("confirmModal");
// Show modal
mainPage.Visible = false;
confirmModal.Visible = true;
termui.SetFocus(modalYesButton);<Container Width="30ch" Height="10ch" Scrollable="true">
<!-- Content larger than container will be scrollable -->
<RadioButton Name="option1" PositionX="2ch" PositionY="2ch" />
<RadioButton Name="option2" PositionX="2ch" PositionY="3ch" />
<!-- ... many more options ... -->
</Container>Use Page Up/Page Down or Ctrl+Page Up/Page Down to scroll.
// Update text dynamically
outputText.Content = "Updated message!";
// Update progress bar
progressBar.Value = 0.75; // 75%
// Update slider
volumeSlider.Value = 50;TermuiX automatically manages console state:
// Initialize - clears screen, hides cursor
var termui = TermuiX.TermuiX.Init();
// DeInit - restores cursor, clears screen, resets colors
TermuiX.TermuiX.DeInit();Ctrl+C automatically calls DeInit() to restore console state. You can control this behavior:
// Disable automatic exit on Ctrl+C
TermuiX.TermuiX.AllowCancelKeyExit = false;TermuiX supports multiple border styles:
- Single - Single-line borders (┌─┐│└┘)
- Double - Double-line borders (╔═╗║╚╝)
- RoundedCorners - Rounded corners with single borders (╭─╮│╰╯)
<Container BorderStyle="Single" RoundedCorners="true">
<!-- Content -->
</Container>- .NET 9.0 or .NET 10.0
- Terminal with Unicode support
- Terminal with ANSI color support
Register your own widget types and reusable components directly in the XML parser:
// Register a custom widget
termui.RegisterWidget("CustomButton", attrs =>
{
var text = attrs.GetValueOrDefault("Text", "Default");
return new CustomButton(text);
});
// Register a reusable component
var fileExplorer = new FileExplorer(termui);
termui.RegisterComponent("FileExplorer", attrs =>
{
var width = attrs.GetValueOrDefault("Width", "100%");
return fileExplorer.BuildXml();
});
fileExplorer.Initialize();
// Use in XML
// <CustomButton Text="Click Me" />
// <FileExplorer Width="80%" Height="90%" />Full support for emojis, East Asian characters (CJK), and all wide Unicode characters with proper display width calculation:
<Text>Hello 👋 World! 你好世界</Text>
<Button>Save 💾</Button>All interactive widgets now support disabled states with customizable colors:
<Button Name="submitBtn" Disabled="true"
DisabledForegroundColor="Gray"
DisabledBackgroundColor="DarkGray">
Submit
</Button>
<Input Disabled="true" DisabledForegroundColor="DarkGray" />
<Checkbox Disabled="true" />
<RadioButton Disabled="true" />
<Slider Disabled="true" />- Multi-targeting for .NET 9.0 and .NET 10.0
- Full Native AOT compatibility for minimal deployment size
- Enabled AOT analyzer for build-time warnings
Complete file explorer implementation demonstrating:
- Two-column layout with file properties
- Navigation with history (back/forward buttons)
- File operations (copy, move, delete, rename)
- Confirmation dialogs
- Filter and sort functionality
- Custom component registration
- Auto-scroll to keep focused widgets visible
- Better rendering of wide characters and emojis
- Improved layout calculations with scrollbar awareness
- Enhanced clipboard support in Input widget
- Better multiline navigation with proper rune handling
Create reusable UI components by adding XML strings dynamically to containers:
var container = termui.GetWidget<Container>("myContainer");
string cardComponent = """
<Container Width="30ch" Height="5ch" BorderStyle="Single" BorderColor="White">
<Text PositionX="1ch" PositionY="1ch">Card Title</Text>
</Container>
""";
container.Add(cardComponent); // Components are automatically clonedUse negative positions to place widgets off-screen or create animated transitions:
<Container PositionX="-50ch" PositionY="0ch" Width="40ch" Height="100%">
<!-- Sidebar that can slide in from the left -->
</Container>// Animate position changes
sidebar.PositionX = $"{currentPosition}ch";All widgets support deep and shallow cloning:
var originalButton = termui.GetWidget<Button>("myButton");
var clonedButton = originalButton.Clone(deep: true); // Deep clone with childrenCheck out the included samples:
- TermuiX.Demo - Comprehensive demo with forms, charts, modals, and more
- TermuiX.Demo2 - Table widget demonstration with multi-line text
- TermuiX.Demo3 - Animated sidebar with burger menu using negative positioning
- TermuiX.Demo4 - Component model demonstration with dynamic widget addition
- TermuiX.Demo5 - Auto-scroll messenger with programmatic scroll control
- TermuiX.FileManager - Full-featured file explorer with custom component registration
Run samples:
cd samples/TermuiX.Demo
dotnet run
cd samples/TermuiX.Demo2
dotnet run
cd samples/TermuiX.Demo3
dotnet run
cd samples/TermuiX.Demo4
dotnet run
cd samples/TermuiX.Demo5
dotnet run
cd samples/TermuiX.FileManager
dotnet runMIT License - see LICENSE file for details
Contributions are welcome! Please feel free to submit issues and pull requests.
