FractalDataWorks.UI.Components.RazorConsole 0.1.0-preview.15

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

FractalDataWorks.UI.Components.RazorConsole

Razor-syntax terminal UI framework that compiles declarative markup to Spectre.Console at build time. Provides a Blazor-like developer experience for creating terminal applications with compile-time validation and IntelliSense support.

Overview

RazorConsole enables writing terminal UI components using Razor syntax that compiles to Spectre.Console rendering code. Components inherit from a CRTP base class that provides lifecycle methods and Spectre.Console integration.

Status: Early implementation. The source generator compiles .cshtml files to C# partial classes with Prompt() and Render() method stubs.

Key Features:

  • Razor syntax with @inherits, @using, and @code blocks
  • Component base class with Blazor-like lifecycle methods
  • Spectre.Console integration through TUIComponent base
  • Source generator produces partial classes from .cshtml files

Target Framework: net10.0

Dependencies:

  • FractalDataWorks.UI.Abstractions
  • FractalDataWorks.UI.Components.TUI
  • Spectre.Console
  • Microsoft.AspNetCore.Components

Architecture

.cshtml file
     |
     v
RazorConsoleSourceGenerator (build-time)
     |
     v
Generated .g.cs partial class
     |
     v
RazorConsoleComponent<TModel> base class
     |
     v
TUIComponent<TSelf, TModel> (Spectre.Console integration)

Component Base Class

From RazorConsoleComponent.cs:15-92:

/// <summary>
/// Base class for RazorConsole components.
/// Inherits from TUIComponent to provide Spectre.Console integration.
/// </summary>
/// <typeparam name="TModel">The model type this component renders</typeparam>
public abstract class RazorConsoleComponent<TModel> : TUIComponent<RazorConsoleComponent<TModel>, TModel>
{
    /// <summary>
    /// The console instance to render to.
    /// Set by generated code or consumer.
    /// </summary>
    protected IAnsiConsole Console { get; set; } = AnsiConsole.Console;

    /// <summary>
    /// Gets or sets the model being rendered.
    /// Alias for Value property from base class for Razor compatibility.
    /// </summary>
    public TModel Model
    {
        get => Value!;
        set => Value = value;
    }

    /// <summary>
    /// Called when component initializes.
    /// Override in derived components for initialization logic.
    /// </summary>
    protected virtual void OnInitialized()
    {
    }

    /// <summary>
    /// Async version of OnInitialized.
    /// </summary>
    protected virtual Task OnInitializedAsync()
    {
        return Task.CompletedTask;
    }

    /// <summary>
    /// Called when parameters are set.
    /// </summary>
    protected virtual void OnParametersSet()
    {
    }

    /// <summary>
    /// Async version of OnParametersSet.
    /// </summary>
    protected virtual Task OnParametersSetAsync()
    {
        return Task.CompletedTask;
    }

    /// <summary>
    /// Prompts for interactive input.
    /// Generated by source generator from .cshtml markup.
    /// </summary>
    public abstract override Task<TModel?> Prompt(IAnsiConsole console);

    /// <summary>
    /// Renders component to console (display-only).
    /// Generated by source generator from .cshtml markup.
    /// </summary>
    public abstract override void Render(IAnsiConsole console);
}

Parameter Attributes

From ParameterAttribute.cs:1-12:

/// <summary>
/// Marks a property as a component parameter.
/// Parameters can be set by parent components.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class ParameterAttribute : Attribute
{
}

From CascadingParameterAttribute.cs:1-16:

/// <summary>
/// Marks a property as a cascading parameter.
/// Cascading parameters flow down the component tree.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class CascadingParameterAttribute : Attribute
{
    /// <summary>
    /// Optional name for this cascading parameter.
    /// </summary>
    public string? Name { get; set; }
}

Source Generator

The source generator compiles .cshtml files to C# partial classes.

From RazorConsoleSourceGenerator.cs:14-39:

/// <summary>
/// Source generator that compiles .cshtml files to Spectre.Console rendering code.
/// </summary>
[Generator(LanguageNames.CSharp)]
public sealed class RazorConsoleSourceGenerator : IIncrementalGenerator
{
    /// <summary>
    /// Initializes the source generator to find and compile Razor files.
    /// </summary>
    /// <param name="context">The incremental generator initialization context.</param>
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // Find all .cshtml files
        var razorFiles = context.AdditionalTextsProvider
            .Where(static file => file.Path.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase));

        // Compile each Razor file
        context.RegisterSourceOutput(razorFiles, static (context, file) =>
        {
            var result = RazorCompiler.Compile(file);

            if (result != null)
            {
                var fileName = System.IO.Path.GetFileNameWithoutExtension(file.Path);
                context.AddSource($"{fileName}.g.cs", SourceText.From(result, Encoding.UTF8));
            }
        });
    }
}

TUI Component Base

RazorConsoleComponent inherits from TUIComponent, which provides Spectre.Console integration.

From TUIComponent.cs:16-72:

/// <summary>
/// CRTP base for Terminal UI components using Spectre.Console.
/// Provides interactive prompting and rich console rendering.
/// </summary>
/// <typeparam name="TSelf">The derived TUI component type (CRTP)</typeparam>
/// <typeparam name="TModel">The model type being rendered</typeparam>
public abstract class TUIComponent<TSelf, TModel> : ComponentBase<TSelf, TModel>
    where TSelf : TUIComponent<TSelf, TModel>
{
    /// <summary>
    /// Theme configuration for this component.
    /// </summary>
    public TUIThemeConfiguration? Theme { get; set; }

    /// <summary>
    /// Prompts the user to enter/edit the model value interactively.
    /// </summary>
    /// <param name="console">The console to prompt on</param>
    /// <returns>The entered/edited model value</returns>
    public abstract Task<TModel?> Prompt(IAnsiConsole console);

    /// <summary>
    /// Renders the current model value to the console (read-only display).
    /// </summary>
    /// <param name="console">The console to render to</param>
    public abstract void Render(IAnsiConsole console);

    /// <summary>
    /// Gets display text for this component (for use in lists/tables).
    /// </summary>
    public virtual string GetDisplayText()
    {
        return Value?.ToString() ?? "[dim]null[/]";
    }

    /// <summary>
    /// Renders a section header with the component name.
    /// </summary>
    protected virtual void RenderHeader(IAnsiConsole console, string? title = null)
    {
        var headerTitle = title ?? typeof(TModel).Name;
        var color = Theme?.Colors.Primary ?? Color.Yellow;

        console.Write(new Rule($"[{color}]{headerTitle}[/]").LeftJustified());
    }

    /// <summary>
    /// Prompts for a TypeCollection selection.
    /// </summary>
    protected virtual int PromptTypeCollectionId<TCollection, TOption>(
        IAnsiConsole console,
        string promptText,
        int currentId = 0)
        where TCollection : class
        where TOption : class, ITypeOption
    {
        return Prompts.TypeCollectionPromptHelper.Prompt<TCollection, TOption>(
            console,
            promptText,
            currentId,
            Theme);
    }
}

Theme Configuration

From TUIThemeConfiguration.cs:1-80:

/// <summary>
/// Theme configuration for Terminal UI components.
/// </summary>
public class TUIThemeConfiguration
{
    private IMenuTheme _theme;

    /// <summary>
    /// Initializes a new instance with the default dark theme.
    /// </summary>
    public TUIThemeConfiguration()
    {
        _theme = MenuThemes.ByName("Dark");
    }

    /// <summary>
    /// Initializes a new instance with the specified theme.
    /// </summary>
    /// <param name="theme">The menu theme to use.</param>
    public TUIThemeConfiguration(IMenuTheme theme)
    {
        _theme = theme;
    }

    /// <summary>
    /// Gets or sets the theme by Id.
    /// </summary>
    public int ThemeId
    {
        get => _theme.Id;
        set => _theme = MenuThemes.ById(value);
    }

    /// <summary>
    /// Gets or sets the theme by name.
    /// </summary>
    public string ThemeName
    {
        get => _theme.Name;
        set => _theme = MenuThemes.ByName(value);
    }

    /// <summary>
    /// Gets the current theme.
    /// </summary>
    public IMenuTheme Theme => _theme;

    /// <summary>
    /// Gets the color palette from the current theme.
    /// </summary>
    public IColorPalette Colors => _theme.Colors;

    /// <summary>
    /// Gets the border style from the current theme.
    /// </summary>
    public IBorderStyle Borders => _theme.Borders;

    /// <summary>
    /// Gets the icon set from the current theme.
    /// </summary>
    public IIconSet Icons => _theme.Icons;

    /// <summary>
    /// Gets or sets whether to use color in output.
    /// </summary>
    public bool UseColor { get; set; } = true;

    /// <summary>
    /// Gets or sets whether to use emoji/icons in output.
    /// </summary>
    public bool UseEmoji { get; set; } = true;
}

Validation Support

The UI.Abstractions package provides validation result types that can be used with RazorConsole components.

From ValidationResult.cs:9-88:

/// <summary>
/// Represents the result of a component validation.
/// </summary>
public sealed class ValidationResult
{
    private readonly List<ValidationMessage> _messages;

    private ValidationResult(bool isValid, IEnumerable<ValidationMessage>? messages = null)
    {
        IsValid = isValid;
        _messages = messages?.ToList() ?? [];
    }

    /// <summary>
    /// Gets a value indicating whether the validation passed.
    /// </summary>
    public bool IsValid { get; }

    /// <summary>
    /// Gets the validation messages.
    /// </summary>
    public IReadOnlyList<ValidationMessage> Messages => _messages.AsReadOnly();

    /// <summary>
    /// Gets the first error message, if any.
    /// </summary>
    public string? FirstError => _messages
        .FirstOrDefault(m => m.Severity == ValidationSeverity.Error)?.Message;

    /// <summary>
    /// Creates a successful validation result.
    /// </summary>
    public static ValidationResult Success() => new(true);

    /// <summary>
    /// Creates a failed validation result with an error message.
    /// </summary>
    public static ValidationResult Error(string error)
        => new(false, [new ValidationMessage(error, ValidationSeverity.Error)]);

    /// <summary>
    /// Combines multiple validation results.
    /// </summary>
    public static ValidationResult Combine(params ValidationResult[] results)
    {
        var allMessages = results.SelectMany(r => r.Messages).ToList();
        var isValid = results.All(r => r.IsValid);
        return new ValidationResult(isValid, allMessages);
    }
}

Current Limitations

The source generator is in early development. Current implementation:

  • Parses .cshtml files using Microsoft.AspNetCore.Razor.Language
  • Generates partial class stubs with abstract method implementations
  • Placeholder code generation for Prompt and Render methods

Planned features (not yet implemented):

  • Custom component tags (<Panel>, <Property>, <Collection>, etc.)
  • Property binding with @bind-Value
  • Conditional rendering
  • Loop support
  • Component composition

Best Practices

  1. Keep components focused on a single responsibility
  2. Use [Parameter] attribute for properties set by parent components
  3. Use [CascadingParameter] for values that flow down the component tree
  4. Override lifecycle methods (OnInitialized, OnParametersSet) for initialization logic
  5. Inject TUIThemeConfiguration via cascading parameter for consistent theming

See Also

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

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-preview.15 0 1/15/2026
0.1.0-preview.11 38 1/12/2026
0.1.0-preview.10 36 1/12/2026
0.1.0-preview.9 39 1/9/2026
0.1.0-preview.7 103 1/7/2026