Termina 0.1.0-beta1
See the version list below for details.
dotnet add package Termina --version 0.1.0-beta1
NuGet\Install-Package Termina -Version 0.1.0-beta1
<PackageReference Include="Termina" Version="0.1.0-beta1" />
<PackageVersion Include="Termina" Version="0.1.0-beta1" />
<PackageReference Include="Termina" />
paket add Termina --version 0.1.0-beta1
#r "nuget: Termina, 0.1.0-beta1"
#:package Termina@0.1.0-beta1
#addin nuget:?package=Termina&version=0.1.0-beta1&prerelease
#tool nuget:?package=Termina&version=0.1.0-beta1&prerelease
Termina
Termina is a reactive terminal UI (TUI) framework for .NET built on top of Spectre.Console. It provides an MVVM architecture with source-generated reactive properties, ASP.NET Core-style routing, and seamless integration with Microsoft.Extensions.Hosting.
Features
- Reactive MVVM Architecture - ViewModels with
[Reactive]attribute for source-generated observable properties - ASP.NET Core-Style Routing - Route templates with parameters (
/tasks/{id:int}) and type constraints - Source Generators - AOT-compatible code generation for reactive properties and route parameter injection
- Dependency Injection - Full integration with
Microsoft.Extensions.DependencyInjection - Hosting Integration - Works with
Microsoft.Extensions.Hostingfor clean application lifecycle management - Spectre.Console Rendering - Beautiful terminal UIs with full Spectre.Console component support
Installation
dotnet add package Termina
Quick Start
1. Define a ViewModel
using Termina.Reactive;
public partial class CounterViewModel : ReactiveViewModel
{
[Reactive] private int _count;
[Reactive] private string _message = "Press Up/Down to change count";
public override void OnActivated()
{
Input.OfType<KeyPressed>()
.Subscribe(HandleKey)
.DisposeWith(Subscriptions);
}
private void HandleKey(KeyPressed key)
{
switch (key.KeyInfo.Key)
{
case ConsoleKey.UpArrow:
Count++;
Message = $"Count: {Count}";
break;
case ConsoleKey.DownArrow:
Count--;
Message = $"Count: {Count}";
break;
case ConsoleKey.Q:
Shutdown();
break;
}
}
}
The [Reactive] attribute generates:
- A
BehaviorSubject<T>backing field - A public property
Countwith get/set - An
IObservable<T>propertyCountChangedfor subscriptions
2. Define a Page
using Spectre.Console;
using Spectre.Console.Rendering;
using Termina.Pages;
public class CounterPage : ReactivePage<CounterViewModel>
{
protected override IRenderable Render(CounterViewModel vm)
{
return new Panel(
new Rows(
new FigletText(vm.Count.ToString()).Color(Color.Cyan1),
new Text(vm.Message)
))
.Header("Counter Demo")
.Border(BoxBorder.Rounded);
}
}
3. Configure and Run
using Microsoft.Extensions.Hosting;
using Termina.Hosting;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddTermina("/counter", termina =>
{
termina.RegisterRoute<CounterPage, CounterViewModel>("/counter");
termina.RegisterRoute<TodoListPage, TodoListViewModel>("/todos");
termina.RegisterRoute<TodoDetailPage, TodoDetailViewModel>("/todos/{id:int}");
});
await builder.Build().RunAsync();
Routing
Termina uses ASP.NET Core-style route templates with parameter support.
Route Templates
// Simple routes
termina.RegisterRoute<HomePage, HomeViewModel>("/");
termina.RegisterRoute<TasksPage, TasksViewModel>("/tasks");
// Routes with parameters
termina.RegisterRoute<TaskDetailPage, TaskDetailViewModel>("/tasks/{id:int}");
termina.RegisterRoute<UserPage, UserViewModel>("/users/{name}");
termina.RegisterRoute<DocumentPage, DocumentViewModel>("/docs/{id:guid}");
Supported Type Constraints
| Constraint | C# Type | Example |
|---|---|---|
:int |
int |
/tasks/{id:int} |
:guid |
Guid |
/docs/{id:guid} |
:bool |
bool |
/items/{active:bool} |
| (none) | string |
/users/{name} |
Route Parameter Injection
Use [FromRoute] to automatically inject route parameters into your ViewModel:
public partial class TaskDetailViewModel : ReactiveViewModel
{
[FromRoute] private int _id; // Injected from route before OnActivated
public override void OnActivated()
{
// Id property is already populated
LoadTask(Id);
}
}
Navigation
// Navigate by path
Navigate("/tasks/42");
// Navigate with route values (type-safe)
NavigateWithParams("/tasks/{id}", new { id = 42 });
// Go back
// (handled by framework when CanGoBack is true)
Reactive Properties
The [Reactive] attribute on private fields generates observable properties:
public partial class MyViewModel : ReactiveViewModel
{
[Reactive] private string _name = "default";
[Reactive] private int _count;
[Reactive] private IReadOnlyList<Item> _items = Array.Empty<Item>();
}
Generated code provides:
Name,Count,Items- public properties with get/setNameChanged,CountChanged,ItemsChanged-IObservable<T>for subscriptions
Pages automatically re-render when any reactive property changes.
Page Lifecycle
public partial class MyViewModel : ReactiveViewModel
{
public override void OnActivated()
{
// Called when navigating TO this page
// Set up subscriptions here
}
public override void OnDeactivating()
{
// Called when navigating AWAY from this page
// Clean up if needed (Subscriptions auto-dispose)
}
}
Navigation Behavior
Control how pages behave when navigated to:
// Reset state each time (default)
termina.RegisterRoute<MyPage, MyViewModel>("/page", NavigationBehavior.ResetOnNavigation);
// Preserve state across navigations
termina.RegisterRoute<MyPage, MyViewModel>("/page", NavigationBehavior.PreserveState);
Testing
Termina includes VirtualInputSource for automated testing:
var scriptedInput = new VirtualInputSource();
builder.Services.AddTerminaVirtualInput(scriptedInput);
// Queue up scripted input
scriptedInput.EnqueueKey(ConsoleKey.UpArrow);
scriptedInput.EnqueueKey(ConsoleKey.Enter);
scriptedInput.EnqueueKey(ConsoleKey.Q);
scriptedInput.Complete();
await host.RunAsync();
Requirements
- .NET 10.0 or later
- AOT-compatible (Native AOT publishing supported)
License
Apache 2.0 - See LICENSE for details.
Contributing
Contributions are welcome! Please feel free to submit issues and pull requests.
| Product | Versions 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. |
-
net10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.0)
- Spectre.Console (>= 0.54.0)
- System.Reactive (>= 6.1.0)
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 |
|---|---|---|
| 0.11.1 | 0 | 6/8/2026 |
| 0.11.0 | 51 | 6/7/2026 |
| 0.10.2 | 2,540 | 5/30/2026 |
| 0.10.1 | 1,341 | 5/24/2026 |
| 0.10.0 | 390 | 5/23/2026 |
| 0.9.0 | 2,397 | 5/18/2026 |
| 0.8.0 | 7,015 | 3/17/2026 |
| 0.7.2 | 1,956 | 3/1/2026 |
| 0.7.1 | 414 | 2/27/2026 |
| 0.7.0 | 113 | 2/26/2026 |
| 0.6.1 | 109 | 2/25/2026 |
| 0.6.0 | 309 | 2/24/2026 |
| 0.5.1 | 489 | 12/19/2025 |
| 0.5.0 | 321 | 12/18/2025 |
| 0.4.0 | 362 | 12/18/2025 |
| 0.3.0 | 298 | 12/17/2025 |
| 0.2.1 | 760 | 12/16/2025 |
| 0.2.0 | 307 | 12/16/2025 |
| 0.1.0 | 325 | 12/16/2025 |
| 0.1.0-beta1 | 141 | 12/12/2025 |
Initial release of Termina - a reactive terminal UI (TUI) framework for .NET.
**Features**:
- Reactive MVVM architecture with source-generated properties
- ASP.NET Core-style routing with parameterized routes and type constraints
- Two-tier event architecture (pages and navigation)
- Virtualizable input support for large datasets
- Built on Spectre.Console for rich terminal rendering
- Full AOT/trimming compatibility