FractalDataWorks.UI.Components.TUI
0.1.0-preview.11
dotnet add package FractalDataWorks.UI.Components.TUI --version 0.1.0-preview.11
NuGet\Install-Package FractalDataWorks.UI.Components.TUI -Version 0.1.0-preview.11
<PackageReference Include="FractalDataWorks.UI.Components.TUI" Version="0.1.0-preview.11" />
<PackageVersion Include="FractalDataWorks.UI.Components.TUI" Version="0.1.0-preview.11" />
<PackageReference Include="FractalDataWorks.UI.Components.TUI" />
paket add FractalDataWorks.UI.Components.TUI --version 0.1.0-preview.11
#r "nuget: FractalDataWorks.UI.Components.TUI, 0.1.0-preview.11"
#:package FractalDataWorks.UI.Components.TUI@0.1.0-preview.11
#addin nuget:?package=FractalDataWorks.UI.Components.TUI&version=0.1.0-preview.11&prerelease
#tool nuget:?package=FractalDataWorks.UI.Components.TUI&version=0.1.0-preview.11&prerelease
FractalDataWorks.UI.Components.TUI
Terminal User Interface (TUI) implementation of the FractalDataWorks UI component system using Spectre.Console.
Overview
This package implements a Terminal UI layer for FractalDataWorks components. It inherits from ComponentBase<TSelf, TModel> and renders to the console using Spectre.Console's rich rendering capabilities including prompts, selections, tables, trees, and progress indicators.
Key Features:
- Spectre.Console integration with colors, styles, and layouts
- Interactive prompts for all data types
- Single and multi-select for TypeCollections and collections
- Table and tree view rendering
- Theming via TypeCollections (IMenuTheme, IColorPalette, IBorderStyle)
Target Framework: net10.0
Dependencies:
- FractalDataWorks.UI.Abstractions
- FractalDataWorks.UI.Themes
- FractalDataWorks.Collections
- Spectre.Console
Architecture
┌─────────────────────────────────────────────┐
│ FractalDataWorks.UI.Abstractions │
│ - ComponentBase<TSelf, TModel> │
│ - PropertyComponent<TSelf, TProperty> │
│ - CollectionComponent │
│ - ZERO UI framework dependencies │
└─────────────────────────────────────────────┘
▲
│ inherits
┌─────────────────────────────────────────────┐
│ FractalDataWorks.UI.Components.TUI │ ← THIS PACKAGE
│ - TUIComponent<TSelf, TModel> │
│ - Spectre.Console rendering │
│ - Interactive prompts │
│ - Table/Tree displays │
│ - TypeCollection selectors │
└─────────────────────────────────────────────┘
Core Components
TUIComponent<TSelf, TModel>
Foundation for all terminal UI components. Inherits from ComponentBase and adds Spectre.Console rendering.
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);
}
}
TUI-Specific Features:
Prompt()- Interactive data entry (abstract, implemented by derived classes)Render()- Display-only rendering (abstract, implemented by derived classes)RenderHeader()- Renders a styled section headerPromptTypeCollectionId()- Helper for TypeCollection selection
TUIPropertyComponent<TSelf, TProperty>
Base for property-level TUI components with type-specific prompts.
From TUIPropertyComponent.cs:12-48:
/// <summary>
/// CRTP base for Terminal UI property-level components.
/// </summary>
/// <typeparam name="TSelf">The derived property component type (CRTP)</typeparam>
/// <typeparam name="TProperty">The property value type</typeparam>
public abstract class TUIPropertyComponent<TSelf, TProperty> : PropertyComponent<TSelf, TProperty>
where TSelf : TUIPropertyComponent<TSelf, TProperty>
{
/// <summary>
/// Theme configuration.
/// </summary>
public TUIThemeConfiguration? Theme { get; set; }
/// <summary>
/// Prompts the user to enter a value for this property.
/// </summary>
public abstract TProperty? PromptValue(IAnsiConsole console);
/// <summary>
/// Renders this property value to the console.
/// </summary>
public abstract void RenderValue(IAnsiConsole console);
/// <summary>
/// Gets the prompt text for this property.
/// </summary>
protected virtual string GetPromptText()
{
return Metadata?.Label ?? typeof(TProperty).Name;
}
/// <summary>
/// Displays help text if available.
/// </summary>
protected virtual void RenderHelpText(IAnsiConsole console)
{
if (!string.IsNullOrEmpty(Metadata?.HelpText))
{
console.MarkupLine($"[dim]{Metadata.HelpText}[/]");
}
}
}
Prompt Helpers
TextPromptHelper
Helper for creating text prompts with validation.
From Prompts/TextPromptHelper.cs:21-72:
public static string Prompt(
IAnsiConsole console,
string promptText,
string? defaultValue = null,
PropertyMetadata? metadata = null,
TUIThemeConfiguration? theme = null)
{
var prompt = new TextPrompt<string>(promptText);
if (!string.IsNullOrEmpty(defaultValue))
{
prompt.DefaultValue(defaultValue);
}
if (metadata?.Required == true)
{
prompt.AllowEmpty = false;
prompt.ValidationErrorMessage($"[{theme?.Colors.Error ?? Color.Red}]{promptText} is required[/]");
}
if (metadata?.ValidationRules.TryGetValue("maxLength", out var maxLengthObj) == true
&& maxLengthObj is int maxLength)
{
prompt.Validate(value =>
{
if (value.Length > maxLength)
{
return ValidationResult.Error($"[{theme?.Colors.Error ?? Color.Red}]Maximum length is {maxLength}[/]");
}
return ValidationResult.Success();
});
}
return console.Prompt(prompt);
}
NumericPromptHelper
Helper for creating numeric prompts with range validation.
From Prompts/NumericPromptHelper.cs:22-65:
public static T Prompt<T>(
IAnsiConsole console,
string promptText,
T? defaultValue = default,
PropertyMetadata? metadata = null,
TUIThemeConfiguration? theme = null)
where T : struct, IComparable<T>
{
var prompt = new TextPrompt<T>(promptText);
if (defaultValue != null && !defaultValue.Equals(default(T)))
{
prompt.DefaultValue(defaultValue.Value);
}
// Range validation
if (metadata?.ValidationRules.TryGetValue("min", out var minObj) == true)
{
var min = (T)Convert.ChangeType(minObj, typeof(T), CultureInfo.InvariantCulture);
prompt.Validate(value =>
{
if (value.CompareTo(min) < 0)
{
return ValidationResult.Error($"[{theme?.Colors.Error ?? Color.Red}]Value must be at least {min}[/]");
}
return ValidationResult.Success();
});
}
return console.Prompt(prompt);
}
ConfirmationPromptHelper
Helper for creating Yes/No confirmation prompts.
From Prompts/ConfirmationPromptHelper.cs:17-25:
public static bool Prompt(
IAnsiConsole console,
string promptText,
bool defaultValue = false,
TUIThemeConfiguration? theme = null)
{
return console.Confirm(promptText, defaultValue);
}
TypeCollectionPromptHelper
Helper for prompting TypeCollection selections.
From Prompts/TypeCollectionPromptHelper.cs:24-59:
public static int Prompt<TCollection, TOption>(
IAnsiConsole console,
string promptText,
int currentId = 0,
TUIThemeConfiguration? theme = null)
where TCollection : class
where TOption : class, ITypeOption
{
// Get TypeCollection instance via reflection
var instanceProperty = typeof(TCollection).GetProperty("Instance",
BindingFlags.Public | BindingFlags.Static);
var instance = instanceProperty?.GetValue(null);
if (instance == null)
{
throw new InvalidOperationException($"Cannot access {typeof(TCollection).Name}.Instance");
}
// Get All() method
var allMethod = typeof(TCollection).GetMethod("All");
var options = (allMethod?.Invoke(instance, null) as IEnumerable<TOption>)?.ToList();
if (options == null || options.Count == 0)
{
console.MarkupLine($"[{theme?.Colors.Warning ?? Color.Yellow}]No options available[/]");
return 0;
}
var selection = console.Prompt(
new SelectionPrompt<TOption>()
.Title(promptText)
.AddChoices(options)
.UseConverter(opt => $"{opt.Name} [dim]({opt.Id})[/]"));
return (int)selection.Id;
}
CollectionPromptHelper
Helper for interactive collection management with add/view/edit/remove.
From Prompts/CollectionPromptHelper.cs:27-79:
public static async Task<List<T>> PromptCollection<T, TComponent>(
IAnsiConsole console,
List<T>? items,
Func<T, int, TComponent> createComponent,
Func<T> createNewItem,
TUIThemeConfiguration? theme = null)
where TComponent : TUIComponent<TComponent, T>
{
items ??= [];
while (true)
{
console.Clear();
RenderCollectionTable(console, items, createComponent, theme);
List<string> choices = [];
if (items.Count > 0)
{
choices.Add("View Item");
choices.Add("Edit Item");
choices.Add("Remove Item");
}
choices.Add("Add Item");
choices.Add("Done");
var action = console.Prompt(
new SelectionPrompt<string>()
.Title("[bold]Collection Actions:[/]")
.AddChoices(choices));
switch (action)
{
case "View Item":
await ViewItem(console, items, createComponent).ConfigureAwait(false);
break;
case "Edit Item":
await EditItem(console, items, createComponent).ConfigureAwait(false);
break;
case "Remove Item":
RemoveItem(console, items);
break;
case "Add Item":
await AddItem(console, items, createNewItem, createComponent).ConfigureAwait(false);
break;
case "Done":
return items;
}
console.WriteLine();
console.Write("Press any key to continue...");
Console.ReadKey(true);
}
}
Renderers
TableRenderer
Renders collections as formatted tables.
From Renderers/TableRenderer.cs:22-53:
public static void RenderTable<T>(
IAnsiConsole console,
IEnumerable<T> items,
Dictionary<string, Func<T, string>> columns,
TUIThemeConfiguration? theme = null)
{
var border = theme?.Borders.Table ?? TableBorder.Rounded;
var borderColor = theme?.Colors.Primary ?? Color.Blue;
var table = new Table()
.Border(border)
.BorderColor(borderColor);
// Add columns
foreach (var columnName in columns.Keys)
{
table.AddColumn($"[bold]{columnName}[/]");
}
// Add rows
foreach (var item in items)
{
List<string> cellValues = [];
foreach (var columnFunc in columns.Values)
{
cellValues.Add(columnFunc(item));
}
table.AddRow(cellValues.ToArray());
}
console.Write(table);
}
TreeRenderer
Renders hierarchical data as trees.
From Renderers/TreeRenderer.cs:18-27:
public static void RenderTree(
IAnsiConsole console,
string rootLabel,
Action<IHasTreeNodes> buildTree,
TUIThemeConfiguration? theme = null)
{
var tree = new Tree(rootLabel);
buildTree(tree);
console.Write(tree);
}
PanelRenderer
Renders content in bordered panels.
From Renderers/PanelRenderer.cs:17-36:
public static void RenderPanel(
IAnsiConsole console,
string content,
string? title = null,
TUIThemeConfiguration? theme = null)
{
var border = theme?.Borders.Panel ?? BoxBorder.Rounded;
var borderColor = theme?.Colors.Primary ?? Color.Blue;
var panel = new Panel(new Markup(content))
.Border(border)
.BorderColor(borderColor);
if (!string.IsNullOrEmpty(title))
{
panel.Header(title);
}
console.Write(panel);
}
Theme System
Theme configuration wraps IMenuTheme from the UI.Themes package, providing access to colors, borders, and icons as TypeCollections.
From Theme/TUIThemeConfiguration.cs:12-80:
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>
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;
}
Theme Properties:
The theme provides access to:
Colors- IColorPalette with Primary, Secondary, Success, Warning, Error, Info, etc.Borders- IBorderStyle with Panel, Table, Input, Menu, Dialog bordersIcons- IIconSet for UI icons
Themes are TypeCollections from FractalDataWorks.UI.Themes and can be selected by name or ID.
Usage
Using Prompt Helpers
using FractalDataWorks.UI.Components.TUI;
using FractalDataWorks.UI.Components.TUI.Prompts;
using Spectre.Console;
var console = AnsiConsole.Console;
var theme = new TUIThemeConfiguration(); // Uses default "Dark" theme
// Text prompt with validation
var name = TextPromptHelper.Prompt(
console,
"Enter name:",
defaultValue: "Default",
metadata: new PropertyMetadata { Required = true });
// Numeric prompt
var port = NumericPromptHelper.Prompt<int>(
console,
"Port:",
defaultValue: 8080);
// Confirmation
var confirm = ConfirmationPromptHelper.Prompt(
console,
"Continue?",
defaultValue: true);
Using Renderers
using FractalDataWorks.UI.Components.TUI.Renderers;
using Spectre.Console;
var console = AnsiConsole.Console;
var theme = new TUIThemeConfiguration();
// Render a table
var items = new[] { ("Item1", 10), ("Item2", 20) };
TableRenderer.RenderTable(
console,
items,
new Dictionary<string, Func<(string, int), string>>
{
["Name"] = x => x.Item1,
["Value"] = x => x.Item2.ToString()
},
theme);
// Render a tree
TreeRenderer.RenderTree(
console,
"[yellow]Configuration[/]",
tree =>
{
var node = tree.AddNode("Settings");
node.AddNode("Option 1");
node.AddNode("Option 2");
},
theme);
// Render a panel
PanelRenderer.RenderPanel(
console,
"[bold]Name:[/] Example\n[bold]Status:[/] Active",
title: "Details",
theme);
Interactive Collection Management
using FractalDataWorks.UI.Components.TUI.Prompts;
using Spectre.Console;
var console = AnsiConsole.Console;
// Manage a collection interactively
var items = await CollectionPromptHelper.PromptCollection<MyItem, MyItemComponent>(
console,
items: existingItems,
createComponent: (item, index) => new MyItemComponent { Value = item },
createNewItem: () => new MyItem());
Best Practices
- Escape user input - Use
Markup.Escape()to prevent markup injection - Provide defaults - All prompts should have sensible default values
- Use theme configuration - Pass
TUIThemeConfigurationto helpers for consistent styling - Validate immediately - Use
.Validate()on prompts for real-time feedback - Handle cancellation - Catch
OperationCanceledExceptionfor Ctrl+C
See Also
- UI.Abstractions - CRTP base classes (ComponentBase, PropertyComponent, CollectionComponent)
- UI.Themes - Theme TypeCollections (IMenuTheme, IColorPalette, IBorderStyle)
- Spectre.Console Documentation - Official Spectre.Console docs
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- FractalDataWorks.Collections (>= 0.1.0-preview.11)
- FractalDataWorks.UI.Abstractions (>= 0.1.0-preview.11)
- FractalDataWorks.UI.Themes (>= 0.1.0-preview.11)
- Spectre.Console (>= 0.54.1-alpha.0.7)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on FractalDataWorks.UI.Components.TUI:
| Package | Downloads |
|---|---|
|
FractalDataWorks.UI.Components.RazorConsole
Development tools and utilities for the FractalDataWorks ecosystem. Build: |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0-preview.11 | 36 | 1/12/2026 |
| 0.1.0-preview.10 | 35 | 1/12/2026 |
| 0.1.0-preview.9 | 44 | 1/9/2026 |
| 0.1.0-preview.7 | 100 | 1/7/2026 |