# RazorConsole > Build C# terminal UIs with reusable Razor components, built-in mouse and keyboard events, and experimental NativeAOT support. [Spectre.Console](https://spectreconsole.net) is part of the rendering foundation. --- # Document: Quick Start # Chapter 1 · Hello World Build your first RazorConsole application and learn how Razor markup becomes an interactive terminal UI. ## Learning objectives By the end of this chapter, you will be able to: - create a console project that compiles Razor components; - render a Razor component as terminal character cells; - handle one small interaction; and - run the same component in the browser preview and a native terminal. ## How RazorConsole renders RazorConsole uses Razor's component model, but it does **not** create browser DOM elements. A component produces a virtual tree that RazorConsole lays out into rows and columns of terminal character cells. Those cells are then written as ANSI terminal output. Components still provide the useful Razor ideas—markup, C# state, parameters, and event callbacks—while the renderer targets a terminal instead of HTML. ## Prerequisites - The [.NET 8 SDK or newer](https://dotnet.microsoft.com/download) - A terminal with ANSI color support - An editor that supports C# and Razor files Check your SDK before continuing: ```shell dotnet --version ``` ## 1. Create the project ```shell dotnet new console -n HelloRazorConsole --framework net8.0 cd HelloRazorConsole dotnet add package RazorConsole.Core ``` The package command is for users consuming a published RazorConsole release from NuGet. The live preview above is built from the current repository checkout and can contain changes that are not in the latest NuGet package yet. To run this exact checkout instead, use the command in **Run the repository version** below. ## 2. Enable the Razor SDK Replace `HelloRazorConsole.csproj` with: ```xml Exe net8.0 enable enable ``` `Microsoft.NET.Sdk.Razor` invokes the Razor compiler for `.razor` files. A plain `Microsoft.NET.Sdk` console project does not generate the component classes used by `Program.cs`. ## 3. Add shared imports Create `_Imports.razor` in the project root: ```razor @using Microsoft.AspNetCore.Components @using Microsoft.AspNetCore.Components.Web @using RazorConsole.Components @using Spectre.Console ``` Imports in this file apply to every Razor component beneath it. ## 4. Create the component Create `HelloWorld.razor`: ```razor @code { private int _helloCount; private string InteractionMessage => _helloCount == 0 ? "Try the focused button below." : $"Hello again! Button pressed {_helloCount} {(_helloCount == 1 ? "time" : "times")}."; private void SayHello() => _helloCount++; } ``` `Rows` stacks terminal widgets vertically. `Markup` emits styled character cells, while `TextButton` participates in terminal focus and invokes its callback when you press Enter. The counter is only here to make the result testable; Chapter 2 will cover state and events in detail. ## 5. Start the host Replace `Program.cs` with: ```csharp using Microsoft.Extensions.Hosting; using RazorConsole.Core; var builder = Host.CreateApplicationBuilder(args); builder.UseRazorConsole(); using var host = builder.Build(); await host.RunAsync(); ``` Run the app: ```shell dotnet run ``` The button starts focused. Press Enter to activate it and Ctrl+C to stop the app. ## Complete source and repository run The complete runnable source for this chapter lives in: - `tutorial/Tutorial.Components/Chapters/HelloWorld.razor` - `tutorial/Tutorial.Runner/Program.cs` To run the exact component used by the browser preview from a RazorConsole checkout: ```shell dotnet run --project tutorial/Tutorial.Runner ``` This repository uses the preview SDK pinned in `global.json`. Install that SDK (including previews), or use the published-package steps above with a supported stable SDK. ## Exercise Add a second `TextButton` named **Reset greeting**. Its callback should set `_helloCount` back to zero. Then add `FocusedColor="@Color.Yellow"` so the two buttons are easy to distinguish while changing focus. ## Common setup errors | Symptom | Fix | | --- | --- | | `.razor` files are ignored or `HelloWorld` cannot be found | Use `Microsoft.NET.Sdk.Razor` in the project file, then rebuild. | | `RazorConsole` namespaces cannot be found | Run `dotnet restore` and confirm the `RazorConsole.Core` package or project reference exists. | | The UI draws with broken borders | Use a UTF-8 terminal and a font that contains box-drawing characters. | | The button does not respond | Give the terminal focus, then press Enter. Use Tab when the exercise adds another button. | | Checkout build requests another SDK | Install the preview SDK version in the repository's `global.json`; NuGet consumers do not need that checkout SDK. | [Next: Chapter 2 · State and Events →](/docs/tutorial/state-and-events) --- # Document: Built-in Components # Built-in Components RazorConsole ships with a library of Spectre.Console-powered components covering layout, input, display, and utility scenarios. You can combine them just like regular Razor components to build rich terminal experiences. ## Layout | Component | Highlights | | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | [Align](https://razorconsole.com/components/Align) | Centers or aligns child content horizontally/vertically with optional fixed width/height. | | [Columns](https://razorconsole.com/components/Columns) | Flows children left-to-right, optionally expanding to fill the console width. | | [Rows](https://razorconsole.com/components/Rows) | Stacks children vertically, great for wizard-style layouts. | | [Grid](https://razorconsole.com/components/Grid) | Builds a multi-column layout with configurable column count and width. | | [Padder](https://razorconsole.com/components/Padder) | Adds Spectre padding around nested content to create spacing. | | [Scrollable](https://razorconsole.com/components/Scrollable) | Renders a sliding window of items (`PageSize`) with built-in keyboard navigation (Arrow keys, PageUp/Down). | ```razor ``` ## Input | Component | Highlights | | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | [TextButton](https://razorconsole.com/components/TextButton) | Focusable button with customizable colors and click handlers via `EventCallback`. | | [TextInput](https://razorconsole.com/components/TextInput) | Collects user input with placeholder, change handler, and optional password masking. | | [Select](https://razorconsole.com/components/Select) | Keyboard-driven dropdown with highlighted selection state and callbacks. | ```razor ``` ## Display | Component | Highlights | | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | [Markup](https://razorconsole.com/components/Markup) | Renders Spectre markup with color, background, and text decoration support. | | [Panel](https://razorconsole.com/components/Panel) | Creates framed sections with optional title and border styling. | | [Border](https://razorconsole.com/components/Border) | Wraps child content in a configurable border and padding. | | [BarChart](https://razorconsole.com/components/BarChart) | Visualizes numeric data as horizontal bars with customizable styling, labels, and scaling. | | [BreakdownChart](https://razorconsole.com/components/BreakdownChart) | Displays proportional data segments (pie-style) with optional legends and percentage values. | | [StepChart](https://razorconsole.com/components/StepChart) | Plots discrete values over time using Unicode box-drawing characters and multiple series. | | [Figlet](https://razorconsole.com/components/Figlet) | Produces large ASCII headers using Figlet fonts. | | [SyntaxHighlighter](https://razorconsole.com/components/SyntaxHighlighter) | Displays colorized source code with optional line numbers. | | [SpectreCanvas](https://razorconsole.com/components/SpectreCanvas) | Provides a low-level pixel buffer for drawing custom graphics or pixel art. | | [Markdown](https://razorconsole.com/components/Markdown) | Renders Markdown text directly in the console. | | [Table](https://razorconsole.com/components/Table) | Converts semantic `` markup into Spectre tables. | ```razor ``` ## Utilities | Component | Highlights | | ------------------------------------------------------------------------- | -------------------------------------------------- | | [Spinner](https://razorconsole.com/components/Spinner) | Animated progress indicator with optional message. | | [Newline](https://razorconsole.com/components/Newline) | Emits an empty line to separate sections. | ```razor ``` ## Tips - All components can be composed within one another—wrap inputs inside `Panel`, place buttons in `Columns`, etc. - Spectre colors map to `Spectre.Console.Color`; use `Color.Red`, `Color.Blue`, or RGB constructors for precise styling. - Prefer `EventCallback` parameters for handlers so components remain async-friendly. - Combine with RazorConsole focus management features to deliver intuitive keyboard navigation. --- # Document: Widget Layout # Widget Layout WidgetLayout becomes RazorConsole's default rendering pipeline starting in 0.6.0. It gives RazorConsole ownership of terminal layout before output reaches Spectre.Console, which means components can expose reliable bounds, focus targets, and layout metadata instead of depending on layout decisions that happen inside Spectre renderables. ## Why it exists The legacy Spectre pipeline renders each `VNode` directly into Spectre.Console `IRenderable` objects. That works well for final terminal output, but it makes it difficult for RazorConsole to answer practical UI questions like "where is this element?", "which node is under focus?", or "what bounds should this component report?" because Spectre computes much of that geometry while rendering. The larger goal is to support richer coordinate-aware events. Events such as hover, click, drag, and pointer-style interactions need RazorConsole to map a terminal coordinate back to the component or delegate that owns that cell. WidgetLayout gives RazorConsole a committed box tree so those future event dispatch paths can be built on known element bounds instead of best-effort render tracing. WidgetLayout adds a RazorConsole-owned widget layer between the virtual DOM and Spectre. Layout is measured and arranged first, so RazorConsole can commit a box tree, update layout accessors, and then paint the final terminal frame. ## How rendering flows At a high level, a render goes through this path: ```text Razor component -> Blazor render batch -> RazorConsole VNode tree -> Widget tree -> LayoutEngine measure/arrange -> TerminalCanvas paint -> Spectre.Console IRenderable -> terminal output ``` Native widgets such as rows, columns, padding, alignment, panels, tables, text input, and scrollable regions compute their own sizes and child bounds. Components that are still difficult to port can be wrapped as `SpectreWidget` leaves, so charts, figlet text, syntax highlighting, and other Spectre-backed output can continue to work while the layout system grows. ## Falling back to legacy layout Starting in 0.6.0, WidgetLayout is used by default when no rendering pipeline is configured. To run an app with the legacy Spectre pipeline, set `RAZORCONSOLE_RENDERING_PIPELINE` before launching the app: ```sh $env:RAZORCONSOLE_RENDERING_PIPELINE = "LegacySpectre" dotnet run ``` You can also use the shorter values `legacy` or `spectre`. To explicitly request the default widget pipeline, use `WidgetLayout` or `widget`. In code, you can choose the pipeline through `ConsoleAppOptions`: ```csharp builder.Services.Configure(options => { options.RenderingPipeline = RazorConsoleRenderingPipeline.LegacySpectre; }); ``` Use the legacy pipeline when you are comparing output during migration or temporarily depending on Spectre layout behavior that has not been ported to native widgets yet. ## Share feedback WidgetLayout is still expanding its native widget coverage. If you see rendering differences between WidgetLayout and the legacy Spectre pipeline, or if you are building interactions that need coordinate-aware events such as hover or click, please open an issue with: - the component or example you are rendering - whether the behavior differs from `LegacySpectre` - the terminal size, operating system, and any relevant styles or layout options - a small repro snippet when possible That feedback helps prioritize which widgets and event scenarios should be ported next. --- # Document: Alternate Screen Buffer # Alternate Screen Buffer The alternate screen buffer lets a RazorConsole app take over the terminal viewport while it is running, then restore the previous terminal contents when the app exits. It is the same terminal mode used by full-screen console tools such as editors, pagers, and interactive dashboards. RazorConsole can use this mode for live applications that need a stable screen area. It is especially useful for coordinate-aware interactions because the app's rendered frame starts at the terminal viewport instead of being mixed into scrollback. ## Why it matters In the normal screen buffer, terminal output is part of scrollback. If the user scrolls, resizes, or launches the app after previous output, terminal coordinates can be harder to relate back to RazorConsole's layout tree. That becomes important for future mouse and pointer interactions, where RazorConsole needs to answer which component owns a specific row and column. The alternate screen buffer gives the app a clean viewport: ```text enter alternate screen -> render live RazorConsole frame -> process keyboard and terminal input -> update the latest frame in place exit alternate screen -> restore previous terminal contents ``` This does not add mouse wheel events by itself. Some terminals translate wheel scrolling in alternate screen mode into up and down key input, so components such as `Select` may move when the wheel is scrolled. That behavior comes from the terminal and keyboard event path, not from RazorConsole parsing mouse wheel events. ## Enabling it Configure the live display options when building the app: ```csharp builder.UseRazorConsole(configure: config => { config.Services.Configure(options => { options.ConsoleLiveDisplayOptions.UseAlternateScreenBuffer = true; }); }); ``` When terminal mouse events are enabled, RazorConsole also uses the alternate screen buffer automatically so future coordinate mapping can be based on the rendered viewport: ```csharp builder.UseRazorConsole(configure: config => { config.Services.Configure(options => { options.ConsoleLiveDisplayOptions.EnableMouseEvents = true; }); }); ``` ## When to use it Use the alternate screen buffer for interactive, live-updating apps where the terminal viewport acts like the application surface. This includes dashboards, forms, selectors, file explorers, and apps that will eventually depend on coordinate-aware mouse or pointer events. For short command output, reports, logs, or tools where preserving visible scrollback is more important than owning the viewport, keep the normal screen buffer. ## Exit behavior RazorConsole restores the normal screen buffer when the live display is disposed. If the process is force-killed or the terminal closes abruptly, the terminal may not receive the restore sequence. In that case, opening a new terminal tab or resetting the terminal usually restores the expected view. --- # Document: Hot Reload ### Hot Reload Support Build, tweak, and iterate without leaving your running console app. RazorConsole supports hot reload via its metadata update handler so UI changes are reflected instantly—no restart required. #### How it works 1. Run your application with `dotnet watch`. 2. Update a Razor component. 3. Save the file. 4. Watch the running console UI refresh automatically. ```shell dotnet watch run ``` > **Tip:** Hot reload shines for component tweaks. Certain structural changes may still need a full restart. --- # Document: Routing # Routing This document explains how routing works in modern `RazorConsole` and how to use it. --- ## 1. Routing basics Routing in Blazor maps URLs to _Razor components_. Key pieces: - `@page` directive – declares a route for a component. - `` / `Routes.razor` – central router component that matches URLs to pages. - `NavigationManager` – service for programmatic navigation. --- ## 2. The `@page` directive Any Razor component (`.razor`) becomes routable when it has at least one `@page` directive: ```razor @page "/" @page "/home"

Home

``` - Each `@page` line defines a route template. - A component can have multiple routes (e.g., an old and a new URL). - Routes are relative to the app’s base URL `/`. ### Example from a simple page: ```razor @page "/welcome"

Welcome!

Welcome to Blazor!

``` ## 3. Route templates & parameters ### 3.1. Basic parameters You can define route parameters in the template: ```razor @page "/todos/{id:int}"

Todo @Id

@code { [Parameter] public int Id { get; set; } } ``` - `{id:int}` – parameter name is `id`, with an `int` constraint. - To get parameter from route use `[Parameter]` attribute. ### 3.2. Optional parameters Optional parameters use `?` syntax: ```razor @page "/products" @page "/products/{category?}"

Products @Category

@code { [Parameter] public string? Category { get; set; } } ``` `/products` and `/products/books` both resolve to the same component. ### 3.3. Multiple parameters ```razor @page "/orders/{year:int}/{month:int}" @code { [Parameter] public int Year { get; set; } [Parameter] public int Month { get; set; } } ``` ### 3.4. Catch-all parameters Catch-all parameters capture the rest of the URL segment: ```razor @page "/files/{*path}" @code { [Parameter] public string? Path { get; set; } } ``` Example URLs: - `/files/readme.txt` - `/files/images/logo.png` ## 4. Routing in `RazorConsole` In the new Blazor Web App template, routing is configured by the Routes component. Its as simple as in [web blazor](https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/routing?view=aspnetcore-10.0) A typical `Routes.razor` ```razor @using Microsoft.AspNetCore.Components.Routing

Sorry, there's nothing at this address.

``` Key points: - `AppAssembly` - router scans this assembly to find components with `@page`. - `` - renders the matched component. - `DefaultLayout` - layout used when the component doesn’t specify its own. - ``- content shown when no route matches. ## 5. Layouts and route rendering Layouts are normal components that wrap pages. Common pattern: ```razor @layout MainLayout @page "/counter"

Counter

... ``` If no `@layout` is specified: - The `DefaultLayout` from `RouteView` in `Routes.razor` is used. ## 6. Programmatic navigation with `NavigationManager` Blazor exposes [`NavigationManager`](https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/routing?view=aspnetcore-10.0) for navigating in code-behind or services. Inject it: ```razor @inject NavigationManager Navigation ``` Basic usage: ```cs Navigation.NavigateTo("/counter"); ``` ## 7. Unsupported features Feel free to contribute - `HeadOutlet` - there is no `head` in cli app. - [Hash routing](https://developer.mozilla.org/en-US/docs/Glossary/Hash_routing) - `NavLink` ## 8. Examples [RazorConsole.Gallery](https://github.com/RazorConsole/RazorConsole/blob/main/gallery/RazorConsole.Gallery) --- # Document: Native Ahead-of-Time Compilation # Native AOT This document explains how to use **Native Ahead-of-Time (AOT)** compilation with **RazorConsole** to distribute native console applications without an installed .NET runtime and avoid JIT warm-up at startup. > [!WARNING] > Native AOT support in RazorConsole is currently **experimental**. > While core features like routing and rendering are tested and working, you may encounter edge cases with third-party > libraries or complex reflection scenarios. Please report any issues on [GitHub](https://github.com/RazorConsole/RazorConsole/issues/new?template=bug-report.yml). ## Install the Native AOT Gallery RazorConsole publishes the Component Gallery as native executables for Windows, Linux, and macOS on x64 and Arm64. These builds do not require the .NET SDK or runtime. On macOS or Linux: ```bash curl -fsSL https://raw.githubusercontent.com/RazorConsole/RazorConsole/main/scripts/install-razor-console-app.sh | sh -s -- --app Gallery ``` On Windows PowerShell: ```shell & ([scriptblock]::Create((irm https://raw.githubusercontent.com/RazorConsole/RazorConsole/main/scripts/install-razor-console-app.ps1))) -App Gallery ``` Both installers resolve the latest GitHub Release, select the correct archive, and verify it against the published SHA-256 checksum before installing it. Manual archives and `checksums-sha256.txt` are available from the [latest release](https://github.com/RazorConsole/RazorConsole/releases/latest). To test the latest successful `main` build, select the nightly channel: ```bash curl -fsSL https://raw.githubusercontent.com/RazorConsole/RazorConsole/main/scripts/install-razor-console-app.sh | sh -s -- --app Gallery --channel nightly ``` ```shell & ([scriptblock]::Create((irm https://raw.githubusercontent.com/RazorConsole/RazorConsole/main/scripts/install-razor-console-app.ps1))) -App Gallery -Channel Nightly ``` Every nightly uses a unique `nightly--` prerelease. CI creates it as a draft, uploads all six platform archives and `checksums-sha256.txt`, then publishes it. Formal releases use the same draft-first sequence. This workflow is compatible with GitHub immutable releases and prevents installers from selecting an incomplete build. See the [Component Gallery guide](/docs/component-gallery/) for installation directories, supported archives, and macOS Gatekeeper guidance. --- ## 1. What is Native AOT? Native AOT compiles your .NET application directly into _native machine code_ like other compiled languages does, rather than Intermediate Language (IL) that requires a JIT compiler at runtime. **Benefits for Console Apps:** - **Startup:** Native compilation removes JIT warm-up; actual startup time depends on the application. - **Standalone Distribution:** No need to install the .NET runtime on the target machine. - **Native Executable:** Include any runtime assets your app needs alongside the binary (for example, the Gallery's `Fonts/` directory). --- ## 2. Prerequisites To build Native AOT applications, you need platform-specific build tools installed on your development machine or CI environment. Look at this article in [msdocs](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/?tabs=windows%2Cnet8). --- ## 3. How to Publish To publish your application as a native executable, use the standard `dotnet publish` command with the `-p:PublishAot=true` property. You **must** specify a Runtime Identifier (RID), as native code is platform-specific. ```bash # Publish for Linux dotnet publish -c Release -r linux-x64 -p:PublishAot=true # Publish for Windows dotnet publish -c Release -r win-x64 -p:PublishAot=true # Publish for macOS (Apple Silicon) dotnet publish -c Release -r osx-arm64 -p:PublishAot=true ``` For a standard SDK project, the resulting binary is located in `bin/Release/{target-framework}/{rid}/publish/`. Projects using the artifacts output layout, including this repository, use their configured artifacts directory instead. To build the Gallery itself for the current macOS Apple Silicon host: ```bash dotnet publish gallery/RazorConsole.Gallery/RazorConsole.Gallery.csproj \ --configuration Release \ --framework net10.0 \ --runtime osx-arm64 \ -p:PublishAot=true \ -p:StripSymbols=true ``` Native AOT supports cross-architecture compilation in some configurations, but not cross-OS compilation. Release builds therefore run on matching Windows, Linux, and macOS GitHub-hosted runners. The distributable archive must include both the executable and the Gallery `Fonts/` directory. --- ## 4. Known Warnings & Limitations ### 4.1. The `IL2104` Warning During the build, you might see: > `warning IL2104: Assembly 'Microsoft.AspNetCore.Components' produced trim warnings` **Why this happens:** Blazor was designed for browser scenarios where the full .NET runtime is available. Some internal Blazor APIs use reflection patterns that the AOT analyzer cannot verify. **Is it safe?** Yes, for RazorConsole use cases. We've tested core features (routing, rendering, DI) and they work correctly. The warnings are about unused code paths in Blazor's browser-specific features. **Suppressing the warning:** ```xml $(NoWarn);IL2104 ``` **When to investigate:** If you're using advanced Blazor features beyond basic component rendering, test thoroughly with AOT. ### 4.2. Routing and Pages By default, the .NET AOT compiler trims unused code aggressively. Because the Router finds pages via reflection, the trimmer might accidentally remove your page components if they aren't directly referenced. To ensure routing works correctly, you must prevent your application assembly from being trimmed. Add this to your project file (`.csproj`): ```xml ``` ### 4.3. Reflection & Parameters Native AOT aggressively trims unused code. Anonymous types rely on reflection to read properties at runtime, and the trimmer may remove property metadata if it cannot statically prove the properties are used. **Avoid Anonymous Types for Parameters:** ```csharp // Avoid this in AOT // The trimmer may remove property metadata, causing runtime failures var parameters = new { Title = "Hello", Count = 5 }; ``` **Use Dictionary Instead:** Explicitly using `Dictionary` ensures the AOT compiler preserves the data. ```csharp // Preferred way for AOT var parameters = new Dictionary { { "Title", "Hello" }, { "Count", 5 } }; await renderer.RenderAsync(parameters); ``` ### 4.4. What Works & What Doesn't **✅ AOT-Compatible:** - Razor component rendering - Routing (`@page` directives) - Dependency injection - `System.Text.Json` (with source generators) - LINQ (query syntax) **⚠️ Requires Care:** - Third-party libraries (check for `IsAotCompatible`) - Custom reflection code - Dynamic assembly loading **❌ Not Supported:** - `System.Reflection.Emit` - C# dynamic keyword --- ## 5. Troubleshooting ### Build fails with `error MSB3073` or `link.exe` not found (Windows) This usually means the C++ Build Tools are missing. 1. Open **Visual Studio Installer**. 2. Modify your installation. 3. Check **Desktop development with C++**. ### App crashes immediately (Segmentation Fault / MissingMethodException) If your app uses third-party libraries that rely heavily on reflection (e.g., JSON serializers other than `System.Text.Json` source generator), they might be incompatible with AOT. - Try enabling the AOT analysis warnings in your project to see potential issues: ```xml true ``` --- ## 6. Examples You can see a working AOT setup in the [RazorConsole.Gallery](https://github.com/RazorConsole/RazorConsole/blob/main/gallery/RazorConsole.Gallery) project. --- # Document: Custom Translators ### Custom Translators RazorConsole converts Razor components into Spectre.Console renderables through a Virtual DOM (VDOM) translation pipeline. You can plug into that pipeline with custom translators to render bespoke elements. #### Creating a translator Implement the `IVdomElementTranslator` interface and translate nodes that match your criteria: ```csharp using RazorConsole.Core.Rendering.Vdom; using RazorConsole.Core.Vdom; using Spectre.Console; using Spectre.Console.Rendering; public sealed class OverflowElementTranslator : IVdomElementTranslator { // Lower priority values are processed first (1-1000+) public int Priority => 85; public bool TryTranslate( VNode node, TranslationContext context, out IRenderable? renderable) { renderable = null; // Check for a div with an overflow attribute if (node.Kind != VNodeKind.Element || !string.Equals(node.TagName, "div", StringComparison.OrdinalIgnoreCase)) { return false; } if (!node.Attributes.TryGetValue("data-overflow", out var overflowType)) { return false; } if (!VdomSpectreTranslator.TryConvertChildrenToRenderables( node.Children, context, out var children)) { return false; } var content = VdomSpectreTranslator.ComposeChildContent(children); renderable = overflowType?.ToLowerInvariant() switch { "ellipsis" => new Padder(content).Overflow(Overflow.Ellipsis), "crop" => new Padder(content).Overflow(Overflow.Crop), "fold" => new Padder(content).Overflow(Overflow.Fold), _ => content }; return true; } } ``` #### Registering the translator ```csharp using Microsoft.Extensions.Hosting; using RazorConsole.Core; using RazorConsole.Core.Vdom; IHostBuilder hostBuilder = Host.CreateDefaultBuilder(args) .UseRazorConsole(configure: config => { config.ConfigureServices(services => { services.AddVdomTranslator(); }); } ); IHost host = hostBuilder.Build(); await host.RunAsync(); ``` #### Using it in components ```razor
This text will be truncated with ellipsis if it's too long
``` For a deeper dive, read the [custom translators guide](https://github.com/RazorConsole/RazorConsole/blob/main/design-doc/custom-translators.md). --- # Document: Absolute Positioning & Z-Index # Absolute Positioning & Z-Index This document explains how absolute positioning and layering work in `RazorConsole` and how to create complex layouts using the `absolute` coordinate system. --- ## 1. Positioning Basics By default, elements in `RazorConsole` follow the **Normal Flow** (stacked vertically or horizontally depending on the container). When you apply `position="absolute"`, the element is: - **Removed from the normal flow**: It no longer takes up space in the layout, and other elements behave as if it isn't there. - **Placed on an Overlay Layer**: It is rendered on top of the background content. - **Positioned relative to the Document Root (Canvas)** or its nearest positioned ancestor. --- ## 2. Coordinate System You can control the position of an absolute element using four attributes: `top`, `bottom`, `left`, and `right`. | Attribute | Description | | ------------ | -------------------------------------------------------- | | **`top`** | Distance from the top edge of the document/ancestor. | | **`left`** | Distance from the left edge of the document/ancestor. | | **`bottom`** | Distance from the bottom edge of the **total document**. | | **`right`** | Distance from the right edge of the **total document**. | ### 2.1. Stretching If you provide both `left` and `right` , the element will **stretch** to fill the specified range. `top` and `bottom` vertical stretching doesn't work, because of the current render system, that does not allow to set height directly. ```razor
``` > [!NOTE] Horizontal stretch will work correctly only with elements that allows to be expanded (like ``). --- ## 3. Hierarchical Positioning (Cumulative Offsets) `RazorConsole` supports nested absolute positioning. If an `absolute` element is placed inside another `absolute` element, the child's `top` and `left` values are added to the parent's coordinates. This is known as **Cumulative Offsets**. The formula for the final global position is: ### Example of nesting: ```razor
``` > [!NOTE] `bottom` and `right` always calculate their position relative to the **edges of the entire document (Canvas)**, regardless of nesting. --- ## 4. Z-Index and Layering The `z-index` attribute determines the stacking order of elements that overlap. - **Default value:** `0`. - **Higher values:** Elements move "closer" to the user (rendered on top). - **Lower/Negative values:** Elements move "further back". ```razor
Top Layer
Bottom Layer
``` In this example, the **Red Panel** will be rendered over the **Blue Panel** because it has a higher `z-index`, even though it starts earlier in the code. --- ## 5. Canvas Expansion In `RazorConsole`, the "Document" (Canvas) is dynamic. 1. It starts with the size of the background content. 2. If an absolute element is placed outside these bounds (e.g., `top="50"` when the background only has 10 lines), the **Canvas automatically expands** with empty lines to accommodate the element. --- ## 6. Implementation Details Absolute positioning is handled by the [`AbsolutePositionMiddleware`](https://github.com/LittleLittleCloud/RazorConsole/blob/main/src/RazorConsole.Core/Rendering/Translation/Translators/AbsolutePositionMiddleware.cs). It intercepts nodes with the `position="absolute"` attribute and moves them to a special `CollectedOverlays` list in the `TranslationContext`. The final composition is performed by [`OverlayRenderable`](https://github.com/LittleLittleCloud/RazorConsole/blob/main/src/RazorConsole.Core/Renderables/OverlayRenderable.cs), which: 1. Renders the background. 2. Splits the output into a line-based `canvas`. 3. Handles `z-index` sorting before merging 4. Merges overlay segments into the canvas. --- ## 7. Unsupported Features - **`position="relative"` or `position="fixed"`**: Currently, we have only `absolute` position or non-positioned elements in main flow. - **Percentage units**: Only integer values (character units) are supported (e.g., `top="10%"` is not supported). --- # Document: Keyboard Events ### Keyboard Events Interactive experiences rely on keyboard input. RazorConsole exposes the familiar Blazor event model so you can respond to keystrokes just like you would on the web. #### Custom key handling ```razor
@code { private string message = "Press any key..."; private void HandleKeyPress(KeyboardEventArgs e) { message = $"You pressed: {e.Key}"; StateHasChanged(); } } ``` #### Supported events - `@onkeydown` — fires when a key is pressed. - `@onkeyup` — fires when a key is released. - `@onkeypress` — fires when a key is pressed and released. --- # Document: Focus Management # Focus Management Console UIs need strong focus cues for keyboard navigation. RazorConsole provides a `FocusManager` that automatically tracks focusable elements and coordinates focus changes, making keyboard navigation predictable and accessible. ## Overview The `FocusManager` is a singleton service that: - Tracks all focusable elements in the virtual DOM - Manages focus order and navigation - Dispatches focus events (`onfocus`, `onfocusin`, `onfocusout`) - Automatically refocuses when the DOM structure changes - Provides programmatic control over focus Focus management is handled automatically when you use RazorConsole's built-in components, but you can also control focus programmatically for advanced scenarios. ## Making Elements Focusable Elements become focusable in two ways: ### 1. Using `data-focusable` Attribute Set `data-focusable="true"` on any element to make it focusable: ```razor
``` ### 2. Elements with Event Handlers Elements with event handlers (like `@onclick`, `@onkeydown`) are automatically focusable: ```razor
``` ### 3. Built-in Components Built-in components like `TextInput` and `TextButton` are automatically focusable: ```razor ``` ## Setting Focus Order Use the `FocusOrder` parameter on built-in components to control tab order: ```razor ``` For custom elements, use the `data-focus-order` attribute: ```razor
``` ## Keyboard Navigation Users can navigate between focusable elements using: - **Tab** - Move focus to the next element - **Shift + Tab** - Move focus to the previous element Navigation wraps around: when reaching the last element, Tab moves to the first, and vice versa. ## Programmatic Focus Control You can programmatically control focus by injecting `FocusManager` into your components. ### Injecting FocusManager ```razor @using RazorConsole.Core.Focus @inject FocusManager FocusManager @code { // Use FocusManager methods here } ``` ### Available Methods #### FocusAsync(string key) Focus a specific element by its key: ```razor @using RazorConsole.Core.Focus @inject FocusManager FocusManager @code { private string username = string.Empty; private string password = string.Empty; private async Task FocusUsername() { await FocusManager.FocusAsync("username-input"); } } ``` #### FocusNextAsync() and FocusPreviousAsync() Move focus programmatically: ```razor @using RazorConsole.Core.Focus @inject FocusManager FocusManager @code { private string field1 = string.Empty; private string field2 = string.Empty; private string field3 = string.Empty; private async Task MoveToNext() { await FocusManager.FocusNextAsync(); } private async Task MoveToPrevious() { await FocusManager.FocusPreviousAsync(); } } ``` #### Checking Focus State Check if an element is currently focused: ```razor @using RazorConsole.Core.Focus @inject FocusManager FocusManager
@code { // Check current focus private string? CurrentFocus => FocusManager.CurrentFocusKey; // Check if manager has any focusable elements private bool HasFocusables => FocusManager.HasFocusables; } ``` ### Subscribing to Focus Changes Subscribe to the `FocusChanged` event to react to focus changes: ```razor @using RazorConsole.Core.Focus @inject FocusManager FocusManager @implements IDisposable @code { private string? currentFocus; protected override void OnInitialized() { FocusManager.FocusChanged += OnFocusChanged; currentFocus = FocusManager.CurrentFocusKey; } private void OnFocusChanged(object? sender, FocusChangedEventArgs e) { currentFocus = e.Key; StateHasChanged(); } public void Dispose() { FocusManager.FocusChanged -= OnFocusChanged; } } ``` ## Responding to Focus Events Elements can respond to focus changes using event handlers: ### onfocus Fired when an element receives focus: ```razor
@code { private string message = "Not focused"; private void OnFocus(FocusEventArgs e) { message = "Focused!"; StateHasChanged(); } } ``` ### onfocusin Similar to `onfocus`, but bubbles up through parent elements: ```razor
@code { private void OnFocusIn(FocusEventArgs e) { // Fired when child receives focus } } ``` ### onfocusout Fired when an element loses focus: ```razor
@code { private string message = "Not focused"; private void OnFocus(FocusEventArgs e) { message = "Focused!"; StateHasChanged(); } private void OnFocusOut(FocusEventArgs e) { message = "Focus lost"; StateHasChanged(); } } ``` ## Focus Sessions Focus management operates within a session that tracks the current render context. Sessions are automatically managed by RazorConsole when your application starts. The `FocusManager`: - Automatically selects the first focusable element when a session begins - Maintains focus state across re-renders - Automatically refocuses when the currently focused element is removed from the DOM - Clears focus when all focusable elements are removed ## Navigation Between Pages When navigating between pages or components, focus management automatically adapts: 1. **New Page Loads**: The first focusable element is automatically focused 2. **Component Changes**: Focus is maintained if the same element exists in the new view 3. **Element Removal**: If the focused element is removed, focus moves to the next available element Example with navigation: ```razor @using RazorConsole.Core.Focus @inject FocusManager FocusManager @inject NavigationManager Navigation @code { private async Task NavigateToLogin() { Navigation.NavigateTo("/login"); // Focus will automatically move to the first focusable element // on the login page when it renders } } ``` ## Complete Example Here's a complete example demonstrating focus management: ```razor @using RazorConsole.Core.Focus @using RazorConsole.Components @inject FocusManager FocusManager @implements IDisposable @code { private string value1 = string.Empty; private string value2 = string.Empty; private string value3 = string.Empty; private string? currentFocus; protected override void OnInitialized() { FocusManager.FocusChanged += OnFocusChanged; currentFocus = FocusManager.CurrentFocusKey; } private void OnFocusChanged(object? sender, FocusChangedEventArgs e) { currentFocus = e.Key; StateHasChanged(); } private async Task FocusFirst() { await FocusManager.FocusAsync("input1"); } private async Task FocusSecond() { await FocusManager.FocusAsync("input2"); } private async Task FocusThird() { await FocusManager.FocusAsync("input3"); } private async Task FocusNext() { await FocusManager.FocusNextAsync(); } private async Task FocusPrevious() { await FocusManager.FocusPreviousAsync(); } public void Dispose() { FocusManager.FocusChanged -= OnFocusChanged; } } ``` ## Best Practices 1. **Always use `@key` attributes** on focusable elements to ensure stable focus keys 2. **Set `FocusOrder`** on form elements to create a logical tab sequence 3. **Handle focus events** to provide visual feedback when elements receive/lose focus 4. **Use `FocusManager` programmatically** for complex navigation scenarios or when focus needs to change based on application logic 5. **Clean up event subscriptions** by implementing `IDisposable` and unsubscribing from `FocusChanged` events 6. **Test keyboard navigation** to ensure your UI is accessible and intuitive --- # Document: VDom Tree Debugging ### VDom Tree Visualization Debug and inspect the Virtual DOM structure of your RazorConsole components with the built-in VDom tree printer. #### Overview RazorConsole uses a Virtual DOM (VDOM) to efficiently render Razor components to the console. When debugging complex component hierarchies or investigating rendering issues, you can enable the VDom tree printer to visualize the internal structure. #### How to Enable Set the `RC_PRINT_VDOM_TREE` environment variable to `true` before running your application: **On Windows (PowerShell):** ```shell $env:RC_PRINT_VDOM_TREE="true" dotnet run ``` **On Linux/macOS:** ```shell RC_PRINT_VDOM_TREE=true dotnet run ``` **Or inline:** ```shell RC_PRINT_VDOM_TREE=true dotnet watch run ``` #### What You'll See When enabled, the tree printer displays each frame's VDOM structure in a panel, showing: - **Node hierarchy** - Visual tree structure with indentation - **Node types** - Element, Text, Component, or Region - **Element details** - Tag names, keys, IDs, and text content - **Attributes** - All data attributes attached to nodes - **Events** - Registered event handlers (onclick, onfocus, etc.) **Example output:** ``` Frame 1 ┌─────────────────────────────────────────┐ │ • Element div key=root-key text='...' │ │ v-id=a3f2 attrs[data-rows=true] │ │ ├── Element span key=first │ │ │ text='First Item' v-id=b7c4 │ │ │ attrs[data-focusable=true] │ │ │ events[onfocus, onfocusin] │ │ └── Element span key=second │ │ text='Second Item' v-id=c1d8 │ │ attrs[data-focusable=true] │ │ events[onfocus, onfocusin] │ └─────────────────────────────────────────┘ ``` #### Frame History The tree printer accumulates frames as your application runs, allowing you to see how the VDOM evolves over time. Each panel represents a snapshot of the VDOM at a specific render cycle. > **Note:** This feature has a performance impact and is intended for development and debugging only. Don't enable it in production environments. #### Technical Details The VDom tree printer is implemented as an `IVdomElementTranslator` with priority 0 (highest priority). When enabled, it intercepts the translation process and generates a text representation of the entire VDOM tree before passing through to the standard rendering pipeline. For more information on custom translators, see the [Custom Translators](/docs#custom-translators) guide. --- # Document: Component Gallery ### Interactive Component Gallery Explore every RazorConsole component in a live playground. The Gallery is distributed as a Native AOT application and does not require the .NET SDK or runtime. #### macOS and Linux ```shell curl -fsSL https://raw.githubusercontent.com/RazorConsole/RazorConsole/main/scripts/install-razor-console-app.sh | sh -s -- --app Gallery ``` The installer detects the operating system and CPU architecture, verifies the SHA-256 checksum, and installs `razorconsole-gallery` under `~/.local`. If `~/.local/bin` is not already on `PATH`, the installer prints the command needed to add it. #### Windows Run the following command in PowerShell: ```shell & ([scriptblock]::Create((irm https://raw.githubusercontent.com/RazorConsole/RazorConsole/main/scripts/install-razor-console-app.ps1))) -App Gallery ``` The installer selects the x64 or Arm64 package, verifies its SHA-256 checksum, installs it under `%LOCALAPPDATA%\RazorConsole\Gallery`, and adds that directory to the user `PATH`. #### Nightly channel Each successful `main` build is published as a uniquely versioned prerelease. Install the newest nightly build on macOS or Linux with: ```shell curl -fsSL https://raw.githubusercontent.com/RazorConsole/RazorConsole/main/scripts/install-razor-console-app.sh | sh -s -- --app Gallery --channel nightly ``` On Windows PowerShell: ```shell & ([scriptblock]::Create((irm https://raw.githubusercontent.com/RazorConsole/RazorConsole/main/scripts/install-razor-console-app.ps1))) -App Gallery -Channel Nightly ``` Nightly builds are unstable and replace an existing Gallery installation. Run the stable installer again to switch back. The installer selects the newest published `nightly-*` prerelease; draft or partially uploaded releases are never selected. Open a new terminal after installation and run: ```shell razorconsole-gallery ``` #### Manual installation Download the archive for your operating system and architecture from the [latest GitHub Release](https://github.com/RazorConsole/RazorConsole/releases/latest): | Operating system | Architectures | Archive | | --- | --- | --- | | Windows | x64, Arm64 | `.zip` | | Linux | x64, Arm64 | `.tar.gz` | | macOS | Intel x64, Apple Silicon Arm64 | `.tar.gz` | Extract the complete directory, including `Fonts/`, and run `razorconsole-gallery`. The release also contains `checksums-sha256.txt` for verifying downloads. The macOS archives are not yet signed or notarized. A browser download might therefore be stopped by Gatekeeper. After verifying the checksum, open **System Settings → Privacy & Security** to approve the application. The shell installer uses `curl`, which normally does not add the browser quarantine attribute. The gallery lets you experiment with layouts, inputs, and utilities before pulling them into your own app. For build details, see [Native Ahead-of-Time Compilation](/docs/native-aot). --- # Document: Choosing a .NET terminal UI library # Choosing a .NET terminal UI library For a C# terminal application, compare the programming model, the interactions you need, and the way you will distribute the finished app. There is no universal winner between RazorConsole, direct Spectre.Console, and Terminal.Gui. Prototype a representative screen and test it in your target terminal. This comparison checked upstream sources on **29 September 2026 (UTC)**. Competitor claims below are bounded to **Spectre.Console 0.57.2**, the separate **Spectre.Console.Cli 0.55.0** package, and **Terminal.Gui v2.5.0**. These versions are comparison references, not RazorConsole dependency requirements. Recheck newer versions before deciding. ## Compare the authoring models | Approach | How you build the UI | When to evaluate it | | --- | --- | --- | | RazorConsole | Compose Razor components, keep state in C#, and attach event handlers in markup. | Your team wants a familiar web-development/component experience in a terminal tool. | | Direct Spectre.Console | Assemble styled output, renderables, and prompts through C# APIs. | Tables, status output, and prompted workflows meet your needs without a Razor component layer. | | Terminal.Gui | Compose views, windows, and built-in widgets around an application lifecycle. | You prefer a terminal view/control model, for inline or full-screen applications. | Spectre.Console is part of RazorConsole's rendering foundation, not a wholly unrelated, mutually exclusive replacement. Choosing RazorConsole means choosing a component-oriented authoring model, not rejecting Spectre.Console. Sources: [Spectre.Console features](https://github.com/spectreconsole/spectre.console/blob/0.57.2/README.md#L18-L27), [Terminal.Gui features and application example](https://github.com/tui-cs/Terminal.Gui/blob/v2.5.0/README.md#L14-L65), and the [RazorConsole interactive tutorial](/docs/tutorial/hello-world/). ## Mouse and keyboard interaction RazorConsole provides built-in keyboard and mouse events that can be handled directly in Razor. That keeps state and interaction close to the component. Follow the [keyboard guide](/blog/keyboard-events/), [focus tutorial](/docs/tutorial/text-input-and-focus/), and [mouse tutorial](/docs/tutorial/mouse-events/). Native applications must enable mouse reporting; terminal capabilities, focus, and the rendering configuration still matter. Input support is **not exclusive to RazorConsole**: - Spectre.Console has interactive prompts. Its [selection prompt processes keyboard input](https://github.com/spectreconsole/spectre.console/blob/0.57.2/src/Spectre.Console/Prompts/SelectionPrompt.cs#L129-L150). This is not an output-only library. This source review does not establish its full mouse-support surface; do not interpret that research limit as a claim that mouse support is absent. - Terminal.Gui explicitly supports keyboard and mouse input. Its [mouse documentation](https://github.com/tui-cs/Terminal.Gui/blob/v2.5.0/docfx/docs/mouse.md#L22-L38) describes parsing, click synthesis, event routing, and command dispatch. Compare how comfortably you can express the interactions your app needs, not simply whether a library has an “interactive” checkbox. ## NativeAOT and distribution NativeAOT avoids JIT warm-up and can produce native applications that need no installed .NET runtime. It does not guarantee a particular startup time, executable size, or faster performance than another library. Measure the finished application, including initialization and external work. | Library or package | Verified scope | What to validate in your app | | --- | --- | --- | | RazorConsole | NativeAOT support is documented as experimental; native Gallery applications are distributed for supported OS/architecture combinations. | Platform build tools, runtime identifier, trimming/reflection, routing preservation, third-party packages, and required runtime assets. | | Spectre.Console 0.57.2 | Declares `IsAotCompatible` for net8.0, net9.0, and net10.0, excluding its netstandard2.0 target from that declaration. | The exact package/target you use and the complete application's dependency graph. | | Spectre.Console.Cli 0.55.0 | This separate command-line package explicitly sets `IsAotCompatible` and `IsTrimmable` to `false` for its modern .NET targets. | Do not infer the CLI package's support from the rendering package, or vice versa. | | Terminal.Gui v2.5.0 | Targets .NET 10, declares AOT/trimming compatibility, and includes a NativeAOT smoke application. | Application-specific publish/runtime behavior. Some AOT/trimming warnings are suppressed and the smoke path has dynamic-code/trimming annotations; this is not a universal warning-free guarantee. | Sources: [RazorConsole Native AOT requirements](/blog/native-aot/), [Spectre.Console project flags](https://github.com/spectreconsole/spectre.console/blob/0.57.2/src/Spectre.Console/Spectre.Console.csproj#L3-L8), [Spectre.Console.Cli project flags](https://github.com/spectreconsole/spectre.console.cli/blob/0.55.0/src/Spectre.Console.Cli/Spectre.Console.Cli.csproj#L3-L8), [Terminal.Gui project flags](https://github.com/tui-cs/Terminal.Gui/blob/v2.5.0/Terminal.Gui/Terminal.Gui.csproj#L17-L35), [smoke publish settings](https://github.com/tui-cs/Terminal.Gui/blob/v2.5.0/Tests/NativeAotSmoke/NativeAotSmoke.csproj#L1-L8), and [smoke application annotations](https://github.com/tui-cs/Terminal.Gui/blob/v2.5.0/Tests/NativeAotSmoke/Program.cs#L18-L51). This comparison inspects source declarations; it does not claim to have executed the competitors' publish or smoke tests. For RazorConsole, read the experimental limitations before choosing dependencies. Distribution may require assets alongside the executable; the Gallery's fonts are an example. A native binary is specific to its target platform, not one executable for every operating system. ## A practical decision sequence 1. Identify whether you need script-friendly output, a prompted workflow, or a sustained interactive UI. 2. Build the same small task with the programming model that best fits your team. Include keyboard navigation, focus, resizing, and mouse interactions you actually need. 3. Publish for your intended deployment model early, especially if NativeAOT is a requirement. 4. Evaluate accessibility, terminal compatibility, maintainability, and measured startup in your own application rather than relying on a generic ranking. If Razor composition fits, run the [interactive tutorial](/docs/tutorial/hello-world/) and browse [component examples](/components/). --- # Document: what's new in RazorConsole 0.6.0 # what's new in RazorConsole 0.6.0 RazorConsole 0.6.0 brings a new default layout engine, terminal mouse events, native app distribution, and an interactive learning path. This article introduces the changes relative to 0.5.0. For the complete change list and scope, read the [0.6.0 release notes](/release-notes/v0.6.0/). RazorConsole lets you build terminal applications with Razor components: keep state and event handlers close to your UI, compose reusable components, and render into terminal cells rather than browser HTML. Version 0.6.0 concentrates on taking ownership of layout, connecting mouse input to components, making the official apps easier to try, and teaching the programming model through working examples. ## WidgetLayout becomes the default The largest change is beneath the components. Starting with 0.6.0, an application that does not select a rendering pipeline uses **WidgetLayout** instead of the legacy Spectre pipeline. Why does that matter? A terminal UI needs more than a picture of its content. It needs to know where a component is, how much space it has, which region scrolls, and where input should go. The widget-based engine calculates layout before rendering, making bounds and layout metadata available to the rest of the application. Spectre.Console remains part of the rendering foundation; this is not a claim that it has been removed. For application authors, the important consequence is to **check layout and interaction together** when upgrading. Try a narrow terminal, resize while content is visible, tab through controls, and exercise scrolling and mouse input. A view that looks correct at one size is not the whole compatibility test. The legacy renderer is still available for comparison. In PowerShell, set this before launching your application: ```text $env:RAZORCONSOLE_RENDERING_PIPELINE = "LegacySpectre" dotnet run ``` Remove the override when you want to return to WidgetLayout: ```text Remove-Item Env:RAZORCONSOLE_RENDERING_PIPELINE ``` Existing `FlexBox`, `ITranslationMiddleware`, and `TranslationContext` APIs remain available. The important migration is behavioral: WidgetLayout does not invoke custom Spectre translators for every widget, and terminal/layout defaults change. The [Widget Layout guide](/blog/widget-layout/) and [custom translator guide](/blog/custom-translators/) explain how to adapt rendering extensions. The engine work landed in [#339](https://github.com/RazorConsole/RazorConsole/pull/339), including regression coverage for rendering and scrolling. There are no new benchmark results here, so the change should not be read as a measured performance claim. ## Mouse events join keyboard input RazorConsole 0.6.0 adds opt-in terminal mouse support, backed by native Windows and Unix input handling. WidgetLayout supplies the element bounds used to route terminal coordinates to the component under the pointer, so input and layout share the same view of the screen ([#339](https://github.com/RazorConsole/RazorConsole/pull/339)). Components can use familiar Razor handlers: `@onclick`, `@onmousedown`, `@onmouseup`, `@onmousemove`, `@onmouseenter`, `@onmouseleave`, and `@onwheel`. That opens up clickable controls, hover feedback, dragging, and wheel scrolling without giving up keyboard interaction. Enable `ConsoleAppOptions.ConsoleLiveDisplayOptions.EnableMouseEvents` in your host configuration. Mouse events are **off by default**, and enabling them also activates the alternate screen. Terminal mouse reporting must be supported by the terminal you run in. Handlers receive `MouseEventArgs`, or `WheelEventArgs` for wheel input, from `Microsoft.AspNetCore.Components.Web`. The names are familiar, but the coordinates are terminal cells, not browser pixels: `ClientX` and `ClientY` are zero-based terminal positions, while `OffsetX` and `OffsetY` are relative to the handling node. Wheel events use line-based `DeltaY` values (`DeltaMode = 1`). The event routing also supports left-click focus and drag capture. For example, `Select` options can be clicked, `Scrollable` responds to the wheel, and Snake demonstrates a draggable speed control. These interactions complement Tab and keyboard navigation rather than replace them. Try the [mouse-events tutorial](/docs/tutorial/mouse-events/) to explore the model; this is not a promise of complete browser pointer-event parity or identical behavior in every terminal. ## Try Gallery and Snake as native apps The Gallery is the quickest way to explore the component collection. In this release, it gains NativeAOT distribution alongside its existing .NET tool package. **Snake** joins it as a complete, responsive terminal-game showcase. The official native archive builds cover Linux, Windows, and macOS, each on x64 and ARM64. Shared installation scripts discover the appropriate app and platform archive, and offer two channels: - **Stable:** the latest published stable release, not whatever is currently on `main`. - **Nightly:** development builds for trying changes before a stable release. To pin a particular version instead of following a moving channel, download the matching platform archive directly from that version's GitHub Release. The installer work also addresses the Windows installation problem. See [#345](https://github.com/RazorConsole/RazorConsole/pull/345), [#346](https://github.com/RazorConsole/RazorConsole/pull/346), [#348](https://github.com/RazorConsole/RazorConsole/pull/348), and [#350](https://github.com/RazorConsole/RazorConsole/pull/350). NativeAOT can make an app runnable without a separately installed .NET runtime. It does **not** remove platform, terminal, trimming, dependency, or asset considerations. Support remains experimental, and a successful native build is not proof that every interaction works on every terminal. Review the [NativeAOT guide](/blog/native-aot/) before applying the same approach to your own application. The [Gallery documentation](/blog/component-gallery/) describes the available installation paths. ## Learn through eight interactive chapters The getting-started path is now a preview-first tutorial rather than a single Quick Start page. Its eight chapters build from Hello World toward a complete application: 1. Hello World. 2. State and events. 3. Text input and focus. 4. Mouse events. 5. Widget layout and resize. 6. Routing. 7. Asynchronous work. 8. A complete app. The browser preview and native `Tutorial.Runner` use the same Razor components. That makes the tutorial useful both for immediate experimentation and for understanding what runs in a real terminal. Start with [Hello World](/docs/tutorial/hello-world/), then change state, type into a control, and follow the effects through the UI. The tutorial was rebuilt in [#343](https://github.com/RazorConsole/RazorConsole/pull/343). A later navigation fix in [#356](https://github.com/RazorConsole/RazorConsole/pull/356) separates server and client loader exports while sharing the actual chapter resolver. Built-output Chromium tests cover the root and project-subdirectory deployments, including client navigation, terminal input, and restart. Those checks do not mean the browser terminal is free of every lifecycle issue: an intermittent xterm `Viewport._innerRefresh` diagnostic involving `dimensions` is still a known limitation. ## Smaller changes worth noticing Version 0.6.0 also includes rendering-lock fixes ([#320](https://github.com/RazorConsole/RazorConsole/pull/320)) and cursor visibility control ([#325](https://github.com/RazorConsole/RazorConsole/pull/325)). The website gains static HTML and metadata improvements, clearer homepage navigation, and a comparison article in [Blog](/blog/choosing-dotnet-tui/). The source checkout now includes a `net11.0` target alongside .NET 8, 9, and 10 and pins a .NET 11 preview SDK in `global.json`. That source-build requirement is different from the runtime needed by an application consuming a lower-target NuGet asset. Official native-app builds use `net10.0`. One fix that is **not** included is the pending Windows 10 rendering change: [#316](https://github.com/RazorConsole/RazorConsole/issues/316) is still open, and [#341](https://github.com/RazorConsole/RazorConsole/pull/341) is not part of 0.6.0. ## Preparing an existing app for 0.6.0 When upgrading, update your application's `RazorConsole.Core` reference to `0.6.0`. Then review the [release notes' migration section](/release-notes/v0.6.0/#upgrade--migration), rebuild any rendering extensions, and compare the default and legacy pipelines where useful. Alternate-screen rendering and cursor hiding now default to on; mouse input remains a separate opt-in. For NativeAOT applications, test the published binary on the platform and terminal you intend to support, not only a framework-dependent development build. For new applications, begin with the [interactive tutorial](/docs/tutorial/hello-world/) and use the Gallery to explore the controls. The release is about making component-based terminal applications more coherent to build, try, and learn. Its limitations remain explicit: experimental NativeAOT support, terminal-specific behavior, and unresolved issues are part of the upgrade decision, not details hidden by a successful build. --- # Chapter 1 · Hello World Build your first RazorConsole application and learn how Razor markup becomes an interactive terminal UI. ## Learning objectives By the end of this chapter, you will be able to: - create a console project that compiles Razor components; - render a Razor component as terminal character cells; - handle one small interaction; and - run the same component in the browser preview and a native terminal. ## How RazorConsole renders RazorConsole uses Razor's component model, but it does **not** create browser DOM elements. A component produces a virtual tree that RazorConsole lays out into rows and columns of terminal character cells. Those cells are then written as ANSI terminal output. Components still provide the useful Razor ideas—markup, C# state, parameters, and event callbacks—while the renderer targets a terminal instead of HTML. ## Prerequisites - The [.NET 8 SDK or newer](https://dotnet.microsoft.com/download) - A terminal with ANSI color support - An editor that supports C# and Razor files Check your SDK before continuing: ```shell dotnet --version ``` ## 1. Create the project ```shell dotnet new console -n HelloRazorConsole --framework net8.0 cd HelloRazorConsole dotnet add package RazorConsole.Core ``` The package command is for users consuming a published RazorConsole release from NuGet. The live preview above is built from the current repository checkout and can contain changes that are not in the latest NuGet package yet. To run this exact checkout instead, use the command in **Run the repository version** below. ## 2. Enable the Razor SDK Replace `HelloRazorConsole.csproj` with: ```xml Exe net8.0 enable enable ``` `Microsoft.NET.Sdk.Razor` invokes the Razor compiler for `.razor` files. A plain `Microsoft.NET.Sdk` console project does not generate the component classes used by `Program.cs`. ## 3. Add shared imports Create `_Imports.razor` in the project root: ```razor @using Microsoft.AspNetCore.Components @using Microsoft.AspNetCore.Components.Web @using RazorConsole.Components @using Spectre.Console ``` Imports in this file apply to every Razor component beneath it. ## 4. Create the component Create `HelloWorld.razor`: ```razor @code { private int _helloCount; private string InteractionMessage => _helloCount == 0 ? "Try the focused button below." : $"Hello again! Button pressed {_helloCount} {(_helloCount == 1 ? "time" : "times")}."; private void SayHello() => _helloCount++; } ``` `Rows` stacks terminal widgets vertically. `Markup` emits styled character cells, while `TextButton` participates in terminal focus and invokes its callback when you press Enter. The counter is only here to make the result testable; Chapter 2 will cover state and events in detail. ## 5. Start the host Replace `Program.cs` with: ```csharp using Microsoft.Extensions.Hosting; using RazorConsole.Core; var builder = Host.CreateApplicationBuilder(args); builder.UseRazorConsole(); using var host = builder.Build(); await host.RunAsync(); ``` Run the app: ```shell dotnet run ``` The button starts focused. Press Enter to activate it and Ctrl+C to stop the app. ## Complete source and repository run The complete runnable source for this chapter lives in: - `tutorial/Tutorial.Components/Chapters/HelloWorld.razor` - `tutorial/Tutorial.Runner/Program.cs` To run the exact component used by the browser preview from a RazorConsole checkout: ```shell dotnet run --project tutorial/Tutorial.Runner ``` This repository uses the preview SDK pinned in `global.json`. Install that SDK (including previews), or use the published-package steps above with a supported stable SDK. ## Exercise Add a second `TextButton` named **Reset greeting**. Its callback should set `_helloCount` back to zero. Then add `FocusedColor="@Color.Yellow"` so the two buttons are easy to distinguish while changing focus. ## Common setup errors | Symptom | Fix | | --- | --- | | `.razor` files are ignored or `HelloWorld` cannot be found | Use `Microsoft.NET.Sdk.Razor` in the project file, then rebuild. | | `RazorConsole` namespaces cannot be found | Run `dotnet restore` and confirm the `RazorConsole.Core` package or project reference exists. | | The UI draws with broken borders | Use a UTF-8 terminal and a font that contains box-drawing characters. | | The button does not respond | Give the terminal focus, then press Enter. Use Tab when the exercise adds another button. | | Checkout build requests another SDK | Install the preview SDK version in the repository's `global.json`; NuGet consumers do not need that checkout SDK. | [Next: Chapter 2 · State and Events →](/docs/tutorial/state-and-events) --- # Chapter 2 · State and Events Turn static terminal markup into a component whose output changes in response to user actions. ## Learning objectives - store UI state in component fields; - subscribe to component callbacks; - derive presentation from state; and - understand when RazorConsole rerenders. ## 1. Add component state Razor components keep state in ordinary C# fields and properties: ```razor @code { private int _count; private string CountText => $"Count: {_count}"; private Color CountColor => _count switch { > 0 => Color.Green, < 0 => Color.Red, _ => Color.White, }; } ``` The terminal output is a projection of `_count`. Do not manually redraw cells; update state and let RazorConsole render the new component tree. ## 2. Subscribe to callbacks `TextButton.OnClick` is an `EventCallback`. It accepts a method group or a lambda: ```razor @code { private void Change(int amount) => _count += amount; private void Reset() => _count = 0; } ``` After a synchronous or asynchronous event callback completes, the component rerenders automatically. Call `StateHasChanged` only when state changes outside Razor's normal callback or lifecycle flow. ## 3. Keep state local Each rendered component instance owns its fields. Restarting the browser preview constructs a new instance, so the counter returns to zero. Share state through parameters or a registered service only when multiple components truly need the same lifetime. ## Run this chapter locally ```shell dotnet run --project tutorial/Tutorial.Runner -- --state-events ``` Use Tab and Shift+Tab to move between buttons, then press Enter. ## Exercise Add a **+10** button, then disable the decrement action when the count reaches `-5`. [← Chapter 1 · Hello World](/docs/tutorial/hello-world) · [Next: Chapter 3 · Text Input and Focus →](/docs/tutorial/text-input-and-focus) --- # Chapter 3 · Text Input and Focus Collect text, bind values, handle submission, and make keyboard focus visible and predictable. ## Learning objectives - bind `TextInput.Value` to component state; - distinguish input, submit, focus, and blur callbacks; - navigate focus with the keyboard; and - mask sensitive display values without losing application state. ## 1. Bind a text input `@bind-Value` combines the `Value` and `ValueChanged` parameters: ```razor @code { private string _name = string.Empty; } ``` The value updates as terminal text input arrives. Use explicit `Value` and `ValueChanged` when the callback needs validation, normalization, or another side effect. ## 2. Input versus submit `OnInput` runs for each edit. `OnSubmit` runs when the focused input receives Enter: ```razor @code { private string _command = string.Empty; private Task RunCommandAsync(string? value) { _command = value ?? string.Empty; return Task.CompletedTask; } } ``` ## 3. Focus callbacks and traversal Subscribe with `OnFocus` and `OnBlur`. RazorConsole maintains one focused node, sends keyboard input to it, and uses the component's focused colors. Tab moves forward and Shift+Tab moves backward through focusable controls. ```razor ``` `MaskInput` only changes the rendered characters. The bound C# value still contains the original text and must be handled as sensitive data. ## Run this chapter locally ```shell dotnet run --project tutorial/Tutorial.Runner -- --text-input ``` Type in the first field, submit it, move to the masked field, and activate **Save profile**. ## Exercise Add a validation message that appears when the name has fewer than three characters. [← Chapter 2 · State and Events](/docs/tutorial/state-and-events) · [Next: Chapter 4 · Mouse Events →](/docs/tutorial/mouse-events) --- # Chapter 4 · Mouse Events Subscribe to mouse events from a Razor component, then combine them into hover, click, wheel, and drag interactions. ## Learning objectives By the end of this chapter, you will be able to: - subscribe to mouse events with Razor event attributes; - implement hover and click feedback; - use terminal-cell coordinates from `MouseEventArgs`; - combine down, move, and up events into a drag interaction; and - enable mouse reporting in a native terminal application. ## 1. Enable terminal mouse reporting Native terminals only send mouse input after an application enables mouse reporting. Update `Program.cs`: ```csharp using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using RazorConsole.Core; var builder = Host.CreateApplicationBuilder(args); builder.UseRazorConsole(configure: app => { app.Services.Configure(options => { options.ConsoleLiveDisplayOptions.EnableMouseEvents = true; }); }); using var host = builder.Build(); await host.RunAsync(); ``` The browser preview already enables mouse input. On Windows, macOS, and Linux, RazorConsole translates the platform's terminal input into the same component events, so the component itself does not need platform checks. Enabling mouse input also uses the alternate screen buffer so terminal mouse reporting is restored reliably on exit. ## 2. Subscribe to hover and click Razor does not have one `@onhover` event. Hover is a state built from `@onmouseenter` and `@onmouseleave`: ```razor
@code { private bool _hovered; private int _clickCount; private void HandleMouseEnter() => _hovered = true; private void HandleMouseLeave() => _hovered = false; private void HandleClick(MouseEventArgs _) => _clickCount++; } ``` Attach the event attributes to the element whose laid-out terminal cells should be interactive. RazorConsole hit-tests the pointer against that layout. A left-button down and up on the same element, without a drag between them, produces one `@onclick` callback. ## 3. Read pointer coordinates Mouse callbacks use the standard Blazor event argument types from `Microsoft.AspNetCore.Components.Web`: | Event | Argument | Useful values | | --- | --- | --- | | `@onmousedown`, `@onmouseup`, `@onmousemove`, `@onclick` | `MouseEventArgs` | `Button`, `ClientX`, `ClientY`, `OffsetX`, `OffsetY`, modifier keys | | `@onwheel` | `WheelEventArgs` | `DeltaY`, coordinates, modifier keys | | `@onmouseenter`, `@onmouseleave` | `MouseEventArgs` or no argument | pointer entry and exit | Unlike browser DOM coordinates, `ClientX` and `ClientY` are zero-based **terminal character-cell coordinates**, not CSS pixels. `OffsetX` and `OffsetY` are relative to the event target's top-left cell. This makes pointer movement line up directly with Widget Layout's integer `left` and `top` positions. ## 4. Build a draggable component Place a card in a fixed-size `Box`, and offset it with a `Padder` whose padding tracks the card's position. `position="relative"` and `position="absolute"` are not used here — `Padder` moves the card in normal flow instead: ```razor
@code { private int _cardX = 2; private int _cardY = 1; private bool _dragging; private double _dragStartX; private double _dragStartY; private int _cardStartX; private int _cardStartY; private void HandleMouseDown(MouseEventArgs e) { if (e.Button != 0) return; _dragging = true; _dragStartX = e.ClientX; _dragStartY = e.ClientY; _cardStartX = _cardX; _cardStartY = _cardY; } private void HandleMouseMove(MouseEventArgs e) { if (!_dragging) return; _cardX = Math.Clamp(_cardStartX + (int)(e.ClientX - _dragStartX), 0, 36); _cardY = Math.Clamp(_cardStartY + (int)(e.ClientY - _dragStartY), 0, 5); } private void HandleMouseUp(MouseEventArgs e) { HandleMouseMove(e); _dragging = false; } } ``` RazorConsole captures the element that handled the left-button down event. Its move and up handlers therefore keep receiving events even when the pointer leaves the card during a drag. Clamp the resulting position to keep the card inside its parent surface. The `data-focus-key` and `data-focusable` attributes let a left click focus the card. Add `@onkeydown` and arrow-key handlers, as the live example does, so the same interaction remains usable without a mouse. ## 5. Subscribe to the wheel The wheel can be handled on a larger parent surface: ```razor
@code { private int _wheelSteps; private void HandleWheel(WheelEventArgs e) => _wheelSteps += Math.Sign(e.DeltaY); } ``` Use the sign when one logical step is enough, or retain `DeltaY` when the magnitude matters. Wheel events are sent to the nearest ancestor that subscribes to `@onwheel`. ## Complete source and repository run The complete component used by the live preview is `tutorial/Tutorial.Components/Chapters/MouseEvents.razor`. Run that exact component from a RazorConsole checkout: ```shell dotnet run --project tutorial/Tutorial.Runner -- --mouse-events ``` Move the mouse across the first card, click it, scroll over the lower canvas, and drag the blue card. Press Ctrl+C to exit. ## Exercise Add a right-click counter. Inspect `MouseEventArgs.Button`, where `0` is the left button, `1` is the middle button, and `2` is the right button. Keep `@onclick` for the primary action and count the right button in `@onmousedown`. ## Common mouse-input errors | Symptom | Fix | | --- | --- | | No native terminal mouse events arrive | Set `ConsoleLiveDisplayOptions.EnableMouseEvents = true`. | | Hover remains active | Subscribe to both `@onmouseenter` and `@onmouseleave`. | | Drag jumps when it starts | Save both the pointer start and card start positions on mouse down, then apply their delta. | | The card escapes its surface | Clamp `left` and `top` to the surface size minus the card size. | | Text selection occurs instead of interaction in the browser | Click inside the terminal preview first; browser input is forwarded through xterm. | [← Chapter 3 · Text Input and Focus](/docs/tutorial/text-input-and-focus) · [Next: Chapter 5 · Widget Layout and Resize →](/docs/tutorial/widget-layout-and-resize) --- # Chapter 5 · Widget Layout and Resize Compose rows, columns, and wrapping content that responds to terminal dimensions. ## Learning objectives - choose between `Rows`, `Columns`, and `FlexBox`; - distinguish fixed cell sizes from expanding content; - build wrapping layouts; and - enable native terminal resize monitoring. ## 1. Think in terminal cells Widget Layout measures width and height in character cells. A `Width="18"` panel occupies 18 columns regardless of the font's pixel size. Parent widgets measure their children, allocate cells, and then arrange them. ```razor ``` Use `Rows` for vertical flow and `Columns` for a small, stable horizontal group. `Expand="true"` lets available width be shared rather than using only each child's desired width. ## 2. Wrap repeated content `FlexBox` is useful when the number or width of children can vary: ```razor @foreach (var card in cards) { } ``` Resize the preview: cards move onto another row when they no longer fit. Keep critical content first because extremely small terminals may clip the bottom of the composed view. ## 3. Enable resize monitoring The browser preview forwards xterm resize events automatically. For a native application, enable terminal monitoring: ```csharp builder.UseRazorConsole(configure: app => { app.Services.Configure(options => { options.EnableTerminalResizing = true; }); }); ``` RazorConsole invalidates layout and renders against the new terminal width and height. Components should express layout constraints rather than reading pixel dimensions. ## Run this chapter locally ```shell dotnet run --project tutorial/Tutorial.Runner -- --layout ``` Resize the terminal window and watch the cards wrap and the columns redistribute. ## Exercise Add a fourth status card and compare `Wrap="FlexWrap.Wrap"` with `FlexWrap.NoWrap` in a narrow terminal. [← Chapter 4 · Mouse Events](/docs/tutorial/mouse-events) · [Next: Chapter 6 · Routing →](/docs/tutorial/routing) --- # Chapter 6 · Routing and Multi-page Apps Use Blazor routing to switch terminal pages without rebuilding the application host. ## Learning objectives - declare routable Razor components; - render matched pages with `Router` and `RouteView`; - navigate from terminal controls; and - provide a not-found view. ## 1. Create routed pages A page is a component with an `@page` directive: ```razor @page "/settings" ``` Put each page in its own `.razor` file. Routes must be unique within the assembly scanned by the router. ## 2. Add the router The root component discovers pages and renders the current match: ```razor ``` `RouteView` creates the selected page component. State local to the previous page is disposed when navigation replaces it. ## 3. Navigate from a component Inject `NavigationManager` and call `NavigateTo` from a normal callback: ```razor @inject NavigationManager Navigation @code { private void OpenSettings() => Navigation.NavigateTo("/settings"); } ``` The live example includes a missing route so you can see the `NotFound` branch. ## Run this chapter locally ```shell dotnet run --project tutorial/Tutorial.Runner -- --routing ``` Use the three navigation buttons to switch between Home, Settings, and the missing route. ## Exercise Add an `/about` page and a fourth navigation button. Give the page a parameterized title component. [← Chapter 5 · Widget Layout and Resize](/docs/tutorial/widget-layout-and-resize) · [Next: Chapter 7 · Async Work →](/docs/tutorial/async-work) --- # Chapter 7 · Async Work, Loading, and Errors Keep the terminal responsive while work is in progress and render every operation state explicitly. ## Learning objectives - use asynchronous event callbacks; - model loading, success, error, and cancellation; - prevent duplicate work; and - clean up cancellation resources. ## 1. Model operation state An async UI should make its states explicit: ```razor @if (_loading) { } else if (_error is not null) { } else { } ``` Avoid blocking with `.Result`, `.Wait()`, or `Thread.Sleep`. Those prevent input and rendering from progressing. ## 2. Await work in the callback ```csharp private async Task LoadAsync() { _loading = true; _error = null; try { _result = await service.LoadAsync(_request.Token); } catch (OperationCanceledException) { _result = "Request cancelled."; } catch (Exception ex) { _error = ex.Message; } finally { _loading = false; } } ``` Razor renders once when the callback yields and again when it completes. Guard actions while `_loading` or cancel the previous request before starting another one, and expose cancellation when users may need to regain control. ## 3. Treat errors as UI state Catch errors close enough to provide an actionable message, while still logging diagnostic details in the service or host. A retry button can call the same async callback after the error is shown. ## Run this chapter locally ```shell dotnet run --project tutorial/Tutorial.Runner -- --async-work ``` Try success, simulated failure, and cancellation. Each path returns to an interactive state. ## Exercise Add a retry counter and use an increasing delay before each retry. [← Chapter 6 · Routing](/docs/tutorial/routing) · [Next: Chapter 8 · Complete App →](/docs/tutorial/complete-app) --- # Chapter 8 · Build a Complete Interactive App Combine component state, text input, focus, mouse input, layout, and callbacks into a small task board. ## Learning objectives - design a feature as small state transitions; - render a collection with stable keys; - support keyboard and mouse interaction; and - organize a production-ready next step. ## 1. Define the data model The task board owns a list and a draft value: ```csharp private string _draft = string.Empty; private List _tasks = []; private sealed record TaskItem(Guid Id, string Title) { public bool Done { get; set; } } ``` Keep UI-only state in the component. Move persistence and external I/O into injected services so those concerns remain testable without a terminal. ## 2. Add and render tasks ```razor @foreach (var task in _tasks) {
} ``` `@key` preserves the identity of each rendered row when items are added or removed. Clicking a task toggles it through the raw mouse-event subscription learned in Chapter 4. ## 3. Make actions deterministic Each callback performs one state transition: ```csharp private void Add() { var title = _draft.Trim(); if (title.Length == 0) return; _tasks.Add(new(Guid.NewGuid(), title)); _draft = string.Empty; } private void Toggle(TaskItem task) => task.Done = !task.Done; private void ClearCompleted() => _tasks.RemoveAll(task => task.Done); ``` Small transitions are easy to test. The final example exposes add, toggle, clear, and reset without embedding terminal input parsing in application code. ## 4. Prepare the app for real use For a larger application, split task rows and editors into child components, inject a repository for persistence, add routed detail pages, and wrap service calls in the loading/error pattern from Chapter 7. Retain keyboard alternatives for every mouse-only action. ## Run the complete app locally ```shell dotnet run --project tutorial/Tutorial.Runner -- --complete-app ``` Add tasks with the input, click existing tasks to toggle them, and use the focused action buttons with Enter. ## Final challenge Persist tasks to a JSON file through an injected service and add a routed view that shows only completed tasks. [← Chapter 7 · Async Work](/docs/tutorial/async-work) --- # Component: Align Wraps child content in an alignment container. ### Parameters: | Name | Type | Default | Description | | ------------ | ---------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------- | | ChildContent | Microsoft.AspNetCore.Components.RenderFragment | - | Aligns content horizontally and vertically within a container using Spectre.Console's Align renderable. | | Height | int? | - | Height of the alignment container in characters. If `null`, automatically determined by content. | | Horizontal | HorizontalAlignment | - | Horizontal alignment of the content. Default is Left. | | Vertical | VerticalAlignment | - | Vertical alignment of the content. Default is Top. | | Width | int? | - | Width of the alignment container in characters. If `null`, automatically determined by content. | ### Usage Example (Align_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components ```` --- # Component: Border Creates a bordered panel around its children. ### Parameters: | Name | Type | Default | Description | | ------------ | ---------------------------------------------- | ------- | -------------------------------------------------------------------------------------- | | BorderColor | Color? | - | Color of the border. If `null`, uses default console color. | | BoxBorder | BoxBorder | - | Style of the border. Default is Rounded. See BoxBorder for available styles. | | ChildContent | Microsoft.AspNetCore.Components.RenderFragment | - | Renders a simple border around content without a title (simplified wrapper for Panel). | | Padding | Padding | - | Padding inside the border as (left, top, right, bottom). Default is (0, 0, 0, 0). | ### Usage Example (Border_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components ```` --- # Component: BarChart Renders a horizontal bar chart with optional label, colors and value display. ### Parameters: | Name | Type | Default | Description | | --------------- | ------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | BarChartItems | List | - | Renders a bar chart using Spectre.Console's BarChart renderable. | | Culture | CultureInfo? | - | CultureInfo to use when rendering values. Default is CurrentCulture. | | Label | string? | - | Label (title) displayed above the bar chart. | | LabelAlignment | Justify? | - | Alignment of the chart label. Options: Left, Center, Right. | | LabelBackground | Color? | - | Background color of the chart label. Default is Plain background (transparent). | | LabelDecoration | Decoration? | - | Text decoration for the chart label (bold, italic, underline, etc.). Default is None. | | LabelForeground | Color? | - | Foreground (text) color of the chart label. Default is Plain foreground. | | MaxValue | double? | - | Fixed maximum value for scaling bars. When set, bars scale relative to this value instead of the largest data point. Example: `MaxValue = 100` creates a progress chart (0–100%). | | ShowValues | bool | - | Whether numeric values should be displayed next to each bar. Default is `false`. | | Width | int? | - | Width of the bar chart in characters. If `null`, uses available console width. | ### Usage Example (BarChart_1.razor): ````razor @using RazorConsole.Components @using System.Globalization @using Spectre.Console @code { private List SalesData => new() { new BarChartItem("Jan", 65.2, Color.FromHex("29B8DB")), new BarChartItem("Feb", 78.9, Color.FromHex("0DBC79")), new BarChartItem("Mar", 91.5, Color.FromHex("F5F543")) }; } ```` --- # Component: BreakdownChart Displays a breakdown chart showing proportional data. ### Parameters: | Name | Type | Default | Description | | ----------------------- | -------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ | | BreakdownChartItems | System.Collections.Generic.List{Spectre.Console.IBreakdownChartItem} | - | Renders a breakdown chart using Spectre.Console's BreakdownChart renderable. | | Compact | System.Boolean | - | Whether the chart and tags should be rendered in compact mode. Default is `false`. | | Culture | System.Globalization.CultureInfo | - | CultureInfo to use when rendering values. Default is CurrentCulture. | | Expand | System.Boolean | - | Whether the chart should expand to available space. When `false`, width is automatically calculated. Default is `false`. | | ShowTagValues | System.Boolean | - | Whether to show tag values. Default is `false`. | | ShowTagValuesPercentage | System.Boolean | - | Whether to show tag values with percentage. Default is `false`. | | ShowTags | System.Boolean | - | Whether to show tags. Default is `false`. | | ValueColor | System.Nullable{Spectre.Console.Color} | - | Color in which the values will be shown. If `null`, uses default color. | | Width | System.Nullable{System.Int32} | - | Width of the breakdown chart in characters. If `null`, automatically calculated. | ### Usage Example (BreakdownChart_1.razor): ````razor @using RazorConsole.Components @using System.Globalization @using Spectre.Console @code { private List Expenses => new() { new BreakdownChartItem("Food", 3200, Color.FromHex("F5F543")), new BreakdownChartItem("Transport", 1800, Color.FromHex("3B8EEA")), new BreakdownChartItem("Utilities", 2500, Color.FromHex("CD3131")), new BreakdownChartItem("Fun", 1400, Color.FromHex("0DBC79")), new BreakdownChartItem("Other", 1100, Color.FromHex("666666")) }; } ```` --- # Component: Columns Arranges children in columns. ### Parameters: | Name | Type | Default | Description | | ------------ | ---------------------------------------------- | ------- | ---------------------------------------------------------------------------------- | | ChildContent | Microsoft.AspNetCore.Components.RenderFragment | - | Content to arrange in columns. | | Expand | System.Boolean | - | Arranges content in horizontal columns using Spectre.Console's Columns renderable. | | FillHeight | System.Boolean | - | Whether columns should fill the height offered by their parent. | | FillWidth | System.Boolean | - | Whether columns should fill the width offered by their parent. | ### Usage Example (Columns_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components ```` --- # Component: Figlet Renders ASCII art text. ### Parameters: | Name | Type | Default | Description | | ------- | ------------- | ------- | ----------------------------------------------------------------------- | | Color | Color? | - | Color of the FIGlet text. Default is Default (console's default color). | | Content | string? | - | Text content to render as FIGlet ASCII art. | | Font | System.String | - | Custom FIGlet font name or path to .flf file | | Justify | Justify? | - | Horizontal alignment of the FIGlet text. Default is Center. | ### Usage Example (Figlet_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components ```` --- # Component: Grid Arranges children in a grid layout. ### Parameters: | Name | Type | Default | Description | | ------------ | ---------------------------------------------- | ------- | -------------------------------------------------------------------------------- | | ChildContent | Microsoft.AspNetCore.Components.RenderFragment | - | Arranges content in a multi-column grid using Spectre.Console's Grid renderable. | | Columns | System.Int32 | - | Number of columns in the grid. Default is 2. Must be positive. | | Expand | System.Boolean | - | Whether the grid should expand to fill available space. Default is `false`. | | Width | System.Nullable{System.Int32} | - | Width of the grid in characters. If `null`, automatically determined by content. | ### Usage Example (Grid_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components ```` --- # Component: ModalWindow Renders a modal in a dialog. ### Parameters: | Name | Type | Default | Description | | -------------------- | --------------------------------------------------------------------------- | ------- | --------------------------------------------------------- | | AdditionalAttributes | System.Collections.Generic.IReadOnlyDictionary{System.String,System.Object} | - | Additional HTML attributes to apply to the table element. | | ChildContent | Microsoft.AspNetCore.Components.RenderFragment | - | | | IsOpened | System.Boolean | - | Parameter that determines visibility of modal window | ### Usage Example (ModalWindow_1.razor): ````razor @using RazorConsole.Components @using Spectre.Console @using Panel = RazorConsole.Components.Panel @if (!_isOpened) { } @code { private bool _isOpened = true; private Color _currentColor = Color.Orange1; private void CloseModal() { _isOpened = false; StateHasChanged(); } private void OpenModal() { _isOpened = true; StateHasChanged(); } private void ChangeColor() { _currentColor = _currentColor == Color.Orange1 ? Color.Red3 : Color.Orange1; StateHasChanged(); } } ```` --- # Component: Markdown Renders markdown content. ### Parameters: | Name | Type | Default | Description | | ------- | ------------- | ------- | -------------------------------------------------------------- | | Content | System.String | - | Markdown content to render. Supports standard Markdown syntax. | ### Usage Example (Markdown_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components @code { private static string markdownText => @"# Welcome to Markdown This is a **bold** statement and this is *italic*. You can also have `inline code`. ## Features Here's what you can do with markdown: - Create bullet lists - With multiple items - Like this one ### Ordered Lists 1. First item 2. Second item 3. Third item ### Nested Lists - Top level item A - Child item A.1 - Grandchild item `A.1.a` - Child item A.2 ### Code Blocks ```csharp public class HelloWorld { public static void Main() { Console.WriteLine(""Hello, World!""); } } ``` ```python def greet(name): print(f""Hello, {name}!"") greet(""World"") ``` ### Quotes > This is a quote block. > It can span multiple lines. ### Tables | Feature | Supported | Description | |---------|-----------|-------------| | Basic Tables | ✓ | Simple table formatting | | Column Alignment | ✓ | Left, center, right alignment | | Complex Content | ✓ | Code, links, and formatting in cells | Here's a more detailed table: | Language | Extension | Example Code | Performance | |:---------|:---------:|:-------------|------------:| | C# | `.cs` | `Console.WriteLine(""Hello"");` | Fast | | Python | `.py` | `print(""Hello"")` | Medium | | JavaScript | `.js` | `console.log(""Hello"");` | Variable | --- That's a horizontal rule above! ".Trim(); } ```` --- # Component: Markup Renders styled text with markup. ### Parameters: | Name | Type | Default | Description | | ---------- | ----------- | ------- | ------------------------------------------------------------------------------------------------------ | | Background | Color? | - | Background color. Default is Plain background. | | Content | string? | - | Text content to render. Content is automatically escaped to prevent markup interpretation. | | Decoration | Decoration? | - | Text decoration (bold, italic, underline, etc.). Default is None. Combine decorations with bitwise OR. | | Foreground | Color? | - | Foreground (text) color. Default is Plain foreground. | | link | string? | - | Optional hyperlink URL. When specified, text renders as a clickable link in supported terminals. | ### Usage Example (Markup_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components ```` --- # Component: Padder Adds padding around its children. ### Parameters: | Name | Type | Default | Description | | ------------ | ---------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ChildContent | Microsoft.AspNetCore.Components.RenderFragment | - | Content to which padding will be applied. | | Expand | System.Boolean | - | Whether the padder should expand to fill available space in native widget layout containers. | | FillHeight | System.Boolean | - | Whether the padder should fill the height offered by its parent. | | FillWidth | System.Boolean | - | Whether the padder should fill the width offered by its parent. | | Margin | Spectre.Console.Padding | - | Margin to apply outside the padded content. | | Padding | Spectre.Console.Padding | - | Padding to apply as (left, top, right, bottom) in characters. Default is (0, 0, 0, 0). Example: `new Padding(2, 1, 2, 1)` adds 2 characters horizontally and 1 line vertically. | ### Usage Example (Padder_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components ```` --- # Component: Panel Creates a bordered panel with optional title. ### Parameters: | Name | Type | Default | Description | | ------------ | ---------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- | | Border | BoxBorder | - | Style of the panel border. See BoxBorder for available styles like Rounded, Square, and Double. | | BorderColor | Color? | - | Color of the panel border. If `null`, uses default console color. | | ChildContent | Microsoft.AspNetCore.Components.RenderFragment | - | Renders a panel with optional title, border, and padding using Spectre.Console's Panel renderable. | | Expand | bool | - | Whether the panel should expand to fill available space. Default is `false`. | | FillHeight | System.Boolean | - | Whether the panel should fill the height offered by its parent. | | FillWidth | System.Boolean | - | Whether the panel should fill the width offered by its parent. | | Height | int? | - | Height of the panel in lines. If `null` or less than 1, height is automatically determined by content. | | Margin | System.Nullable{Spectre.Console.Padding} | - | Margin outside the panel border as (left, top, right, bottom). If `null`, no margin is applied. | | Padding | Padding? | - | Padding inside the panel border as (left, top, right, bottom). If `null`, no padding is applied. | | Title | string? | - | Title text displayed at the top of the panel border. If `null` or empty, no title is shown. | | TitleColor | Color? | - | Color of the panel title. If `null`, uses default console color. | | Width | int? | - | Width of the panel in characters. If `null` or less than 1, width is automatically determined by content. | ### Usage Example (Panel_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components ```` --- # Component: Rows Arranges children in rows. ### Parameters: | Name | Type | Default | Description | | ------------ | ---------------------------------------------- | ------- | -------------------------------------------------------------------------- | | ChildContent | Microsoft.AspNetCore.Components.RenderFragment | - | Content to arrange in rows. | | Expand | System.Boolean | - | Arranges content in vertical rows using Spectre.Console's Rows renderable. | | FillHeight | System.Boolean | - | Whether rows should fill the height offered by their parent. | | FillWidth | System.Boolean | - | Whether rows should fill the width offered by their parent. | ### Usage Example (Rows_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components ```` --- # Component: Scrollable Provides scrollable content area. ### Parameters: | Name | Type | Default | Description | | ------------------- | ----------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------- | | ChildContent | Microsoft.AspNetCore.Components.RenderFragment{RazorConsole.Components.Scrollable`1.ScrollContext{{TItem}}} | - | Child content template that receives the visible items and scroll context. | | IsScrollbarEmbedded | System.Boolean | - | Flag that determines will scrollbar be embedded or not. | | Items | System.Collections.Generic.IReadOnlyList{{TItem}} | - | Provides scrollable navigation through a list of items with keyboard support. | | PageSize | System.Int32 | - | Number of items visible at one time (page size). | | ScrollOffset | System.Int32 | - | Current scroll offset (index of the first visible item). | | ScrollOffsetChanged | Microsoft.AspNetCore.Components.EventCallback{System.Int32} | - | Event callback invoked when the scroll offset changes. | | Scrollbar | RazorConsole.Core.Rendering.ScrollbarSettings | - | Scrollbar settings. If provided, the scrollbar is enabled. | ### Usage Example (Scrollable_1.razor): ````razor @using Microsoft.AspNetCore.Components.Web @using Spectre.Console @using RazorConsole.Components N Letter @foreach (var item in context) { } @*Spaces beteween panels*@ N Letter @foreach (var item in context) { }
    @foreach (var item in context) {
  1. }
    @foreach (var item in context) {
  1. }
@code { private Color _tableColor = Color.White; record Item(int Number, string Letter, Color Color); private IReadOnlyList<(int Number, string Letter, Color Color)> GetAlphabetData() { return [ (1, "A", Color.Yellow), (2, "B", Color.Red), (3, "C", Color.Aqua), (4, "D", Color.Orange1), (5, "E", Color.Chartreuse1), (6, "F", Color.Magenta1), (7, "G", Color.Green3), (8, "H", Color.Blue), (9, "I", Color.Purple), (10, "J", Color.Gold1), (11, "K", Color.Turquoise2), (12, "L", Color.HotPink), (13, "M", Color.Lime), (14, "N", Color.LightCoral), (15, "O", Color.SkyBlue1) ]; } private void OnFocusTable(FocusEventArgs e) { _tableColor = Color.Aqua; } private void OnFocusOutTable(FocusEventArgs e) { _tableColor = Color.White; } } ```` --- # Component: ViewHeightScrollable Provides scrollable content area that scrolls through physical lines of any content. Has all functionalities of default Scrollable. ### Parameters: | Name | Type | Default | Description | | ------------------- | ---------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------- | | ChildContent | Microsoft.AspNetCore.Components.RenderFragment{RazorConsole.Components.ViewHeightScrollable.ScrollContext} | - | | | IsScrollbarEmbedded | System.Boolean | - | Flag that determines will scrollbar be embedded or not. | | LinesToRender | System.Int32 | - | Number of lines visible at one time. | | ScrollOffset | System.Int32 | - | Current scroll offset (index of the first visible line). | | ScrollOffsetChanged | Microsoft.AspNetCore.Components.EventCallback{System.Int32} | - | Event callback invoked when the scroll offset changes. | | Scrollbar | RazorConsole.Core.Rendering.ScrollbarSettings | - | Scrollbar settings. If provided, the scrollbar is enabled. | ### Usage Example (ViewHeightScrollable_1.razor): ````razor @using RazorConsole.Components @using Spectre.Console @using Markup = RazorConsole.Components.Markup @using Panel = RazorConsole.Components.Panel @code { private readonly string _largeText =@" #### Counter.razor ```razor @using Microsoft.AspNetCore.Components @using Microsoft.AspNetCore.Components.Web @using RazorConsole.Components

Current count

@code { private int currentCount = 0; private void IncrementCount() { currentCount++; } } ``` #### Program.cs ```csharp using Microsoft.Extensions.Hosting; using RazorConsole.Core; IHostBuilder hostBuilder = Host.CreateDefaultBuilder(args) .UseRazorConsole(); IHost host = hostBuilder.Build(); await host.RunAsync(); ``` ".Trim(); } ```` --- # Component: Select Interactive dropdown for choosing a value with keyboard navigation. ### Parameters: | Name | Type | Default | Description | | ------------------------ | --------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------ | | AdditionalAttributes | System.Collections.Generic.IReadOnlyDictionary{System.String,System.Object} | - | Additional attributes to apply to the root element. | | Comparer | System.Collections.Generic.IEqualityComparer{{TItem}} | - | Custom comparer for items in the list. Default is object.Equals() | | Expand | System.Boolean | - | Expands the rows layout to fill available space when true. | | FocusedValue | {TItem} | - | Currently highlighted option during keyboard navigation (may differ from committed Value). | | FocusedValueChanged | Microsoft.AspNetCore.Components.EventCallback{{TItem}} | - | Event raised when the focused option changes. | | Formatter | System.Func{{TItem},System.String} | - | Custom formatter for the items in the list. Default is object.ToString() | | IsFocused | System.Boolean | - | Indicates whether the select currently has focus. | | IsFocusedChanged | Microsoft.AspNetCore.Components.EventCallback{System.Boolean} | - | Event raised when the focus state changes. | | OptionDecoration | Spectre.Console.Decoration | - | Text decoration for non-focused options. | | OptionForeground | Spectre.Console.Color | - | Foreground color for non-focused options. | | Options | {TItem}[] | - | Available options rendered by the select. | | SelectedIndicator | System.Char | - | Character used to indicate the focused option. | | SelectedOptionDecoration | Spectre.Console.Decoration | - | Text decoration for the focused option. | | SelectedOptionForeground | Spectre.Console.Color | - | Foreground color for the focused option. | | UnselectedIndicator | System.Char | - | Character used to indicate non-focused options. | | Value | {TItem} | - | Currently committed option value. | | ValueChanged | Microsoft.AspNetCore.Components.EventCallback{{TItem}} | - | Event raised when the committed value changes. | ### Usage Example (Select_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components
Name Value
```` --- # Component: TextInput Single-line text input field. ### Parameters: | Name | Type | Default | Description | | --------------------- | ------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- | | AdditionalAttributes | System.Collections.Generic.IReadOnlyDictionary{System.String,System.Object} | - | Additional HTML attributes to apply to the input element. | | BorderColor | Spectre.Console.Color | - | Border color when input is not focused and not disabled. | | BorderPadding | Spectre.Console.Padding | - | Padding between the border and the content. | | BorderStyle | Spectre.Console.BoxBorder | - | Style of the input border. | | ContentPadding | Spectre.Console.Padding | - | Padding around the input content. | | Disabled | System.Boolean | - | Whether the input is disabled. When disabled, cannot receive focus or accept input. | | DisabledBorderColor | Spectre.Console.Color | - | Border color when input is disabled. | | Expand | System.Boolean | - | Whether the input should expand to fill available horizontal space. | | FillHeight | System.Boolean | - | Whether the input should fill the height offered by its parent. | | FillWidth | System.Boolean | - | Whether the input should fill the width offered by its parent. | | FocusOrder | System.Nullable{System.Int32} | - | Tab order for keyboard navigation. Lower values receive focus first. If `null`, natural document order is used. | | FocusedBorderColor | Spectre.Console.Color | - | Border color when input has focus. | | Label | System.String | - | Label text displayed above the input field. If null or empty, no label is shown. | | LabelColor | Spectre.Console.Color | - | Color of the label text. | | LabelDecoration | Spectre.Console.Decoration | - | Decoration style of the label text. | | MaskInput | System.Boolean | - | Whether input characters should be masked (displayed as bullets). Useful for password fields. Default is `false`. | | OnBlur | Microsoft.AspNetCore.Components.EventCallback{Microsoft.AspNetCore.Components.Web.FocusEventArgs} | - | Event callback invoked when input loses focus. | | OnFocus | Microsoft.AspNetCore.Components.EventCallback{Microsoft.AspNetCore.Components.Web.FocusEventArgs} | - | Event callback invoked when input receives focus. | | OnInput | Microsoft.AspNetCore.Components.EventCallback{System.String} | - | Event callback invoked on each character input (fired for every keystroke). | | OnSubmit | Microsoft.AspNetCore.Components.EventCallback{System.String} | - | Event callback invoked when submitting input (typically by pressing Enter). | | Placeholder | string? | - | Placeholder text shown when input is empty. | | PlaceholderColor | Spectre.Console.Color | - | Color of the placeholder text. | | PlaceholderDecoration | Spectre.Console.Decoration | - | Decoration style of the placeholder text. | | Value | string | - | Current value of the text input. Supports two-way binding with @bind-Value. | | ValueChanged | EventCallback | - | Event callback invoked when the value changes. | | ValueColor | Spectre.Console.Color | - | Color of the input value text. | | ValueExpression | System.Linq.Expressions.Expression{System.Func{System.String}} | - | Expression identifying the bound value for validation scenarios with EditContext. | ### Usage Example (TextInput_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components @code { private static readonly Padding _inputPadding = new(1, 0, 1, 0); private string _firstName = string.Empty; private string _secretNote = string.Empty; private string PreviewText => _firstName.Length == 0 ? "Preview: (none)" : $"Preview: {_firstName}"; private string SecretLengthText => $"Stored secret length: {_secretNote.Length}"; private Task OnFirstNameChangedAsync(string? value) { _firstName = value ?? string.Empty; StateHasChanged(); return Task.CompletedTask; } private Task OnSecretChangedAsync(string? value) { _secretNote = value ?? string.Empty; StateHasChanged(); return Task.CompletedTask; } } ```` --- # Component: TextButton Interactive button component. ### Parameters: | Name | Type | Default | Description | | -------------------- | --------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------- | | BackgroundColor | Spectre.Console.Color | - | Background color when not focused. Default is Default. | | Content | System.String | - | Text content displayed on the button. | | FocusOrder | System.Nullable{System.Int32} | - | Tab order for keyboard navigation. Lower values receive focus first. If `null`, natural document order is used. | | FocusedColor | Spectre.Console.Color | - | Background color when focused. Default is DeepSkyBlue1. | | HoverColor | Spectre.Console.Color | - | Background used when the pointer is over an actionable button. | | HoverForegroundColor | Spectre.Console.Color | - | Text color paired with HoverColor. | | OnClick | Microsoft.AspNetCore.Components.EventCallback | - | Event callback invoked when the button is clicked. | ### Usage Example (TextButton_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components

Count: @count

@code { private int count = 0; private void HandleClick() { count++; StateHasChanged(); } } ```` --- # Component: FlexBox Lays out children using a CSS-like flexbox model with configurable direction, justification, alignment, wrapping, and gap. ### Parameters: | Name | Type | Default | Description | | ------------ | ---------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- | | Align | FlexAlign | - | How items are aligned along the cross axis. Default is Start. | | ChildContent | Microsoft.AspNetCore.Components.RenderFragment | - | The child content to lay out within the flex container. | | Direction | FlexDirection | - | Lays out child content using a CSS-like flexbox model with configurable direction, justification, alignment, wrapping, and gap. | | Gap | int | - | Spacing between items along the main axis in characters (Row) or lines (Column). Default is 0. | | Height | int? | - | Explicit height constraint in lines. When null, uses the natural content height. | | Justify | FlexJustify | - | How free space is distributed along the main axis. Default is Start. | | Width | int? | - | Explicit width constraint in characters. When null, uses all available width. | | Wrap | FlexWrap | - | Whether items wrap to new lines when they exceed the available space. Default is NoWrap. | ### Usage Example (FlexBox_1.razor): ````razor @using Spectre.Console @using RazorConsole.Components @using RazorConsole.Core.Renderables @* ── Name fields with SpaceBetween ── *@ @* ── Email field full width ── *@ @* ── Password field full width ── *@ @* ── Role selector with description ── *@