Created
August 7, 2026 10:41
-
-
Save artydev/43950a51fe2373e04b79396a83eacd53 to your computer and use it in GitHub Desktop.
Termina llm.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Termina: Reactive Terminal UI Framework for .NET | |
| ## Overview | |
| Termina is a reactive terminal UI (TUI) framework for .NET that enables developers to build beautiful, interactive terminal applications with declarative layouts and reactive state management. It provides an MVVM architecture with automatic UI updates, ASP.NET Core-style routing, and seamless integration with Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Hosting. | |
| **Version:** 0.15.1 (at documentation time) | |
| **License:** Apache 2.0 | |
| **Repository:** https://github.com/Aaronontheweb/Termina | |
| **Documentation:** https://aaronstannard.com/termina/ | |
| **NuGet Package:** https://www.nuget.org/packages/Termina | |
| ## Key Features | |
| ### Reactive MVVM Architecture | |
| - ViewModels use `ReactiveProperty<T>` for observable state management | |
| - Source-generated reactive properties with no boilerplate required | |
| - `ReactiveProperty<T>` is both a value holder and an `Observable<T>` for automatic UI updates | |
| - Full integration with R3 observables library (migrated from System.Reactive in v0.7.0) | |
| ### Declarative Layouts | |
| - Tree-based layout system for composing complex UIs | |
| - Fluent API with `.WithChild()` for clean composition | |
| - Support for Vertical, Horizontal, Grid, and Stack layouts | |
| - Size constraints: Fixed, Fill, Auto, and Percent | |
| - Nesting layouts for complex UI composition | |
| - Responsive design with terminal resize support | |
| ### Surgical Region-Based Rendering | |
| - Only changed regions re-render, not the entire screen | |
| - Enables smooth streaming updates for real-time content | |
| - Direct ANSI rendering with zero external rendering dependencies | |
| - Surgical updates for efficient terminal performance | |
| ### ASP.NET Core-Style Routing | |
| - Type-safe route parameters with templates like `/tasks/{id:int}` | |
| - Route parameter injection via `[FromRoute]` attribute | |
| - Navigation via `Navigate()` and `NavigateWithParams()` | |
| - Full type constraint support for route parameters | |
| ### Source Generators | |
| - AOT (Ahead-of-Time) compatible code generation | |
| - Native AOT publishing support with single-file executables | |
| - Zero reflection approach for performance | |
| ### Streaming Support | |
| - Native `StreamingTextNode` for token-by-token content rendering | |
| - Ideal for LLM output, real-time logs, and live data streams | |
| - Character-level subscription updates for smooth rendering | |
| ### Dependency Injection | |
| - Full integration with `Microsoft.Extensions.DependencyInjection` | |
| - Works seamlessly with `Microsoft.Extensions.Hosting` | |
| - Allows clean lifecycle management and service composition | |
| ### Testing Support | |
| - `VirtualInputSource` for automated testing with scripted input | |
| - Test mode detection for CI/CD integration | |
| - Deterministic input replay for regression testing | |
| ## Installation | |
| ### Requirements | |
| - .NET 10.0 SDK or later | |
| - Terminal emulator with ANSI support (Windows Terminal, iTerm2, etc.) | |
| ### Package Installation | |
| ```bash | |
| dotnet add package Termina | |
| dotnet add package Microsoft.Extensions.Hosting | |
| ``` | |
| ## Core Architecture | |
| ### The Three-Part Pattern | |
| Termina applications follow an MVVM pattern with three key components: | |
| #### 1. ViewModel | |
| - Inherits from `ReactiveViewModel` | |
| - Manages application state using `ReactiveProperty<T>` | |
| - Handles keyboard input via `Input.OfType<KeyPressed>()` | |
| - Provides navigation and shutdown actions | |
| - Must dispose all `ReactiveProperty<T>` instances in `Dispose()` method | |
| - Implements `OnActivated()` for initialization logic | |
| #### 2. Page | |
| - Inherits from `ReactivePage<TViewModel>` | |
| - Builds UI layout in `BuildLayout()` method | |
| - Implements reactive bindings via `.Select(...).AsLayout()` pattern | |
| - Manages focus for modals and interactive controls | |
| - Optionally implements `OnBound()` for additional initialization | |
| #### 3. Host Configuration | |
| - Uses `Microsoft.Extensions.Hosting` for lifecycle management | |
| - Registers routes via `AddTermina()` and `RegisterRoute<TPage, TViewModel>()` | |
| - Supports virtual input for testing | |
| ### Reactive Property Pattern | |
| `ReactiveProperty<T>` is the foundation of Termina's reactivity: | |
| - Holds a value accessible via `.Value` property | |
| - Emits observables when value changes | |
| - Built-in `DistinctUntilChanged` behavior (only emits on actual value change) | |
| - Used in layout bindings: `ViewModel.Count.Select(...).AsLayout()` | |
| - Must be disposed properly to prevent memory leaks | |
| ## Layout System | |
| ### How Layout Works | |
| The layout system processes node trees in two phases: | |
| **1. Measure Phase:** Starting from root, each node calculates how much space it needs given available space. Container nodes recursively measure children. | |
| **2. Render Phase:** Once measurements are complete, each node receives final bounds and renders to the terminal. | |
| ### Layout Nodes vs. Container Nodes | |
| **Layout Nodes (render content):** | |
| - `TextNode` - Styled text with word wrapping | |
| - `PanelNode` - Bordered container with title | |
| - `SpinnerNode` - Animated loading indicator | |
| - `StreamingTextNode` - Streaming content with scrolling | |
| - `GraphNode` - Live scrolling graph with gradient coloring | |
| - `ProgressBarNode` - Progress bar with gradient fill and label | |
| - `TextInputNode` - Single-line text input with cursor | |
| - `TextAreaNode` - Multi-line text input with word wrap | |
| - `CopyableTextNode` - Read-only text with clipboard support | |
| - `SelectionListNode` - Interactive list selection | |
| - `FilePickerNode` - File/folder picker | |
| **Container Nodes (arrange children):** | |
| - `VerticalLayout` - Stack children top-to-bottom | |
| - `HorizontalLayout` - Stack children left-to-right | |
| - `GridNode` - 2D grid with consistent column/row sizing | |
| - `StackLayout` - Overlay children (z-stack) | |
| - `ScrollableContainer` - Scrollable content area | |
| - `ModalNode` - Modal overlay with backdrop | |
| ### Size Constraints | |
| Four constraint types control sizing: | |
| | Constraint | Description | Example | | |
| |-----------|------------|---------| | |
| | `Fixed(n)` | Exactly n rows/columns | `.Height(3)` | | |
| | `Fill(weight)` | Take remaining space | `.Fill()` or `.Fill(2)` | | |
| | `Auto` | Size to content | `.HeightAuto()` | | |
| | `Percent(n)` | n% of available space | `.Height(SizeConstraint.Percent(50))` | | |
| ### Common Layout Patterns | |
| **Vertical stacking with header, content, footer:** | |
| ```csharp | |
| Layouts.Vertical() | |
| .WithChild(header.Height(3)) // Fixed height | |
| .WithChild(content.Fill()) // Take remaining space | |
| .WithChild(footer.Height(1)); | |
| ``` | |
| **Horizontal layout with weighted fills:** | |
| ```csharp | |
| Layouts.Horizontal() | |
| .WithChild(menu.Width(30)) | |
| .WithChild(main.Fill(2)) // 2x weight | |
| .WithChild(aside.Fill(1)); // 1x weight | |
| ``` | |
| **Nested layouts:** | |
| ```csharp | |
| Layouts.Vertical() | |
| .WithChild( | |
| Layouts.Horizontal() | |
| .WithChild(sidebar.Width(20)) | |
| .WithChild(content.Fill()) | |
| ) | |
| .WithChild(footer.Height(1)); | |
| ``` | |
| ## Component Library | |
| ### Display Components | |
| **TextNode** | |
| - Renders styled text with word wrapping | |
| - Supports foreground/background colors | |
| - Methods: `.Bold()`, `.Dim()`, `.Italic()`, `.Underline()`, `.Strike()`, `.WithForeground()`, `.NoWrap()` | |
| **PanelNode** | |
| - Bordered container with optional title | |
| - Border styles: `BorderStyle.Single`, `BorderStyle.Double`, `BorderStyle.Rounded` | |
| - Methods: `.WithTitle()`, `.WithBorder()`, `.WithBorderColor()`, `.WithTitleColor()`, `.WithContent()` | |
| **SpinnerNode** | |
| - Animated loading indicator | |
| - Various spinner styles available | |
| **StreamingTextNode** | |
| - For real-time, token-by-token content rendering | |
| - Created via `StreamingTextNode.Create()` | |
| - Add content: `.Append(chunk)` | |
| - Subscription pattern for ViewModel observables | |
| **GraphNode** | |
| - Live scrolling graph with gradient coloring | |
| **ProgressBarNode** | |
| - Progress bar with gradient fill and label | |
| ### Input Components | |
| **TextInputNode** | |
| - Single-line text input with cursor | |
| - Keyboard input handling | |
| **TextAreaNode** | |
| - Multi-line text input | |
| - Word wrap and vertical scrolling | |
| **SelectionListNode** | |
| - Interactive list selection | |
| - Keyboard navigation | |
| **FilePickerNode** | |
| - File/folder picker with directory navigation | |
| **CopyableTextNode** | |
| - Read-only text with keyboard selection and clipboard support | |
| ### Container Components | |
| **ScrollableContainer** | |
| - Vertical scrolling container for overflow content | |
| **StackLayout** | |
| - Overlapping children (z-stack) for layering | |
| **ModalNode** | |
| - Modal overlay with backdrop | |
| ### Reactive Components | |
| **ReactiveLayoutNode** | |
| - Updates content from observables | |
| **ConditionalNode** | |
| - Show/hide content based on condition | |
| **DynamicLayoutNode** | |
| - Re-evaluates factory on invalidation | |
| **KeyedDynamicLayoutNode** | |
| - Key-based content switching with caching | |
| **WizardNode** | |
| - Multi-step wizard with progress, navigation, and focus | |
| ### Utility Components | |
| **EmptyNode** | |
| - Placeholder that renders nothing | |
| **DeferredNode** | |
| - Delegates to node without owning it | |
| ## Input Handling | |
| ### Keyboard Input | |
| Input is accessed via `Input.OfType<IInputEvent, KeyPressed>()` in ViewModels: | |
| ```csharp | |
| public override void OnActivated() | |
| { | |
| Input.OfType<IInputEvent, KeyPressed>() | |
| .Subscribe(HandleKeyPress) | |
| .DisposeWith(Subscriptions); | |
| } | |
| private void HandleKeyPress(KeyPressed key) | |
| { | |
| switch (key.KeyInfo.Key) | |
| { | |
| case ConsoleKey.UpArrow: | |
| // Handle up arrow | |
| break; | |
| case ConsoleKey.Enter: | |
| // Handle enter | |
| break; | |
| case ConsoleKey.Escape: | |
| Shutdown(); | |
| break; | |
| default: | |
| // Handle printable characters | |
| if (key.KeyInfo.KeyChar >= 32 && key.KeyInfo.KeyChar < 127) | |
| { | |
| // Character input | |
| } | |
| break; | |
| } | |
| } | |
| ``` | |
| ### Printable Character Handling | |
| Access the character via `key.KeyInfo.KeyChar` property. | |
| ### Shutdown | |
| Call `Shutdown()` to exit the application. | |
| ## Routing | |
| ### Route Registration | |
| Routes are registered during host configuration: | |
| ```csharp | |
| builder.Services.AddTermina("/", termina => | |
| { | |
| termina.RegisterRoute<HomePage, HomeViewModel>("/"); | |
| termina.RegisterRoute<TasksPage, TasksViewModel>("/tasks"); | |
| termina.RegisterRoute<TaskDetailPage, TaskDetailViewModel>("/tasks/{id:int}"); | |
| termina.RegisterRoute<UserPage, UserViewModel>("/users/{name}"); | |
| }); | |
| ``` | |
| ### Route Parameters | |
| Routes support type-safe parameters with type constraints: | |
| ```csharp | |
| // Route template with parameter | |
| "/tasks/{id:int}" | |
| // ViewModel with route parameter injection | |
| public partial class TaskDetailViewModel : ReactiveViewModel | |
| { | |
| [FromRoute] private int _id; // Injected from route | |
| public override void OnActivated() | |
| { | |
| LoadTask(Id); // Id is already populated | |
| } | |
| } | |
| ``` | |
| ### Navigation | |
| Navigate between routes programmatically: | |
| ```csharp | |
| // Simple navigation | |
| Navigate("/tasks/42"); | |
| // With parameters | |
| NavigateWithParams("/tasks/{id}", new { id = 42 }); | |
| // Exit application | |
| Shutdown(); | |
| ``` | |
| ## Styling and Colors | |
| ### Color System | |
| Termina supports ANSI colors: | |
| - Basic colors: `Color.Black`, `Color.Red`, `Color.Green`, `Color.Yellow`, `Color.Blue`, `Color.Magenta`, `Color.Cyan`, `Color.White`, `Color.Gray` | |
| - Bright variants: `Color.BrightRed`, `Color.BrightGreen`, etc. | |
| ### Text Styling | |
| Apply styling via fluent API: | |
| - `.WithForeground(Color.Cyan)` - Text color | |
| - `.WithBackground(Color.Black)` - Background color | |
| - `.Bold()` - Bold text | |
| - `.Dim()` - Dimmed text | |
| - `.Italic()` - Italic text | |
| - `.Underline()` - Underlined text | |
| - `.Strike()` - Strikethrough text | |
| ### Word Wrapping | |
| Control text wrapping: | |
| - `.NoWrap()` - Disable word wrapping, truncate at edge | |
| ## Reactive Bindings | |
| ### Observable to Layout Pattern | |
| Convert observables to layout nodes: | |
| ```csharp | |
| ViewModel.Count | |
| .Select<int, ILayoutNode>(count => new TextNode($"Count: {count}") | |
| .WithForeground(Color.BrightCyan)) | |
| .AsLayout() | |
| ``` | |
| ### Multiple Observable Bindings | |
| Combine multiple observables: | |
| ```csharp | |
| Observable.CombineLatest( | |
| ViewModel.Title, | |
| ViewModel.Message | |
| ) | |
| .Select(x => new TextNode($"{x[0]}: {x[1]}").WithForeground(Color.Cyan)) | |
| .AsLayout() | |
| ``` | |
| ### Streaming Content Pattern | |
| For real-time content like LLM output: | |
| ```csharp | |
| // In Page | |
| private StreamingTextNode _output = null!; | |
| protected override void OnBound() | |
| { | |
| _output = StreamingTextNode.Create(); | |
| ViewModel.StreamOutput.Subscribe(chunk => _output.Append(chunk)); | |
| } | |
| // In ViewModel | |
| public Observable<string> StreamOutput => _streamOutput; | |
| private readonly Subject<string> _streamOutput = new(); | |
| private async Task StreamResponse() | |
| { | |
| await foreach (var chunk in GetStreamingData()) | |
| { | |
| _streamOutput.OnNext(chunk); // Character-level updates | |
| } | |
| } | |
| ``` | |
| ## Testing | |
| ### VirtualInputSource | |
| Use `VirtualInputSource` for automated testing with scripted input: | |
| ```csharp | |
| var scriptedInput = new VirtualInputSource(); | |
| builder.Services.AddTerminaVirtualInput(scriptedInput); | |
| // Queue input | |
| scriptedInput.EnqueueKey(ConsoleKey.UpArrow); | |
| scriptedInput.EnqueueString("Hello World"); | |
| scriptedInput.EnqueueKey(ConsoleKey.Enter); | |
| scriptedInput.Complete(); | |
| await host.RunAsync(); | |
| ``` | |
| ### Test Mode Detection | |
| Detect test mode with command-line flag: | |
| ```csharp | |
| var testMode = args.Contains("--test"); | |
| if (testMode) | |
| { | |
| var scriptedInput = new VirtualInputSource(); | |
| builder.Services.AddTerminaVirtualInput(scriptedInput); | |
| // Queue test inputs | |
| scriptedInput.EnqueueKey(ConsoleKey.UpArrow); | |
| // ... more inputs | |
| scriptedInput.Complete(); | |
| } | |
| ``` | |
| ## Complete Application Template | |
| ### Step 1: Define ViewModel | |
| ```csharp | |
| using R3; | |
| using Termina.Input; | |
| using Termina.Reactive; | |
| public class CounterViewModel : ReactiveViewModel | |
| { | |
| public ReactiveProperty<int> Count { get; } = new(0); | |
| public ReactiveProperty<string> Message { get; } = new("Press Up/Down"); | |
| public override void OnActivated() | |
| { | |
| Input.OfType<IInputEvent, KeyPressed>() | |
| .Subscribe(HandleKey) | |
| .DisposeWith(Subscriptions); | |
| } | |
| private void HandleKey(KeyPressed key) | |
| { | |
| switch (key.KeyInfo.Key) | |
| { | |
| case ConsoleKey.UpArrow: | |
| Count.Value++; | |
| Message.Value = $"Count: {Count.Value}"; | |
| break; | |
| case ConsoleKey.DownArrow: | |
| Count.Value--; | |
| Message.Value = $"Count: {Count.Value}"; | |
| break; | |
| case ConsoleKey.Escape: | |
| Shutdown(); | |
| break; | |
| } | |
| } | |
| public override void Dispose() | |
| { | |
| Count.Dispose(); | |
| Message.Dispose(); | |
| base.Dispose(); | |
| } | |
| } | |
| ``` | |
| ### Step 2: Define Page | |
| ```csharp | |
| using R3; | |
| using Termina.Extensions; | |
| using Termina.Layout; | |
| using Termina.Reactive; | |
| using Termina.Rendering; | |
| using Termina.Terminal; | |
| public class CounterPage : ReactivePage<CounterViewModel> | |
| { | |
| public override ILayoutNode BuildLayout() | |
| { | |
| return Layouts.Vertical() | |
| .WithChild( | |
| new PanelNode() | |
| .WithTitle("Counter Demo") | |
| .WithBorder(BorderStyle.Rounded) | |
| .WithBorderColor(Color.Cyan) | |
| .WithContent( | |
| ViewModel.Count | |
| .Select<int, ILayoutNode>(count => new TextNode($"Count: {count}") | |
| .WithForeground(Color.BrightCyan)) | |
| .AsLayout()) | |
| .Height(5)) | |
| .WithChild( | |
| ViewModel.Message | |
| .Select<string, ILayoutNode>(msg => new TextNode(msg)) | |
| .AsLayout() | |
| .Height(1)); | |
| } | |
| } | |
| ``` | |
| ### Step 3: Configure Host | |
| ```csharp | |
| using Microsoft.Extensions.Hosting; | |
| using Termina.Hosting; | |
| var builder = Host.CreateApplicationBuilder(args); | |
| // Optional: suppress host logging | |
| builder.Logging.SetMinimumLevel(LogLevel.Warning); | |
| // Register Termina | |
| builder.Services.AddTermina("/counter", termina => | |
| { | |
| termina.RegisterRoute<CounterPage, CounterViewModel>("/counter"); | |
| }); | |
| var host = builder.Build(); | |
| await host.RunAsync(); | |
| ``` | |
| ## Common Patterns | |
| ### List Display | |
| ```csharp | |
| ViewModel.Items | |
| .Select<List<string>, ILayoutNode>(items => new TextNode( | |
| string.Join("\n", items.Take(10)) // Show first 10 items | |
| )) | |
| .AsLayout() | |
| ``` | |
| ### Status Display with Reactive Updates | |
| ```csharp | |
| new PanelNode() | |
| .WithTitle("Status") | |
| .WithContent( | |
| Observable.CombineLatest( | |
| ViewModel.Status, | |
| ViewModel.Progress | |
| ) | |
| .Select(x => new TextNode($"{x[0]} - {x[1]}%")) | |
| .AsLayout() | |
| ) | |
| ``` | |
| ### Modal Dialog | |
| ```csharp | |
| new ModalNode() | |
| .WithTitle("Confirm") | |
| .WithContent(new TextNode("Are you sure?")) | |
| .WithBackdrop(true) | |
| ``` | |
| ### Scrollable Content | |
| ```csharp | |
| new ScrollableContainer() | |
| .WithChild(contentNode) | |
| .Height(20) | |
| ``` | |
| ### Loading Indicator | |
| ```csharp | |
| new SpinnerNode() | |
| .WithLabel("Loading...") | |
| ``` | |
| ## Performance Considerations | |
| 1. **Surgical Rendering:** Only changed regions re-render automatically | |
| 2. **ReactiveProperty Disposal:** Always dispose in ViewModel.Dispose() | |
| 3. **Subscription Management:** Use `.DisposeWith(Subscriptions)` in OnActivated() | |
| 4. **String Concatenation:** For frequently updated content, consider StringBuilder | |
| 5. **Observable Subscriptions:** Subscribe only to properties that change in BuildLayout() | |
| ## Breaking Changes | |
| ### Version 0.7.0 | |
| - Migrated from System.Reactive to R3 | |
| - R3 observable syntax may differ slightly from System.Reactive | |
| - See migration guide at https://aaronstannard.com/termina/guide/migration-0.7.html | |
| ### Version 0.11.0 | |
| - Changes to render loop threading | |
| - See upgrade guide at https://aaronstannard.com/termina/guide/upgrade-0.11.html | |
| ## Resources | |
| - **Official Documentation:** https://aaronstannard.com/termina/ | |
| - **GitHub Repository:** https://github.com/Aaronontheweb/Termina | |
| - **NuGet Package:** https://www.nuget.org/packages/Termina | |
| - **Author:** Aaron Stannard (https://aaronstannard.com/) | |
| - **License:** Apache 2.0 | |
| ## Akka.NET Integration Potential | |
| While Termina doesn't have a hard dependency on Akka.NET, the framework was created by Aaron Stannard, who also founded Akka.NET. This creates significant architectural synergy opportunities: | |
| ### Observable-to-Observable Bridge | |
| Termina's R3 observables can integrate with Akka.NET's streaming infrastructure: | |
| ```csharp | |
| // Termina observable to Akka.Streams | |
| public ReactiveProperty<string> MessageStream { get; } = new(""); | |
| // Bridge to Akka.Streams | |
| Source.FromObservable(ViewModel.MessageStream) | |
| .RunForEach(msg => { | |
| // Process Akka.NET actor messages into Termina UI | |
| }); | |
| ``` | |
| ### Actor-Based Backend with Termina UI | |
| Use Akka.NET actors for backend logic and Termina for the TUI frontend: | |
| ```csharp | |
| // Host configuration with both Akka.NET and Termina | |
| var builder = Host.CreateApplicationBuilder(args); | |
| // Register Akka.NET | |
| builder.Services.AddAkka("termina-app", configBuilder => { | |
| configBuilder.AddActorSystem(); | |
| }); | |
| // Register Termina | |
| builder.Services.AddTermina("/dashboard", termina => { | |
| termina.RegisterRoute<DashboardPage, DashboardViewModel>("/dashboard"); | |
| }); | |
| ``` | |
| ### Real-Time Dashboard Pattern | |
| Common use case: Use Akka.NET actors to process distributed data and Termina to display it: | |
| - **Akka.NET:** Handles concurrent data aggregation, event sourcing, clustering | |
| - **Termina:** Displays real-time metrics with surgical rendering and responsive UI | |
| - **Integration:** R3 observables bridge actor state to UI bindings | |
| ### Streaming Architecture | |
| Akka.Streams integrates with observables (since version 1.3.2): | |
| ```csharp | |
| // Expose Akka.Streams as observable | |
| IObservable<T> observable = akkaStream.RunWith(Sink.AsObservable<T>(), materializer); | |
| // Use in Termina UI | |
| observable | |
| .Select(item => new TextNode($"{item}")) | |
| .AsLayout() | |
| ``` | |
| ### Backpressure and Flow Control | |
| Akka.Streams offers backpressure awareness that R3 observables can leverage: | |
| - Prevents UI from being overwhelmed by rapid state changes | |
| - Buffers with configurable overflow strategies | |
| - Maintains responsiveness under high-volume actor message streams | |
| ### Testing Distributed Systems | |
| Combine Akka.TestKit with Termina's VirtualInputSource for end-to-end testing: | |
| ```csharp | |
| // Test Akka actors and their UI representation | |
| var probe = CreateTestProbe(); | |
| var actorRef = ActorOf<MyActor>(); | |
| // Verify UI behavior with scripted input | |
| var scriptedInput = new VirtualInputSource(); | |
| builder.Services.AddTerminaVirtualInput(scriptedInput); | |
| // Send messages to actor, verify UI updates | |
| actorRef.Tell(new MyMessage()); | |
| scriptedInput.EnqueueKey(ConsoleKey.Enter); | |
| ``` | |
| ### Deployment Scenarios | |
| **Single-Node Monitor:** | |
| - Termina TUI for local terminal-based dashboards | |
| - Akka.NET local actors for data collection | |
| **Distributed Monitoring:** | |
| - Akka.Cluster processes data on multiple nodes | |
| - Termina TUI on each node for local monitoring | |
| - Akka.Remote for inter-node communication | |
| **CLI Tools with Actor Backend:** | |
| - Termina provides interactive CLI interface | |
| - Akka.NET handles long-running background tasks | |
| - Observable streams bridge progress/status updates | |
| ## Related Concepts | |
| - **MVVM Pattern:** Model-View-ViewModel architectural pattern | |
| - **Reactive Programming:** Using observables for state management | |
| - **Terminal UI (TUI):** Text-based user interfaces in terminal/console | |
| - **ANSI Escape Codes:** Terminal formatting and color codes | |
| - **Dependency Injection:** Microsoft.Extensions.DependencyInjection | |
| - **Async/Await:** Asynchronous programming model for .NET | |
| - **R3 Observables:** Modern reactive extensions library for .NET | |
| - **Akka.NET:** Actor model framework for distributed/concurrent systems | |
| - **Akka.Streams:** Stream processing with backpressure on top of actors | |
| - **Microsoft.Extensions.Hosting:** Host lifecycle management | |
| ## Summary | |
| Termina is a production-ready, modern terminal UI framework that brings ASP.NET Core patterns to terminal applications. Its reactive architecture, surgical rendering, and declarative layout system make it ideal for building interactive CLI tools, dashboards, monitoring applications, and any text-based UI that needs real-time updates. The framework's AOT compatibility ensures you can deploy single-file executables for any platform. | |
| ## Complementary Technologies | |
| Aaron Stannard (Termina's creator) is also the founder of Akka.NET, which creates natural architectural synergy: | |
| - **Termina:** Excellent for responsive, declarative TUI layer | |
| - **Akka.NET:** Ideal for concurrent, distributed, fault-tolerant backend | |
| - **Observable Bridge:** R3 observables seamlessly connect actor state to UI updates | |
| For systems requiring both sophisticated backend logic (actor model, distributed processing, event sourcing) and interactive terminal interfaces (dashboards, | |
| monitoring, real-time displays), combining Akka.NET and Termina provides a cohesive, modern development experience across the full stack. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment