Releases: Aaronontheweb/termina
Termina 0.7.2
0.7.2 March 1st 2026
New Features:
- TextAreaNode multi-line text input (#161, #163)
- New
TextAreaNodecomponent for multi-line text input with line-by-line editing - Extracted common base class
TextInputBaseNodeshared with single-lineTextInputNode - Alt+Enter for universal newline insertion (works on all terminals, unlike Ctrl+Enter)
- Kitty keyboard protocol support for Ctrl+Enter detection when available (#165)
- Proper cursor positioning after multiple consecutive newlines
- Full input clearing on submit with committed segments model
- New
Bug Fixes:
-
Improved TextInputNode paste behavior (#162)
- Enhanced multi-line paste handling with committed segments model
- Paste summaries display character count and line count for better UX
- Full pasted content preserved and submitted as single unit
-
TextAreaNode hardening (#165)
- Fixed cursor jump after consecutive newlines in TextAreaNode
- Corrected input clearing on submit to clear entire buffer, not just committed segments
- Improved stability and consistency of multi-line input behavior
Dependencies:
- Bumped Akka.Hosting from 1.5.60 to 1.5.61 (#160)
0.7.1 February 26th 2026
Bug Fixes:
-
DynamicLayoutNode is now invalidation-driven (#155)
- Factory no longer runs on every Measure/Render cycle — runs once, then only on
Invalidate() - Fixes silent state destruction (highlights, typed text, focus) when factory creates new instances
Invalidate()now eagerly evaluates the factory for immediate child swapOnActivate()marks the node dirty so factory re-evaluates after navigation round-trip
- Factory no longer runs on every Measure/Render cycle — runs once, then only on
-
WizardNode sub-step focus propagation (#156)
PropagateFocusToContent()now called on sub-step changes, not just step changes- Fixes stale focus when navigating between sub-steps with different focusable content
New Features:
- KeyedDynamicLayoutNode<TKey> — pit-of-success API for key-based content switching
Layouts.KeyedDynamic<TKey>(keySelector, contentFactory)factory method- Caches content by key — navigating back reuses cached instances, preserving child state
- WizardNode now uses this internally (replaces manual step content cache)
Upgrade Advisory:
DynamicLayoutNodeno longer evaluates its factory per-frame. If you relied on per-frame evaluation, addInvalidate()calls when state changes. TheAsDynamicLayout(trigger)extension already callsInvalidate()on each trigger emission and works unchanged.- For content that switches based on a key (enum, step index, tab), prefer
Layouts.KeyedDynamic<TKey>()overLayouts.Dynamic()for automatic caching and state preservation.
0.7.0 February 26th 2026
Breaking Changes:
-
Migrate from System.Reactive to R3 (#140)
- Replaced System.Reactive (Rx.NET) with R3 throughout the framework
IObservable<T>→Observable<T>in all public APIsOfType<T>()→OfType<TSrc, TDest>()(R3 requires both type parameters)Select(...)may require explicit type parameters:Select<TIn, TOut>(...)Subscribecallbacks:onError→onErrorResume,onCompletedtakesResultparameterBehaviorSubject<T>removed — useReactiveProperty<T>insteadSubject<T>now from R3 namespace- See Migration Guide for details
-
Replace
[Reactive]source generator withReactiveProperty<T>(#149)- Removed
[Reactive]attribute and its source generator - Use
ReactiveProperty<T>for all ViewModel state:public ReactiveProperty<int> Count { get; } = new(0); - Property access via
.Value:Count.Value++instead ofCount++ - Page bindings subscribe directly to property:
ViewModel.Count.Select(...)instead ofViewModel.CountChanged.Select(...) - ViewModels no longer need
partialkeyword (unless using[FromRoute]) - Manual
Dispose()override required to dispose allReactiveProperty<T>instances - Built-in
DistinctUntilChanged— only emits when value actually changes
- Removed
-
Replace timers with
Observable.Interval+TimeProvider(#140)- All timer-based components now accept optional
TimeProviderparameter - Enables deterministic testing with
FakeTimeProvider System.Timers.TimerandSystem.Threading.Timerno longer used
- All timer-based components now accept optional
New Features:
-
DynamicLayoutNode (#147)
- New
Layouts.Dynamic(Func<ILayoutNode> factory)for imperative, page-local state - Evaluates factory once, then only on
Invalidate()with reference equality to avoid lifecycle churn Invalidate()method for programmatic trigger;AsDynamicLayout(Observable<Unit>)extension- Implements
IInvalidatingNodefor proper invalidation propagation
- New
-
FocusManager auto-focus and Tab cycling (#146)
FocusPolicyenum:Manual,FirstFocusable,ByPriority— set onReactivePagefor automatic focus on navigationCollectFocusables(ILayoutNode root)— depth-first tree walk collecting focusable nodesCycleFocus(focusables, reverse)— next/previous with wrap-aroundCycleFocusForward()/CycleFocusBackward()helpers onReactivePageFocusManagermigrated fromBehaviorSubject<IFocusable?>toReactiveProperty<IFocusable?>
-
WizardNode (#148)
- Multi-step wizard component:
Layouts.Wizard<TStep>()whereTStep : struct, Enum - Fluent builder:
.WithStep(),.WithProgressStyle(),.WithTitle(),.WithBorder() - Navigation:
TryAdvance(),TryGoBack(),GoToStep(),AdvanceSubStep() - Cancellable
BeforeAdvanceobservable for step validation StepChangedandCompletedobservables- Progress styles:
BlockBar,Arrow,Dots,None - Sub-step support within each step
- Implements
IFocusableandIInvalidatingNode - Uses
KeyedDynamicLayoutNode<int>internally for step content switching with automatic caching
- Multi-step wizard component:
Bug Fixes:
- Fix invalidation subscription lost after navigation round-trip (#150)
- Fixed rendering bug where leaf nodes lost their invalidation subscriptions after navigating away and back
- Custom
LayoutNodesubclasses should useCreateSubContext(bounds)in Render methods
Cleanup:
- Removed dead
HasReactiveAttributehelper fromLayoutNodeInViewModelAnalyzer(references removed[Reactive]attribute)
0.6.1 February 25th 2026
New Features:
-
Built-in input history for TextInputNode (#144)
- New
WithHistory(maxEntries)fluent API for enabling input history - New
AddHistory(text)method for programmatically adding history entries - Up/Down arrow keys navigate through previous input submissions
- Enter key automatically records non-empty text for future recall
- Off by default for full backward compatibility
- Move history logic from
StreamingChatViewModelintoTextInputNodefor better reusability - Simplified
StreamingChatPageby removing manual history wiring - Includes 17 new comprehensive tests covering navigation, eviction, and restoration
- Updated text-input-node.md documentation
- New
-
Auto-route PasteEvent to focused IPasteReceiver via layout tree walk (#143)
- Automatic paste event routing when no focused
IPasteReceiverexists - New
GetChildNodes()virtual method onLayoutNodefor tree traversal - Implemented in
ContainerNode,PanelNode,ModalNode,ScrollableContainerNode,ReactiveLayoutNode, andConditionalNode - Eliminates manual paste subscription boilerplate from pages
- Paste routing order: focused receiver > tree walk > ViewModel.Input
- Multi-line paste content in history recall shows as summary (e.g.,
[Pasted 3 lines, 20 chars]) TextInputNode.Textsetter automatically condenses multi-line content into paste-display mode- Includes comprehensive routing tests covering all scenarios
- Automatic paste event routing when no focused
0.6.0 February 23rd 2026
New Features:
-
Scrollbar for StreamingTextNode (#138)
- Visual scrollbar renders on the right edge of
StreamingTextNodewhen content exceeds the viewport ScrollbarOptionsrecord for customizing track/thumb characters and colors, withAutoHidesupportWithScrollbar()fluent method onStreamingTextNodefor opt-in configurationGetMaxScrollOffset()exposed onPersistedStreamBufferfor programmatic scroll position access- Closes #135
- Visual scrollbar renders on the right edge of
-
Bracketed paste mode for TextInputNode (#138)
TextInputNodenow implementsIPasteReceiverand handles terminal bracketed paste sequences- Multi-line pastes are captured as a single unit and displayed as a summary placeholder (e.g.,
[Pasted 500 lines, 12345 chars]) - Full pasted content (with newlines preserved) is submitted on Enter
- Any edit action clears the paste and returns to normal input mode
- Prevents multi-line pastes from triggeri...
Termina 0.7.1
0.7.1 February 26th 2026
Bug Fixes:
-
DynamicLayoutNode is now invalidation-driven (#155)
- Factory no longer runs on every Measure/Render cycle — runs once, then only on
Invalidate() - Fixes silent state destruction (highlights, typed text, focus) when factory creates new instances
Invalidate()now eagerly evaluates the factory for immediate child swapOnActivate()marks the node dirty so factory re-evaluates after navigation round-trip
- Factory no longer runs on every Measure/Render cycle — runs once, then only on
-
WizardNode sub-step focus propagation (#156)
PropagateFocusToContent()now called on sub-step changes, not just step changes- Fixes stale focus when navigating between sub-steps with different focusable content
New Features:
- KeyedDynamicLayoutNode<TKey> — pit-of-success API for key-based content switching
Layouts.KeyedDynamic<TKey>(keySelector, contentFactory)factory method- Caches content by key — navigating back reuses cached instances, preserving child state
- WizardNode now uses this internally (replaces manual step content cache)
Upgrade Advisory:
DynamicLayoutNodeno longer evaluates its factory per-frame. If you relied on per-frame evaluation, addInvalidate()calls when state changes. TheAsDynamicLayout(trigger)extension already callsInvalidate()on each trigger emission and works unchanged.- For content that switches based on a key (enum, step index, tab), prefer
Layouts.KeyedDynamic<TKey>()overLayouts.Dynamic()for automatic caching and state preservation.
0.7.0 February 26th 2026
Breaking Changes:
-
Migrate from System.Reactive to R3 (#140)
- Replaced System.Reactive (Rx.NET) with R3 throughout the framework
IObservable<T>→Observable<T>in all public APIsOfType<T>()→OfType<TSrc, TDest>()(R3 requires both type parameters)Select(...)may require explicit type parameters:Select<TIn, TOut>(...)Subscribecallbacks:onError→onErrorResume,onCompletedtakesResultparameterBehaviorSubject<T>removed — useReactiveProperty<T>insteadSubject<T>now from R3 namespace- See Migration Guide for details
-
Replace
[Reactive]source generator withReactiveProperty<T>(#149)- Removed
[Reactive]attribute and its source generator - Use
ReactiveProperty<T>for all ViewModel state:public ReactiveProperty<int> Count { get; } = new(0); - Property access via
.Value:Count.Value++instead ofCount++ - Page bindings subscribe directly to property:
ViewModel.Count.Select(...)instead ofViewModel.CountChanged.Select(...) - ViewModels no longer need
partialkeyword (unless using[FromRoute]) - Manual
Dispose()override required to dispose allReactiveProperty<T>instances - Built-in
DistinctUntilChanged— only emits when value actually changes
- Removed
-
Replace timers with
Observable.Interval+TimeProvider(#140)- All timer-based components now accept optional
TimeProviderparameter - Enables deterministic testing with
FakeTimeProvider System.Timers.TimerandSystem.Threading.Timerno longer used
- All timer-based components now accept optional
New Features:
-
DynamicLayoutNode (#147)
- New
Layouts.Dynamic(Func<ILayoutNode> factory)for imperative, page-local state - Evaluates factory once, then only on
Invalidate()with reference equality to avoid lifecycle churn Invalidate()method for programmatic trigger;AsDynamicLayout(Observable<Unit>)extension- Implements
IInvalidatingNodefor proper invalidation propagation
- New
-
FocusManager auto-focus and Tab cycling (#146)
FocusPolicyenum:Manual,FirstFocusable,ByPriority— set onReactivePagefor automatic focus on navigationCollectFocusables(ILayoutNode root)— depth-first tree walk collecting focusable nodesCycleFocus(focusables, reverse)— next/previous with wrap-aroundCycleFocusForward()/CycleFocusBackward()helpers onReactivePageFocusManagermigrated fromBehaviorSubject<IFocusable?>toReactiveProperty<IFocusable?>
-
WizardNode (#148)
- Multi-step wizard component:
Layouts.Wizard<TStep>()whereTStep : struct, Enum - Fluent builder:
.WithStep(),.WithProgressStyle(),.WithTitle(),.WithBorder() - Navigation:
TryAdvance(),TryGoBack(),GoToStep(),AdvanceSubStep() - Cancellable
BeforeAdvanceobservable for step validation StepChangedandCompletedobservables- Progress styles:
BlockBar,Arrow,Dots,None - Sub-step support within each step
- Implements
IFocusableandIInvalidatingNode - Uses
KeyedDynamicLayoutNode<int>internally for step content switching with automatic caching
- Multi-step wizard component:
Bug Fixes:
- Fix invalidation subscription lost after navigation round-trip (#150)
- Fixed rendering bug where leaf nodes lost their invalidation subscriptions after navigating away and back
- Custom
LayoutNodesubclasses should useCreateSubContext(bounds)in Render methods
Cleanup:
- Removed dead
HasReactiveAttributehelper fromLayoutNodeInViewModelAnalyzer(references removed[Reactive]attribute)
0.6.1 February 25th 2026
New Features:
-
Built-in input history for TextInputNode (#144)
- New
WithHistory(maxEntries)fluent API for enabling input history - New
AddHistory(text)method for programmatically adding history entries - Up/Down arrow keys navigate through previous input submissions
- Enter key automatically records non-empty text for future recall
- Off by default for full backward compatibility
- Move history logic from
StreamingChatViewModelintoTextInputNodefor better reusability - Simplified
StreamingChatPageby removing manual history wiring - Includes 17 new comprehensive tests covering navigation, eviction, and restoration
- Updated text-input-node.md documentation
- New
-
Auto-route PasteEvent to focused IPasteReceiver via layout tree walk (#143)
- Automatic paste event routing when no focused
IPasteReceiverexists - New
GetChildNodes()virtual method onLayoutNodefor tree traversal - Implemented in
ContainerNode,PanelNode,ModalNode,ScrollableContainerNode,ReactiveLayoutNode, andConditionalNode - Eliminates manual paste subscription boilerplate from pages
- Paste routing order: focused receiver > tree walk > ViewModel.Input
- Multi-line paste content in history recall shows as summary (e.g.,
[Pasted 3 lines, 20 chars]) TextInputNode.Textsetter automatically condenses multi-line content into paste-display mode- Includes comprehensive routing tests covering all scenarios
- Automatic paste event routing when no focused
0.6.0 February 23rd 2026
New Features:
-
Scrollbar for StreamingTextNode (#138)
- Visual scrollbar renders on the right edge of
StreamingTextNodewhen content exceeds the viewport ScrollbarOptionsrecord for customizing track/thumb characters and colors, withAutoHidesupportWithScrollbar()fluent method onStreamingTextNodefor opt-in configurationGetMaxScrollOffset()exposed onPersistedStreamBufferfor programmatic scroll position access- Closes #135
- Visual scrollbar renders on the right edge of
-
Bracketed paste mode for TextInputNode (#138)
TextInputNodenow implementsIPasteReceiverand handles terminal bracketed paste sequences- Multi-line pastes are captured as a single unit and displayed as a summary placeholder (e.g.,
[Pasted 500 lines, 12345 chars]) - Full pasted content (with newlines preserved) is submitted on Enter
- Any edit action clears the paste and returns to normal input mode
- Prevents multi-line pastes from triggering individual per-line submissions
AnsiCodes.EnableBracketedPaste/DisableBracketedPasteANSI escape constants added- Closes #134
-
Mouse wheel scrolling for scrollable components (#138)
- New
MouseScrollEventinput event for mouse wheel up/down - New
IScrollableinterface for components that support scroll input - Mouse scroll events are automatically routed to the currently focused
IScrollablecomponent EscapeSequenceParserdetects SGR mouse scroll sequences- Closes #136
- New
0.5.1 December 19th 2025
Bug Fixes:
- Fix nullability propagation in [Reactive] source generator (#112)
- Source generator now preserves nullable reference type annotations (e.g.,
string?,int?) in generated properties - Added custom
SymbolDisplayFormatwithIncludeNullableReferenceTypeModifieroption - Nullable fields now generate properties with matching nullability annotations, providing correct nullability hints in consuming code
- Previously, nullable fields like
string? _fieldwould generate non-nullable properties, losing nullability information
- Source generator now preserves nullable reference type annotations (e.g.,
0.5.0 December 18th 2025
New Features:
- BlockSegment wrapper for block-level streaming text (#108)
...
Termina 0.7.0
0.7.0 February 26th 2026
Breaking Changes:
-
Migrate from System.Reactive to R3 (#140)
- Replaced System.Reactive (Rx.NET) with R3 throughout the framework
IObservable<T>→Observable<T>in all public APIsOfType<T>()→OfType<TSrc, TDest>()(R3 requires both type parameters)Select(...)may require explicit type parameters:Select<TIn, TOut>(...)Subscribecallbacks:onError→onErrorResume,onCompletedtakesResultparameterBehaviorSubject<T>removed — useReactiveProperty<T>insteadSubject<T>now from R3 namespace- See Migration Guide for details
-
Replace
[Reactive]source generator withReactiveProperty<T>(#149)- Removed
[Reactive]attribute and its source generator - Use
ReactiveProperty<T>for all ViewModel state:public ReactiveProperty<int> Count { get; } = new(0); - Property access via
.Value:Count.Value++instead ofCount++ - Page bindings subscribe directly to property:
ViewModel.Count.Select(...)instead ofViewModel.CountChanged.Select(...) - ViewModels no longer need
partialkeyword (unless using[FromRoute]) - Manual
Dispose()override required to dispose allReactiveProperty<T>instances - Built-in
DistinctUntilChanged— only emits when value actually changes
- Removed
-
Replace timers with
Observable.Interval+TimeProvider(#140)- All timer-based components now accept optional
TimeProviderparameter - Enables deterministic testing with
FakeTimeProvider System.Timers.TimerandSystem.Threading.Timerno longer used
- All timer-based components now accept optional
New Features:
-
DynamicLayoutNode (#147)
- New
Layouts.Dynamic(Func<ILayoutNode> factory)for imperative, page-local state - Re-evaluates factory on each Render/Measure cycle with reference equality to avoid lifecycle churn
Invalidate()method for programmatic trigger;AsDynamicLayout(Observable<Unit>)extension- Implements
IInvalidatingNodefor proper invalidation propagation
- New
-
FocusManager auto-focus and Tab cycling (#146)
FocusPolicyenum:Manual,FirstFocusable,ByPriority— set onReactivePagefor automatic focus on navigationCollectFocusables(ILayoutNode root)— depth-first tree walk collecting focusable nodesCycleFocus(focusables, reverse)— next/previous with wrap-aroundCycleFocusForward()/CycleFocusBackward()helpers onReactivePageFocusManagermigrated fromBehaviorSubject<IFocusable?>toReactiveProperty<IFocusable?>
-
WizardNode (#148)
- Multi-step wizard component:
Layouts.Wizard<TStep>()whereTStep : struct, Enum - Fluent builder:
.WithStep(),.WithProgressStyle(),.WithTitle(),.WithBorder() - Navigation:
TryAdvance(),TryGoBack(),GoToStep(),AdvanceSubStep() - Cancellable
BeforeAdvanceobservable for step validation StepChangedandCompletedobservables- Progress styles:
BlockBar,Arrow,Dots,None - Sub-step support within each step
- Implements
IFocusableandIInvalidatingNode - Uses
DynamicLayoutNodeinternally for step content switching
- Multi-step wizard component:
Bug Fixes:
- Fix invalidation subscription lost after navigation round-trip (#150)
- Fixed rendering bug where leaf nodes lost their invalidation subscriptions after navigating away and back
- Custom
LayoutNodesubclasses should useCreateSubContext(bounds)in Render methods
Cleanup:
- Removed dead
HasReactiveAttributehelper fromLayoutNodeInViewModelAnalyzer(references removed[Reactive]attribute)
0.6.1 February 25th 2026
New Features:
-
Built-in input history for TextInputNode (#144)
- New
WithHistory(maxEntries)fluent API for enabling input history - New
AddHistory(text)method for programmatically adding history entries - Up/Down arrow keys navigate through previous input submissions
- Enter key automatically records non-empty text for future recall
- Off by default for full backward compatibility
- Move history logic from
StreamingChatViewModelintoTextInputNodefor better reusability - Simplified
StreamingChatPageby removing manual history wiring - Includes 17 new comprehensive tests covering navigation, eviction, and restoration
- Updated text-input-node.md documentation
- New
-
Auto-route PasteEvent to focused IPasteReceiver via layout tree walk (#143)
- Automatic paste event routing when no focused
IPasteReceiverexists - New
GetChildNodes()virtual method onLayoutNodefor tree traversal - Implemented in
ContainerNode,PanelNode,ModalNode,ScrollableContainerNode,ReactiveLayoutNode, andConditionalNode - Eliminates manual paste subscription boilerplate from pages
- Paste routing order: focused receiver > tree walk > ViewModel.Input
- Multi-line paste content in history recall shows as summary (e.g.,
[Pasted 3 lines, 20 chars]) TextInputNode.Textsetter automatically condenses multi-line content into paste-display mode- Includes comprehensive routing tests covering all scenarios
- Automatic paste event routing when no focused
0.6.0 February 23rd 2026
New Features:
-
Scrollbar for StreamingTextNode (#138)
- Visual scrollbar renders on the right edge of
StreamingTextNodewhen content exceeds the viewport ScrollbarOptionsrecord for customizing track/thumb characters and colors, withAutoHidesupportWithScrollbar()fluent method onStreamingTextNodefor opt-in configurationGetMaxScrollOffset()exposed onPersistedStreamBufferfor programmatic scroll position access- Closes #135
- Visual scrollbar renders on the right edge of
-
Bracketed paste mode for TextInputNode (#138)
TextInputNodenow implementsIPasteReceiverand handles terminal bracketed paste sequences- Multi-line pastes are captured as a single unit and displayed as a summary placeholder (e.g.,
[Pasted 500 lines, 12345 chars]) - Full pasted content (with newlines preserved) is submitted on Enter
- Any edit action clears the paste and returns to normal input mode
- Prevents multi-line pastes from triggering individual per-line submissions
AnsiCodes.EnableBracketedPaste/DisableBracketedPasteANSI escape constants added- Closes #134
-
Mouse wheel scrolling for scrollable components (#138)
- New
MouseScrollEventinput event for mouse wheel up/down - New
IScrollableinterface for components that support scroll input - Mouse scroll events are automatically routed to the currently focused
IScrollablecomponent EscapeSequenceParserdetects SGR mouse scroll sequences- Closes #136
- New
0.5.1 December 19th 2025
Bug Fixes:
- Fix nullability propagation in [Reactive] source generator (#112)
- Source generator now preserves nullable reference type annotations (e.g.,
string?,int?) in generated properties - Added custom
SymbolDisplayFormatwithIncludeNullableReferenceTypeModifieroption - Nullable fields now generate properties with matching nullability annotations, providing correct nullability hints in consuming code
- Previously, nullable fields like
string? _fieldwould generate non-nullable properties, losing nullability information
- Source generator now preserves nullable reference type annotations (e.g.,
0.5.0 December 18th 2025
New Features:
-
BlockSegment wrapper for block-level streaming text (#108)
- Enables text segments to render as block elements starting on new lines with vertical word wrapping
- New
BlockSegmentclass wraps anyITextSegmentfor block-level rendering - Fluent
.AsBlock()API for creating block segments StreamingTextNodeautomatically detectsBlockSegmentand inserts newlines- Supports both static and animated content (spinners, status indicators)
- Perfect for LLM agent "thinking" displays and streaming content that should update in-place within a fixed area
- Closes #107
-
Page-level key binding system with capture phase (#105)
- Pages can now intercept keyboard input before focused components receive it
- New
PageKeyBindingsclass for registering page-level key handlers - Added
HandlePageInputmethod toIBindablePageinterface TerminaApplication.ProcessEvent()now uses capture phase (page) then bubble phase (focused component)ReactivePageexposesKeyBindingsproperty for navigation key handling- Enables reliable Escape, Tab, and other navigation keys in complex pages
- Closes #104
-
GridNode 2D layout primitive (#100)
- New 2D grid layout enabling consistent column/row sizing across all cells
- Cell addressing via
SetCell(row, col, node)or fluentAddRow()API - Constraint-based sizing for columns and rows (Fixed, Auto, Fill, Percent)
- Grid line rendering with Single, Double, Rounded, and Ascii border styles
- Cell spanning sup...
Termina 0.6.1
0.6.1 February 25th 2026
New Features:
-
Built-in input history for TextInputNode (#144)
- New
WithHistory(maxEntries)fluent API for enabling input history - New
AddHistory(text)method for programmatically adding history entries - Up/Down arrow keys navigate through previous input submissions
- Enter key automatically records non-empty text for future recall
- Off by default for full backward compatibility
- Move history logic from
StreamingChatViewModelintoTextInputNodefor better reusability - Simplified
StreamingChatPageby removing manual history wiring - Includes 17 new comprehensive tests covering navigation, eviction, and restoration
- Updated text-input-node.md documentation
- New
-
Auto-route PasteEvent to focused IPasteReceiver via layout tree walk (#143)
- Automatic paste event routing when no focused
IPasteReceiverexists - New
GetChildNodes()virtual method onLayoutNodefor tree traversal - Implemented in
ContainerNode,PanelNode,ModalNode,ScrollableContainerNode,ReactiveLayoutNode, andConditionalNode - Eliminates manual paste subscription boilerplate from pages
- Paste routing order: focused receiver > tree walk > ViewModel.Input
- Multi-line paste content in history recall shows as summary (e.g.,
[Pasted 3 lines, 20 chars]) TextInputNode.Textsetter automatically condenses multi-line content into paste-display mode- Includes comprehensive routing tests covering all scenarios
- Automatic paste event routing when no focused
0.6.0 February 23rd 2026
New Features:
-
Scrollbar for StreamingTextNode (#138)
- Visual scrollbar renders on the right edge of
StreamingTextNodewhen content exceeds the viewport ScrollbarOptionsrecord for customizing track/thumb characters and colors, withAutoHidesupportWithScrollbar()fluent method onStreamingTextNodefor opt-in configurationGetMaxScrollOffset()exposed onPersistedStreamBufferfor programmatic scroll position access- Closes #135
- Visual scrollbar renders on the right edge of
-
Bracketed paste mode for TextInputNode (#138)
TextInputNodenow implementsIPasteReceiverand handles terminal bracketed paste sequences- Multi-line pastes are captured as a single unit and displayed as a summary placeholder (e.g.,
[Pasted 500 lines, 12345 chars]) - Full pasted content (with newlines preserved) is submitted on Enter
- Any edit action clears the paste and returns to normal input mode
- Prevents multi-line pastes from triggering individual per-line submissions
AnsiCodes.EnableBracketedPaste/DisableBracketedPasteANSI escape constants added- Closes #134
-
Mouse wheel scrolling for scrollable components (#138)
- New
MouseScrollEventinput event for mouse wheel up/down - New
IScrollableinterface for components that support scroll input - Mouse scroll events are automatically routed to the currently focused
IScrollablecomponent EscapeSequenceParserdetects SGR mouse scroll sequences- Closes #136
- New
0.5.1 December 19th 2025
Bug Fixes:
- Fix nullability propagation in [Reactive] source generator (#112)
- Source generator now preserves nullable reference type annotations (e.g.,
string?,int?) in generated properties - Added custom
SymbolDisplayFormatwithIncludeNullableReferenceTypeModifieroption - Nullable fields now generate properties with matching nullability annotations, providing correct nullability hints in consuming code
- Previously, nullable fields like
string? _fieldwould generate non-nullable properties, losing nullability information
- Source generator now preserves nullable reference type annotations (e.g.,
0.5.0 December 18th 2025
New Features:
-
BlockSegment wrapper for block-level streaming text (#108)
- Enables text segments to render as block elements starting on new lines with vertical word wrapping
- New
BlockSegmentclass wraps anyITextSegmentfor block-level rendering - Fluent
.AsBlock()API for creating block segments StreamingTextNodeautomatically detectsBlockSegmentand inserts newlines- Supports both static and animated content (spinners, status indicators)
- Perfect for LLM agent "thinking" displays and streaming content that should update in-place within a fixed area
- Closes #107
-
Page-level key binding system with capture phase (#105)
- Pages can now intercept keyboard input before focused components receive it
- New
PageKeyBindingsclass for registering page-level key handlers - Added
HandlePageInputmethod toIBindablePageinterface TerminaApplication.ProcessEvent()now uses capture phase (page) then bubble phase (focused component)ReactivePageexposesKeyBindingsproperty for navigation key handling- Enables reliable Escape, Tab, and other navigation keys in complex pages
- Closes #104
-
GridNode 2D layout primitive (#100)
- New 2D grid layout enabling consistent column/row sizing across all cells
- Cell addressing via
SetCell(row, col, node)or fluentAddRow()API - Constraint-based sizing for columns and rows (Fixed, Auto, Fill, Percent)
- Grid line rendering with Single, Double, Rounded, and Ascii border styles
- Cell spanning support (
colspanandrowspan) for merged header cells - Focus navigation with 2D arrow key support
- Navigation modes: None, CellNavigation, ChildFocusRouting
- Perfect for dashboards, data tables, and structured layouts
-
TextNode horizontal alignment (#100)
- New
TextAlignmentenum: Left (default), Center, Right - Fluent methods:
Align(),AlignCenter(),AlignRight() - Works seamlessly with GridNode cells and other layout containers
- New
-
Component gallery demo (#103)
- New
Termina.Demo.Galleryproject showcasing UI components - Interactive gallery pages for SelectionList, Grid, and other components
- Run with:
dotnet run --project demos/Termina.Demo.Gallery
- New
Bug Fixes:
-
Fix SelectionListNode number prefixes (#103)
- SelectionListNode now shows number prefixes for all items, not just items 1-9
- Fixes #101
-
Fix SelectionListNode "Other" option auto-start (#103)
- Navigating to "Other" option with arrow keys no longer auto-starts text input
- Text input mode now requires explicit Enter or Space key press
- Fixes #102
-
Fix async test method not awaiting (#109)
- Corrected test method signature to properly await async operations
- Improves test reliability and prevents potential race conditions
0.4.0 December 17th 2025
Breaking Changes:
- VerticalLayout and HorizontalLayout now default to Auto() sizing (#97)
- Previously, these layouts defaulted to Fill(), which caused nested layouts to compete with siblings for space
- New Auto() default means nested layouts size to their content by default
- Fill() is now only needed for children that should claim remaining space within a container
- Root layouts always fill terminal bounds regardless of constraints
- StackLayout retains Fill() as its default since overlays should fill
- Fixes #95
New Features:
-
Rich content support for SelectionListNode (#96)
- New
SelectionItemContentclass for multi-line, styled selection items - Support for
ITextSegment(static and animated) in selection item content - "Other" option with inline text input for custom user responses
- Multi-line items with per-line styling and animated segments (spinners, etc.)
- Proper coordinate translation via sub-context rendering
- New
-
Platform-native console abstraction for event-driven input (#90)
- Replaced polling-based input with platform-native event-driven input on Windows
- Windows console enables VT100/ANSI support via
ENABLE_VIRTUAL_TERMINAL_PROCESSING - Event-driven input via
ReadConsoleInputWeliminates polling latency - Window resize events via
WINDOW_BUFFER_SIZE_EVENT - ~1000x rendering performance improvement on Windows (22-60ms → 0.02-0.08ms per frame)
- Cached console dimensions to avoid repeated P/Invoke calls
- Fallback console for non-Windows platforms with 1ms polling delay
- Closes #78, #79, #81
-
Formalized diagnostic tracing system (#84)
- Lightweight, zero-cost-when-disabled tracing system for debugging TUI applications
- Custom abstraction over Microsoft.Extensions.Logging (no direct dependency required)
- Deferred string formatting via
TraceEventstruct with template and args - Lock-fr...
Termina 0.6.0
0.6.0 February 23rd 2026
New Features:
-
Scrollbar for StreamingTextNode (#138)
- Visual scrollbar renders on the right edge of
StreamingTextNodewhen content exceeds the viewport ScrollbarOptionsrecord for customizing track/thumb characters and colors, withAutoHidesupportWithScrollbar()fluent method onStreamingTextNodefor opt-in configurationGetMaxScrollOffset()exposed onPersistedStreamBufferfor programmatic scroll position access- Closes #135
- Visual scrollbar renders on the right edge of
-
Bracketed paste mode for TextInputNode (#138)
TextInputNodenow implementsIPasteReceiverand handles terminal bracketed paste sequences- Multi-line pastes are captured as a single unit and displayed as a summary placeholder (e.g.,
[Pasted 500 lines, 12345 chars]) - Full pasted content (with newlines preserved) is submitted on Enter
- Any edit action clears the paste and returns to normal input mode
- Prevents multi-line pastes from triggering individual per-line submissions
AnsiCodes.EnableBracketedPaste/DisableBracketedPasteANSI escape constants added- Closes #134
-
Mouse wheel scrolling for scrollable components (#138)
- New
MouseScrollEventinput event for mouse wheel up/down - New
IScrollableinterface for components that support scroll input - Mouse scroll events are automatically routed to the currently focused
IScrollablecomponent EscapeSequenceParserdetects SGR mouse scroll sequences- Closes #136
- New
0.5.1 December 19th 2025
Bug Fixes:
- Fix nullability propagation in [Reactive] source generator (#112)
- Source generator now preserves nullable reference type annotations (e.g.,
string?,int?) in generated properties - Added custom
SymbolDisplayFormatwithIncludeNullableReferenceTypeModifieroption - Nullable fields now generate properties with matching nullability annotations, providing correct nullability hints in consuming code
- Previously, nullable fields like
string? _fieldwould generate non-nullable properties, losing nullability information
- Source generator now preserves nullable reference type annotations (e.g.,
0.5.0 December 18th 2025
New Features:
-
BlockSegment wrapper for block-level streaming text (#108)
- Enables text segments to render as block elements starting on new lines with vertical word wrapping
- New
BlockSegmentclass wraps anyITextSegmentfor block-level rendering - Fluent
.AsBlock()API for creating block segments StreamingTextNodeautomatically detectsBlockSegmentand inserts newlines- Supports both static and animated content (spinners, status indicators)
- Perfect for LLM agent "thinking" displays and streaming content that should update in-place within a fixed area
- Closes #107
-
Page-level key binding system with capture phase (#105)
- Pages can now intercept keyboard input before focused components receive it
- New
PageKeyBindingsclass for registering page-level key handlers - Added
HandlePageInputmethod toIBindablePageinterface TerminaApplication.ProcessEvent()now uses capture phase (page) then bubble phase (focused component)ReactivePageexposesKeyBindingsproperty for navigation key handling- Enables reliable Escape, Tab, and other navigation keys in complex pages
- Closes #104
-
GridNode 2D layout primitive (#100)
- New 2D grid layout enabling consistent column/row sizing across all cells
- Cell addressing via
SetCell(row, col, node)or fluentAddRow()API - Constraint-based sizing for columns and rows (Fixed, Auto, Fill, Percent)
- Grid line rendering with Single, Double, Rounded, and Ascii border styles
- Cell spanning support (
colspanandrowspan) for merged header cells - Focus navigation with 2D arrow key support
- Navigation modes: None, CellNavigation, ChildFocusRouting
- Perfect for dashboards, data tables, and structured layouts
-
TextNode horizontal alignment (#100)
- New
TextAlignmentenum: Left (default), Center, Right - Fluent methods:
Align(),AlignCenter(),AlignRight() - Works seamlessly with GridNode cells and other layout containers
- New
-
Component gallery demo (#103)
- New
Termina.Demo.Galleryproject showcasing UI components - Interactive gallery pages for SelectionList, Grid, and other components
- Run with:
dotnet run --project demos/Termina.Demo.Gallery
- New
Bug Fixes:
-
Fix SelectionListNode number prefixes (#103)
- SelectionListNode now shows number prefixes for all items, not just items 1-9
- Fixes #101
-
Fix SelectionListNode "Other" option auto-start (#103)
- Navigating to "Other" option with arrow keys no longer auto-starts text input
- Text input mode now requires explicit Enter or Space key press
- Fixes #102
-
Fix async test method not awaiting (#109)
- Corrected test method signature to properly await async operations
- Improves test reliability and prevents potential race conditions
0.4.0 December 17th 2025
Breaking Changes:
- VerticalLayout and HorizontalLayout now default to Auto() sizing (#97)
- Previously, these layouts defaulted to Fill(), which caused nested layouts to compete with siblings for space
- New Auto() default means nested layouts size to their content by default
- Fill() is now only needed for children that should claim remaining space within a container
- Root layouts always fill terminal bounds regardless of constraints
- StackLayout retains Fill() as its default since overlays should fill
- Fixes #95
New Features:
-
Rich content support for SelectionListNode (#96)
- New
SelectionItemContentclass for multi-line, styled selection items - Support for
ITextSegment(static and animated) in selection item content - "Other" option with inline text input for custom user responses
- Multi-line items with per-line styling and animated segments (spinners, etc.)
- Proper coordinate translation via sub-context rendering
- New
-
Platform-native console abstraction for event-driven input (#90)
- Replaced polling-based input with platform-native event-driven input on Windows
- Windows console enables VT100/ANSI support via
ENABLE_VIRTUAL_TERMINAL_PROCESSING - Event-driven input via
ReadConsoleInputWeliminates polling latency - Window resize events via
WINDOW_BUFFER_SIZE_EVENT - ~1000x rendering performance improvement on Windows (22-60ms → 0.02-0.08ms per frame)
- Cached console dimensions to avoid repeated P/Invoke calls
- Fallback console for non-Windows platforms with 1ms polling delay
- Closes #78, #79, #81
-
Formalized diagnostic tracing system (#84)
- Lightweight, zero-cost-when-disabled tracing system for debugging TUI applications
- Custom abstraction over Microsoft.Extensions.Logging (no direct dependency required)
- Deferred string formatting via
TraceEventstruct with template and args - Lock-free file output using Channel with single reader pattern
- Category-based filtering (Focus, Layout, Input, Page, Render, etc.)
- Level-based filtering (Trace, Debug, Info, Warning, Error)
- Optional MEL integration via
LoggerTraceListeneradapter - DI extension methods for easy configuration
- Trace logs written to %TEMP%/termina-logs/ with timestamped filenames
- Addresses #72
0.3.0 December 17th 2025
New Features:
- Inline animated text segments with tracked segment support (#71)
- Opt-in tracked segments for
StreamingTextNodeenabling inline animations (spinners, timers, etc.) - Caller-provided
SegmentIdsystem (like HTML div IDs) for tracking and manipulating segments - New interfaces:
ITextSegment,IAnimatedTextSegment,ICompositeTextSegment SpinnerSegmentcomponent with 6 animation styles: Dots, Line, Arrow, Bounce, Box, CircleStaticTextSegmentfor trackable static text- Methods:
AppendTracked(id, segment),Replace(id, segment, keepTracked),Remove(id) - Interface-based message protocol with
IChatMessagefor clean chat operations - Enables any inline animated element (timers, progress bars, blinks, highlighters) with minimal overhead
- Opt-in tracked segments for
Bug Fixes:
Termina 0.5.1
0.5.1 December 19th 2025
Bug Fixes:
- Fix nullability propagation in [Reactive] source generator (#112)
- Source generator now preserves nullable reference type annotations (e.g.,
string?,int?) in generated properties - Added custom
SymbolDisplayFormatwithIncludeNullableReferenceTypeModifieroption - Nullable fields now generate properties with matching nullability annotations, providing correct nullability hints in consuming code
- Previously, nullable fields like
string? _fieldwould generate non-nullable properties, losing nullability information
- Source generator now preserves nullable reference type annotations (e.g.,
0.5.0 December 18th 2025
New Features:
-
BlockSegment wrapper for block-level streaming text (#108)
- Enables text segments to render as block elements starting on new lines with vertical word wrapping
- New
BlockSegmentclass wraps anyITextSegmentfor block-level rendering - Fluent
.AsBlock()API for creating block segments StreamingTextNodeautomatically detectsBlockSegmentand inserts newlines- Supports both static and animated content (spinners, status indicators)
- Perfect for LLM agent "thinking" displays and streaming content that should update in-place within a fixed area
- Closes #107
-
Page-level key binding system with capture phase (#105)
- Pages can now intercept keyboard input before focused components receive it
- New
PageKeyBindingsclass for registering page-level key handlers - Added
HandlePageInputmethod toIBindablePageinterface TerminaApplication.ProcessEvent()now uses capture phase (page) then bubble phase (focused component)ReactivePageexposesKeyBindingsproperty for navigation key handling- Enables reliable Escape, Tab, and other navigation keys in complex pages
- Closes #104
-
GridNode 2D layout primitive (#100)
- New 2D grid layout enabling consistent column/row sizing across all cells
- Cell addressing via
SetCell(row, col, node)or fluentAddRow()API - Constraint-based sizing for columns and rows (Fixed, Auto, Fill, Percent)
- Grid line rendering with Single, Double, Rounded, and Ascii border styles
- Cell spanning support (
colspanandrowspan) for merged header cells - Focus navigation with 2D arrow key support
- Navigation modes: None, CellNavigation, ChildFocusRouting
- Perfect for dashboards, data tables, and structured layouts
-
TextNode horizontal alignment (#100)
- New
TextAlignmentenum: Left (default), Center, Right - Fluent methods:
Align(),AlignCenter(),AlignRight() - Works seamlessly with GridNode cells and other layout containers
- New
-
Component gallery demo (#103)
- New
Termina.Demo.Galleryproject showcasing UI components - Interactive gallery pages for SelectionList, Grid, and other components
- Run with:
dotnet run --project demos/Termina.Demo.Gallery
- New
Bug Fixes:
-
Fix SelectionListNode number prefixes (#103)
- SelectionListNode now shows number prefixes for all items, not just items 1-9
- Fixes #101
-
Fix SelectionListNode "Other" option auto-start (#103)
- Navigating to "Other" option with arrow keys no longer auto-starts text input
- Text input mode now requires explicit Enter or Space key press
- Fixes #102
-
Fix async test method not awaiting (#109)
- Corrected test method signature to properly await async operations
- Improves test reliability and prevents potential race conditions
0.4.0 December 17th 2025
Breaking Changes:
- VerticalLayout and HorizontalLayout now default to Auto() sizing (#97)
- Previously, these layouts defaulted to Fill(), which caused nested layouts to compete with siblings for space
- New Auto() default means nested layouts size to their content by default
- Fill() is now only needed for children that should claim remaining space within a container
- Root layouts always fill terminal bounds regardless of constraints
- StackLayout retains Fill() as its default since overlays should fill
- Fixes #95
New Features:
-
Rich content support for SelectionListNode (#96)
- New
SelectionItemContentclass for multi-line, styled selection items - Support for
ITextSegment(static and animated) in selection item content - "Other" option with inline text input for custom user responses
- Multi-line items with per-line styling and animated segments (spinners, etc.)
- Proper coordinate translation via sub-context rendering
- New
-
Platform-native console abstraction for event-driven input (#90)
- Replaced polling-based input with platform-native event-driven input on Windows
- Windows console enables VT100/ANSI support via
ENABLE_VIRTUAL_TERMINAL_PROCESSING - Event-driven input via
ReadConsoleInputWeliminates polling latency - Window resize events via
WINDOW_BUFFER_SIZE_EVENT - ~1000x rendering performance improvement on Windows (22-60ms → 0.02-0.08ms per frame)
- Cached console dimensions to avoid repeated P/Invoke calls
- Fallback console for non-Windows platforms with 1ms polling delay
- Closes #78, #79, #81
-
Formalized diagnostic tracing system (#84)
- Lightweight, zero-cost-when-disabled tracing system for debugging TUI applications
- Custom abstraction over Microsoft.Extensions.Logging (no direct dependency required)
- Deferred string formatting via
TraceEventstruct with template and args - Lock-free file output using Channel with single reader pattern
- Category-based filtering (Focus, Layout, Input, Page, Render, etc.)
- Level-based filtering (Trace, Debug, Info, Warning, Error)
- Optional MEL integration via
LoggerTraceListeneradapter - DI extension methods for easy configuration
- Trace logs written to %TEMP%/termina-logs/ with timestamped filenames
- Addresses #72
0.3.0 December 17th 2025
New Features:
- Inline animated text segments with tracked segment support (#71)
- Opt-in tracked segments for
StreamingTextNodeenabling inline animations (spinners, timers, etc.) - Caller-provided
SegmentIdsystem (like HTML div IDs) for tracking and manipulating segments - New interfaces:
ITextSegment,IAnimatedTextSegment,ICompositeTextSegment SpinnerSegmentcomponent with 6 animation styles: Dots, Line, Arrow, Bounce, Box, CircleStaticTextSegmentfor trackable static text- Methods:
AppendTracked(id, segment),Replace(id, segment, keepTracked),Remove(id) - Interface-based message protocol with
IChatMessagefor clean chat operations - Enables any inline animated element (timers, progress bars, blinks, highlighters) with minimal overhead
- Opt-in tracked segments for
Bug Fixes:
-
Fix modal text input not accepting keystrokes (#70, #74)
- Critical fix where
TextInputNodein modal dialogs would be immediately disposed after being focused - Fixed
ReactiveLayoutNodelifecycle to callOnDeactivate()instead ofDispose()when switching children - Fixed modal focus propagation -
ModalNodenow forwardsOnFocused()andOnBlurred()to content - Fixed
ReactiveLayoutNodeto callOnActivate()on new children when they are dynamically swapped in - Fixed render loop invalidation to properly propagate through container hierarchy
- Users can now properly type in modal dialogs without
ObjectDisposedException
- Critical fix where
-
Fix NavigationBehavior.PreserveState layout disposal issue (#67, #69)
- Implemented Active/Inactive State Pattern for layout nodes to prevent
ObjectDisposedExceptionwhen navigating - Added
IActivatableNodeinterface definingOnActivate/OnDeactivatelifecycle methods - Layout nodes now pause/resume instead of dispose/recreate on navigation with PreserveState behavior
ReactivePagenow builds layout once and preserves it across navigationsTextInputNode,SpinnerNode,ReactiveLayoutNode,ConditionalNode,SelectionListNode,ModalNode, andContainerNodeall properly implement lifecycle propagationTerminaApplicationnow properly disposes cached pages viaIDisposableimplementation- Fixes race conditions and ensures proper resource cleanup
- Implemented Active/Inactive State Pattern for layout nodes to prevent
Improvements:
- Add TERMINA002 analyzer and refactor MVVM architecture (#68)
- New Roslyn analyzer (TERMINA002) detects layout nodes incorrectly stored as fields/properties in ViewModels
- Refactored MVVM pattern: ViewModel owns Input (public) for business logic, Page owns Focus for interactive control
- ViewModels now handle keyboard input dir...
Termina 0.5.0
0.5.0 December 18th 2025
New Features:
-
BlockSegment wrapper for block-level streaming text (#108)
- Enables text segments to render as block elements starting on new lines with vertical word wrapping
- New
BlockSegmentclass wraps anyITextSegmentfor block-level rendering - Fluent
.AsBlock()API for creating block segments StreamingTextNodeautomatically detectsBlockSegmentand inserts newlines- Supports both static and animated content (spinners, status indicators)
- Perfect for LLM agent "thinking" displays and streaming content that should update in-place within a fixed area
- Closes #107
-
Page-level key binding system with capture phase (#105)
- Pages can now intercept keyboard input before focused components receive it
- New
PageKeyBindingsclass for registering page-level key handlers - Added
HandlePageInputmethod toIBindablePageinterface TerminaApplication.ProcessEvent()now uses capture phase (page) then bubble phase (focused component)ReactivePageexposesKeyBindingsproperty for navigation key handling- Enables reliable Escape, Tab, and other navigation keys in complex pages
- Closes #104
-
GridNode 2D layout primitive (#100)
- New 2D grid layout enabling consistent column/row sizing across all cells
- Cell addressing via
SetCell(row, col, node)or fluentAddRow()API - Constraint-based sizing for columns and rows (Fixed, Auto, Fill, Percent)
- Grid line rendering with Single, Double, Rounded, and Ascii border styles
- Cell spanning support (
colspanandrowspan) for merged header cells - Focus navigation with 2D arrow key support
- Navigation modes: None, CellNavigation, ChildFocusRouting
- Perfect for dashboards, data tables, and structured layouts
-
TextNode horizontal alignment (#100)
- New
TextAlignmentenum: Left (default), Center, Right - Fluent methods:
Align(),AlignCenter(),AlignRight() - Works seamlessly with GridNode cells and other layout containers
- New
-
Component gallery demo (#103)
- New
Termina.Demo.Galleryproject showcasing UI components - Interactive gallery pages for SelectionList, Grid, and other components
- Run with:
dotnet run --project demos/Termina.Demo.Gallery
- New
Bug Fixes:
-
Fix SelectionListNode number prefixes (#103)
- SelectionListNode now shows number prefixes for all items, not just items 1-9
- Fixes #101
-
Fix SelectionListNode "Other" option auto-start (#103)
- Navigating to "Other" option with arrow keys no longer auto-starts text input
- Text input mode now requires explicit Enter or Space key press
- Fixes #102
-
Fix async test method not awaiting (#109)
- Corrected test method signature to properly await async operations
- Improves test reliability and prevents potential race conditions
0.4.0 December 17th 2025
Breaking Changes:
- VerticalLayout and HorizontalLayout now default to Auto() sizing (#97)
- Previously, these layouts defaulted to Fill(), which caused nested layouts to compete with siblings for space
- New Auto() default means nested layouts size to their content by default
- Fill() is now only needed for children that should claim remaining space within a container
- Root layouts always fill terminal bounds regardless of constraints
- StackLayout retains Fill() as its default since overlays should fill
- Fixes #95
New Features:
-
Rich content support for SelectionListNode (#96)
- New
SelectionItemContentclass for multi-line, styled selection items - Support for
ITextSegment(static and animated) in selection item content - "Other" option with inline text input for custom user responses
- Multi-line items with per-line styling and animated segments (spinners, etc.)
- Proper coordinate translation via sub-context rendering
- New
-
Platform-native console abstraction for event-driven input (#90)
- Replaced polling-based input with platform-native event-driven input on Windows
- Windows console enables VT100/ANSI support via
ENABLE_VIRTUAL_TERMINAL_PROCESSING - Event-driven input via
ReadConsoleInputWeliminates polling latency - Window resize events via
WINDOW_BUFFER_SIZE_EVENT - ~1000x rendering performance improvement on Windows (22-60ms → 0.02-0.08ms per frame)
- Cached console dimensions to avoid repeated P/Invoke calls
- Fallback console for non-Windows platforms with 1ms polling delay
- Closes #78, #79, #81
-
Formalized diagnostic tracing system (#84)
- Lightweight, zero-cost-when-disabled tracing system for debugging TUI applications
- Custom abstraction over Microsoft.Extensions.Logging (no direct dependency required)
- Deferred string formatting via
TraceEventstruct with template and args - Lock-free file output using Channel with single reader pattern
- Category-based filtering (Focus, Layout, Input, Page, Render, etc.)
- Level-based filtering (Trace, Debug, Info, Warning, Error)
- Optional MEL integration via
LoggerTraceListeneradapter - DI extension methods for easy configuration
- Trace logs written to %TEMP%/termina-logs/ with timestamped filenames
- Addresses #72
0.3.0 December 17th 2025
New Features:
- Inline animated text segments with tracked segment support (#71)
- Opt-in tracked segments for
StreamingTextNodeenabling inline animations (spinners, timers, etc.) - Caller-provided
SegmentIdsystem (like HTML div IDs) for tracking and manipulating segments - New interfaces:
ITextSegment,IAnimatedTextSegment,ICompositeTextSegment SpinnerSegmentcomponent with 6 animation styles: Dots, Line, Arrow, Bounce, Box, CircleStaticTextSegmentfor trackable static text- Methods:
AppendTracked(id, segment),Replace(id, segment, keepTracked),Remove(id) - Interface-based message protocol with
IChatMessagefor clean chat operations - Enables any inline animated element (timers, progress bars, blinks, highlighters) with minimal overhead
- Opt-in tracked segments for
Bug Fixes:
-
Fix modal text input not accepting keystrokes (#70, #74)
- Critical fix where
TextInputNodein modal dialogs would be immediately disposed after being focused - Fixed
ReactiveLayoutNodelifecycle to callOnDeactivate()instead ofDispose()when switching children - Fixed modal focus propagation -
ModalNodenow forwardsOnFocused()andOnBlurred()to content - Fixed
ReactiveLayoutNodeto callOnActivate()on new children when they are dynamically swapped in - Fixed render loop invalidation to properly propagate through container hierarchy
- Users can now properly type in modal dialogs without
ObjectDisposedException
- Critical fix where
-
Fix NavigationBehavior.PreserveState layout disposal issue (#67, #69)
- Implemented Active/Inactive State Pattern for layout nodes to prevent
ObjectDisposedExceptionwhen navigating - Added
IActivatableNodeinterface definingOnActivate/OnDeactivatelifecycle methods - Layout nodes now pause/resume instead of dispose/recreate on navigation with PreserveState behavior
ReactivePagenow builds layout once and preserves it across navigationsTextInputNode,SpinnerNode,ReactiveLayoutNode,ConditionalNode,SelectionListNode,ModalNode, andContainerNodeall properly implement lifecycle propagationTerminaApplicationnow properly disposes cached pages viaIDisposableimplementation- Fixes race conditions and ensures proper resource cleanup
- Implemented Active/Inactive State Pattern for layout nodes to prevent
Improvements:
-
Add TERMINA002 analyzer and refactor MVVM architecture (#68)
- New Roslyn analyzer (TERMINA002) detects layout nodes incorrectly stored as fields/properties in ViewModels
- Refactored MVVM pattern: ViewModel owns Input (public) for business logic, Page owns Focus for interactive control
- ViewModels now handle keyboard input directly in
OnActivated() - Pages can access
ViewModel.Inputwhen routing to interactive layout nodes - Updated documentation to reflect clearer separation of concerns
- Reduces ceremony while maintaining clean MVVM architecture
-
Documentation improvements (#66)
- Added quick install section to docs homepage with NuGet badges and installation commands
- Improved documentation discoverability for new users
- Fixed docs deployment workflow trigger
0.2.1 December 16th 2025
Improvements:
- Diff-based rendering system ([#61](https://github.com/Aaronontheweb/...
Termina 0.4.0
0.4.0 December 17th 2025
Breaking Changes:
- VerticalLayout and HorizontalLayout now default to Auto() sizing (#97)
- Previously, these layouts defaulted to Fill(), which caused nested layouts to compete with siblings for space
- New Auto() default means nested layouts size to their content by default
- Fill() is now only needed for children that should claim remaining space within a container
- Root layouts always fill terminal bounds regardless of constraints
- StackLayout retains Fill() as its default since overlays should fill
- Fixes #95
New Features:
-
Rich content support for SelectionListNode (#96)
- New
SelectionItemContentclass for multi-line, styled selection items - Support for
ITextSegment(static and animated) in selection item content - "Other" option with inline text input for custom user responses
- Multi-line items with per-line styling and animated segments (spinners, etc.)
- Proper coordinate translation via sub-context rendering
- New
-
Platform-native console abstraction for event-driven input (#90)
- Replaced polling-based input with platform-native event-driven input on Windows
- Windows console enables VT100/ANSI support via
ENABLE_VIRTUAL_TERMINAL_PROCESSING - Event-driven input via
ReadConsoleInputWeliminates polling latency - Window resize events via
WINDOW_BUFFER_SIZE_EVENT - ~1000x rendering performance improvement on Windows (22-60ms → 0.02-0.08ms per frame)
- Cached console dimensions to avoid repeated P/Invoke calls
- Fallback console for non-Windows platforms with 1ms polling delay
- Closes #78, #79, #81
-
Formalized diagnostic tracing system (#84)
- Lightweight, zero-cost-when-disabled tracing system for debugging TUI applications
- Custom abstraction over Microsoft.Extensions.Logging (no direct dependency required)
- Deferred string formatting via
TraceEventstruct with template and args - Lock-free file output using Channel with single reader pattern
- Category-based filtering (Focus, Layout, Input, Page, Render, etc.)
- Level-based filtering (Trace, Debug, Info, Warning, Error)
- Optional MEL integration via
LoggerTraceListeneradapter - DI extension methods for easy configuration
- Trace logs written to %TEMP%/termina-logs/ with timestamped filenames
- Addresses #72
0.3.0 December 17th 2025
New Features:
- Inline animated text segments with tracked segment support (#71)
- Opt-in tracked segments for
StreamingTextNodeenabling inline animations (spinners, timers, etc.) - Caller-provided
SegmentIdsystem (like HTML div IDs) for tracking and manipulating segments - New interfaces:
ITextSegment,IAnimatedTextSegment,ICompositeTextSegment SpinnerSegmentcomponent with 6 animation styles: Dots, Line, Arrow, Bounce, Box, CircleStaticTextSegmentfor trackable static text- Methods:
AppendTracked(id, segment),Replace(id, segment, keepTracked),Remove(id) - Interface-based message protocol with
IChatMessagefor clean chat operations - Enables any inline animated element (timers, progress bars, blinks, highlighters) with minimal overhead
- Opt-in tracked segments for
Bug Fixes:
-
Fix modal text input not accepting keystrokes (#70, #74)
- Critical fix where
TextInputNodein modal dialogs would be immediately disposed after being focused - Fixed
ReactiveLayoutNodelifecycle to callOnDeactivate()instead ofDispose()when switching children - Fixed modal focus propagation -
ModalNodenow forwardsOnFocused()andOnBlurred()to content - Fixed
ReactiveLayoutNodeto callOnActivate()on new children when they are dynamically swapped in - Fixed render loop invalidation to properly propagate through container hierarchy
- Users can now properly type in modal dialogs without
ObjectDisposedException
- Critical fix where
-
Fix NavigationBehavior.PreserveState layout disposal issue (#67, #69)
- Implemented Active/Inactive State Pattern for layout nodes to prevent
ObjectDisposedExceptionwhen navigating - Added
IActivatableNodeinterface definingOnActivate/OnDeactivatelifecycle methods - Layout nodes now pause/resume instead of dispose/recreate on navigation with PreserveState behavior
ReactivePagenow builds layout once and preserves it across navigationsTextInputNode,SpinnerNode,ReactiveLayoutNode,ConditionalNode,SelectionListNode,ModalNode, andContainerNodeall properly implement lifecycle propagationTerminaApplicationnow properly disposes cached pages viaIDisposableimplementation- Fixes race conditions and ensures proper resource cleanup
- Implemented Active/Inactive State Pattern for layout nodes to prevent
Improvements:
-
Add TERMINA002 analyzer and refactor MVVM architecture (#68)
- New Roslyn analyzer (TERMINA002) detects layout nodes incorrectly stored as fields/properties in ViewModels
- Refactored MVVM pattern: ViewModel owns Input (public) for business logic, Page owns Focus for interactive control
- ViewModels now handle keyboard input directly in
OnActivated() - Pages can access
ViewModel.Inputwhen routing to interactive layout nodes - Updated documentation to reflect clearer separation of concerns
- Reduces ceremony while maintaining clean MVVM architecture
-
Documentation improvements (#66)
- Added quick install section to docs homepage with NuGet badges and installation commands
- Improved documentation discoverability for new users
- Fixed docs deployment workflow trigger
0.2.1 December 16th 2025
Improvements:
- Diff-based rendering system (#61)
- Implemented double-buffering terminal wrapper that eliminates screen flickering
- Only outputs changed cells on flush instead of clearing entire screen
- New
DiffingTerminalwrapper withFrameBufferfor efficient cell-level diffing - Uses same proven pattern as ncurses and termbox for flicker-free rendering
TerminaApplicationautomatically wraps terminals withDiffingTerminal- Added
ForceFullRefresh()for complete redraw when needed (resize/corruption)
Bug Fixes:
- Fix terminal state cleanup on application exit (#62)
- Properly restore terminal when Termina application exits
- Disable mouse tracking if enabled
- Reset colors and text attributes
- Flush buffered ANSI commands
- Prevents visual artifacts and inconsistent terminal state after exit
- Fixes #44
0.2.0 December 16th 2025
Breaking Changes:
- Pure reactive architecture (#50)
- All .NET events migrated to
IObservable<T>for consistency with System.Reactive IInvalidatingNode.Invalidated:event Action?→IObservable<Unit>TextInputNode:Submitted,TextChanged,Invalidatednow use observables- Other components (
SpinnerNode,ConditionalNode,ReactiveLayoutNode,ScrollableContainerNode,StreamingTextNode) migrated to observables - Rendering layer events migrated:
OnSubmit→Submitted,OnDirty→Dirty - Migration required: Replace event subscriptions (
+=) with observable subscriptions (.Subscribe()) - Use
.DisposeWith()for automatic subscription cleanup in ViewModels
- All .NET events migrated to
New Features:
- Focus management system (#51)
- Stack-based focus management for interactive components
IFocusableinterface for components that can receive keyboard focusIFocusManagerwith priority-based focus routingTerminaApplicationnow routes keyboard input through focus managerReactiveViewModelexposesFocusproperty for managing focus state
- ModalNode component (#51)
- Overlay component for modal dialogs and selection prompts
- Configurable backdrop styles: Transparent, Dim, Solid
- Positioning options: Center, Top, Bottom
- Escape key dismissal support
- Integrates with focus management system
- Created via
Layouts.Modal()factory method
- SelectionListNode component (#51)
- Keyboard-navigable selection lists with single/multi-select modes
- Number key shortcuts (1-9) for quick selection
- Home/End navigation and automatic scrolling
- Optional "Other" option for custom text input
- Typed items support with
SelectionListNode<T> - Created via
Layouts.SelectionList()factory methods - Exposes
SelectionConfirmedandSelectionCancelledobservables
- DeferredNode component (#51)
- Non-owning node delegation pattern for reactive layouts
- Prevents
ObjectDisposedExceptionwhen conditionally showing/hiding nodes - Useful for modal dialogs that shouldn't be disposed when hidden
- Created via
Layouts.Deferred()factory method
- Inline styled text support (#48)
- Structured...
Termina 0.3.0
0.3.0 December 17th 2025
New Features:
- Inline animated text segments with tracked segment support (#71)
- Opt-in tracked segments for
StreamingTextNodeenabling inline animations (spinners, timers, etc.) - Caller-provided
SegmentIdsystem (like HTML div IDs) for tracking and manipulating segments - New interfaces:
ITextSegment,IAnimatedTextSegment,ICompositeTextSegment SpinnerSegmentcomponent with 6 animation styles: Dots, Line, Arrow, Bounce, Box, CircleStaticTextSegmentfor trackable static text- Methods:
AppendTracked(id, segment),Replace(id, segment, keepTracked),Remove(id) - Interface-based message protocol with
IChatMessagefor clean chat operations - Enables any inline animated element (timers, progress bars, blinks, highlighters) with minimal overhead
- Opt-in tracked segments for
Bug Fixes:
-
Fix modal text input not accepting keystrokes (#70, #74)
- Critical fix where
TextInputNodein modal dialogs would be immediately disposed after being focused - Fixed
ReactiveLayoutNodelifecycle to callOnDeactivate()instead ofDispose()when switching children - Fixed modal focus propagation -
ModalNodenow forwardsOnFocused()andOnBlurred()to content - Fixed
ReactiveLayoutNodeto callOnActivate()on new children when they are dynamically swapped in - Fixed render loop invalidation to properly propagate through container hierarchy
- Users can now properly type in modal dialogs without
ObjectDisposedException
- Critical fix where
-
Fix NavigationBehavior.PreserveState layout disposal issue (#67, #69)
- Implemented Active/Inactive State Pattern for layout nodes to prevent
ObjectDisposedExceptionwhen navigating - Added
IActivatableNodeinterface definingOnActivate/OnDeactivatelifecycle methods - Layout nodes now pause/resume instead of dispose/recreate on navigation with PreserveState behavior
ReactivePagenow builds layout once and preserves it across navigationsTextInputNode,SpinnerNode,ReactiveLayoutNode,ConditionalNode,SelectionListNode,ModalNode, andContainerNodeall properly implement lifecycle propagationTerminaApplicationnow properly disposes cached pages viaIDisposableimplementation- Fixes race conditions and ensures proper resource cleanup
- Implemented Active/Inactive State Pattern for layout nodes to prevent
Improvements:
-
Add TERMINA002 analyzer and refactor MVVM architecture (#68)
- New Roslyn analyzer (TERMINA002) detects layout nodes incorrectly stored as fields/properties in ViewModels
- Refactored MVVM pattern: ViewModel owns Input (public) for business logic, Page owns Focus for interactive control
- ViewModels now handle keyboard input directly in
OnActivated() - Pages can access
ViewModel.Inputwhen routing to interactive layout nodes - Updated documentation to reflect clearer separation of concerns
- Reduces ceremony while maintaining clean MVVM architecture
-
Documentation improvements (#66)
- Added quick install section to docs homepage with NuGet badges and installation commands
- Improved documentation discoverability for new users
- Fixed docs deployment workflow trigger
Termina 0.2.1
0.2.1 December 16th 2025
Improvements:
- Diff-based rendering system (#61)
- Implemented double-buffering terminal wrapper that eliminates screen flickering
- Only outputs changed cells on flush instead of clearing entire screen
- New
DiffingTerminalwrapper withFrameBufferfor efficient cell-level diffing - Uses same proven pattern as ncurses and termbox for flicker-free rendering
TerminaApplicationautomatically wraps terminals withDiffingTerminal- Added
ForceFullRefresh()for complete redraw when needed (resize/corruption)
Bug Fixes:
- Fix terminal state cleanup on application exit (#62)
- Properly restore terminal when Termina application exits
- Disable mouse tracking if enabled
- Reset colors and text attributes
- Flush buffered ANSI commands
- Prevents visual artifacts and inconsistent terminal state after exit
- Fixes #44
0.2.0 December 16th 2025
Breaking Changes:
- Pure reactive architecture (#50)
- All .NET events migrated to
IObservable<T>for consistency with System.Reactive IInvalidatingNode.Invalidated:event Action?→IObservable<Unit>TextInputNode:Submitted,TextChanged,Invalidatednow use observables- Other components (
SpinnerNode,ConditionalNode,ReactiveLayoutNode,ScrollableContainerNode,StreamingTextNode) migrated to observables - Rendering layer events migrated:
OnSubmit→Submitted,OnDirty→Dirty - Migration required: Replace event subscriptions (
+=) with observable subscriptions (.Subscribe()) - Use
.DisposeWith()for automatic subscription cleanup in ViewModels
- All .NET events migrated to
New Features:
- Focus management system (#51)
- Stack-based focus management for interactive components
IFocusableinterface for components that can receive keyboard focusIFocusManagerwith priority-based focus routingTerminaApplicationnow routes keyboard input through focus managerReactiveViewModelexposesFocusproperty for managing focus state
- ModalNode component (#51)
- Overlay component for modal dialogs and selection prompts
- Configurable backdrop styles: Transparent, Dim, Solid
- Positioning options: Center, Top, Bottom
- Escape key dismissal support
- Integrates with focus management system
- Created via
Layouts.Modal()factory method
- SelectionListNode component (#51)
- Keyboard-navigable selection lists with single/multi-select modes
- Number key shortcuts (1-9) for quick selection
- Home/End navigation and automatic scrolling
- Optional "Other" option for custom text input
- Typed items support with
SelectionListNode<T> - Created via
Layouts.SelectionList()factory methods - Exposes
SelectionConfirmedandSelectionCancelledobservables
- DeferredNode component (#51)
- Non-owning node delegation pattern for reactive layouts
- Prevents
ObjectDisposedExceptionwhen conditionally showing/hiding nodes - Useful for modal dialogs that shouldn't be disposed when hidden
- Created via
Layouts.Deferred()factory method
- Inline styled text support (#48)
- Structured API for colored and decorated text in
StreamingTextNode TextDecorationflags: Bold, Dim, Italic, Underline, StrikethroughTextStylerecord combining foreground, background, and decorationsStyledSegmentandStyledLinefor composing styled textStyledWordWrapperpreserves styles across word wrap boundaries- New styled
Append/AppendLineoverloads onStreamingTextNode - Avoids escaping issues with LLM output and user content
- Structured API for colored and decorated text in
Bug Fixes:
- Fix auto-disposal of reactive fields (#49)
- Source generator now creates
DisposeReactiveFields()method for classes with[Reactive]fields - Auto-generates
Dispose()override forReactiveViewModelsubclasses - Added TERMINA001 compiler error when custom
Dispose()doesn't callDisposeReactiveFields() - Prevents
BehaviorSubjectbacking field leaks
- Source generator now creates
Improvements:
- Updated documentation with new components (ModalNode, SelectionListNode, DeferredNode)
- Added
WithChild()method toStackLayoutfor fluent API consistency TextInputNodenow implementsIFocusablefor modal integration- Enhanced Todo demo showcasing modal dialogs and two-step input flows
- 48 new unit tests covering focus management, modals, and selection lists
0.1.0 December 15th 2025
First stable release of Termina - a reactive terminal UI (TUI) framework for .NET.
Major Changes:
This release represents a complete architectural redesign from the beta, removing the Spectre.Console dependency and introducing a custom ANSI terminal rendering system with a declarative layout API.
Breaking Changes:
- Complete removal of Spectre.Console dependency (#33)
- Replaced with custom ANSI terminal rendering system for direct terminal control
- New tree-based declarative layout API replaces component-based approach
- Surgical region-based updates for better performance
- New layout components:
TextNode,PanelNode,TextInputNode,ScrollableContainerNode,StreamingTextNode,SpinnerNode - New layout containers:
Layouts.Vertical()andLayouts.Horizontal()with fluent API - Size constraint system:
Fixed,Fill,Auto,Percent - Pages now use
BuildLayout()returningILayoutNodeinstead of render-based approach - Migration required: All beta applications will need to be rewritten using the new declarative layout API
New Features:
- Live documentation website (#40)
- Comprehensive VitePress documentation now available at https://aaronstannard.com/Termina/
- Includes getting started guides, tutorials, component reference, and advanced topics
- Code examples synced with actual source files
- Automated deployment on releases
- Word wrapping support (#38)
TextNodeautomatically wraps text to multiple lines when exceeding available width- Configurable via
WordWrapproperty andNoWrap()fluent method
- Automatic terminal resize detection (#37)
- UI automatically adjusts when terminal window is resized
- Cross-platform polling approach works on all platforms
- Streaming text components (#24)
- Two-tier buffer architecture with Persisted and Windowed modes
StreamingTextNodefor chat interfaces, logs, and LLM output- Word wrapping support for streaming content
- Includes streaming chat demo with Akka.NET
Bug Fixes:
- Fix
TextInputNodetimer disposal race condition preventing crashes during shutdown (#34) - Fix
PreserveStatesubscription leak by auto-disposing subscriptions inReactiveViewModel(#35) - Fix reactive layout disposal bug (#39)
Improvements:
- XML documentation now generated for all public APIs
- Enhanced demos moved to dedicated
demos/folder - Better AOT/trimming support with analyzer packaging improvements
0.1.0-beta1 December 11th 2025
Initial release of Termina - a reactive terminal UI (TUI) framework for .NET.
Features:
- Reactive MVVM architecture with source-generated properties
- ASP.NET Core-style routing with parameterized routes and type constraints
- Two-tier event architecture (pages and navigation)
- Virtualizable input support for large datasets
- Built on Spectre.Console for rich terminal rendering
- Full AOT/trimming compatibility