Skip to content

Conversation

@Aaronontheweb
Copy link
Owner

Summary

  • Remove Spectre.Console dependency entirely - replaced with custom ANSI rendering
  • Implement complete v2 tree-based declarative layout system
  • Delete deprecated stringly-typed region APIs (~3,500 lines removed)
  • Add reactive MVVM framework with page/viewmodel binding
  • Fix NuGet packaging for Termina.Generators (now embedded as analyzer)

Technical Changes

Tree-Based Layout System

  • ILayoutNode hierarchy: TextNode, PanelNode, VerticalLayout, HorizontalLayout
  • Fluent builder pattern for layout construction
  • ReactiveLayoutNode for observable-to-layout bindings
  • Sub-context pattern for proper coordinate translation

Removed APIs (Breaking Changes)

  • ReactivePageBase<T> → use ReactivePage<T> with BuildLayout() instead
  • Region class and region-based layout engine
  • RenderCoordinator and RenderCommand
  • FocusManager (will be reimplemented for tree-based system)
  • ObservableExtensions for region binding

NuGet Packaging Fix

  • Termina.Generators now embedded in analyzers/dotnet/cs/ folder
  • Consumers no longer need to reference the generator separately

Test Plan

  • All 337 tests pass
  • V2 demo renders correctly with tree-based layout
  • NuGet package contains embedded analyzer
  • Build succeeds in Release mode

Implement core infrastructure for direct ANSI terminal control to replace
Spectre.Console rendering. This provides surgical region-based updates for
better performance and control.

Phase 1 - Core Infrastructure:
- ANSI terminal abstraction (IAnsiTerminal, AnsiTerminal, AnsiCodes)
- VirtualTerminal for deterministic testing
- Color struct with Default, Indexed (256), and RGB modes
- RenderCoordinator with channel-based command batching
- Region-based layout system with flexible constraints
  (Fixed, Percent, Remaining, Auto, FromBottom, FromRight)
- LayoutEngine for computing absolute screen positions
- IRenderContext and RegionRenderContext for relative rendering
- Mouse and resize events added to input system

Phase 2 - Component Foundation:
- IRenderable interface for v2 components
- Text component with color support
- TextInput with cursor, placeholder, keyboard handling
- Panel with multiple border styles (Single, Double, Rounded, Ascii)
- ScrollableContent with scrollbar visualization
- FocusManager for Tab navigation between regions
- ObservableExtensions with RenderTo binding

All 510 tests passing.
Implement Phase 3 of v2 rendering infrastructure:

- Add ReactivePageBase<TViewModel> for defining regions and wiring
  observable bindings via .RenderTo() extension
- Add IReactivePage interface for v2 page lifecycle management
- Add ReactiveTerminaApp for integrating reactive pages with
  RenderCoordinator, input handling, and focus management
- Add TerminaApp for simpler event-based scenarios

New Termina.Demo.V2 project demonstrates the reactive pattern:
- CounterViewModel uses [Reactive] attributes for observable properties
- CounterPage binds observables to regions in OnBound()
- Supports both interactive console and headless test modes

Includes comprehensive test coverage (11 new tests, 530 total passing)
for reactive app lifecycle, page binding, input handling, and focus.
Replace Spectre.Console with a fully custom ANSI terminal rendering
system that provides surgical region-based updates.

New declarative layout API:
- ILayoutNode interface for tree-based layouts
- Layouts.Vertical() and Layouts.Horizontal() containers with fluent API
- TextNode, PanelNode, EmptyNode for basic UI elements
- SpinnerNode for animated loading indicators
- TextInputNode with cursor, selection, and history support
- ScrollableContainerNode with auto-scroll policies
- StreamingTextNode wrapping persisted/windowed buffers
- ReactiveLayoutNode for observable-driven UI (.AsLayout() extension)
- ConditionalNode for When() conditional rendering
- SizeConstraint system (Fixed, Fill, Auto, Percent)

Framework changes:
- ReactivePage now uses BuildLayout() returning ILayoutNode
- TerminaApplication renders directly via IAnsiTerminal
- IPage interface changed from Render() to BuildLayout()
- Added Gray, DarkGray, LightGray colors to Color struct

Deleted Spectre.Console components:
- Panel, SelectList, StatusBar, TextInput, StreamingText
- RenderMode enum (no longer needed)
- Old component tests (functionality covered by new layout tests)

Updated all demo projects to use declarative layout API.
- Fix PanelNode and TextNode rendering to use sub-contexts for proper
  coordinate translation within bounds
- Migrate V2 demo to use TerminaApplication with generic host pattern
- Delete deprecated stringly-typed region APIs:
  - ReactivePageBase, IReactivePage, ReactiveTerminaApp, TerminaApp
  - Region, LayoutEngine, LayoutConstraint, RenderCoordinator
  - FocusManager, ObservableExtensions.RenderTo()
- Remove associated test files for deleted APIs

The tree-based layout system (ReactivePage<T> with BuildLayout()
returning ILayoutNode) is now the only supported approach.
The Termina.Generators source generator was not being included as a
dependency in the NuGet package due to PrivateAssets="all" on the
project reference. Rather than making it a separate package dependency,
this embeds the generator DLL directly in the analyzers/dotnet/cs folder
of the Termina NuGet package.

This follows the Roslyn source generators cookbook pattern for packaging
generators that are tightly coupled to the main library.
Replace reflection-based access to LayoutRoot property with interface-based
access via IBindablePage. This fixes IL2075 trim analysis error that caused
AOT builds to fail.
Configure Termina.Generators for proper NuGet distribution:
- Package DLL into analyzers/dotnet/cs using PackBuildOutputs target
- Set IncludeBuildOutput=false, DevelopmentDependency=true
- SuppressDependenciesWhenPacking prevents Roslyn dependencies from leaking

Update Termina to reference Termina.Generators as a normal ProjectReference
so the dependency flows to NuGet consumers. Pattern follows MemoryPack's
approach where the generator packages itself correctly and the main library
just takes a plain reference.
Add try/catch with fallback defaults (80x24) when accessing
Console.WindowWidth/Height fails due to no TTY being available.
This fixes AOT test failures in CI environments where there is no
terminal attached.
@Aaronontheweb Aaronontheweb enabled auto-merge (squash) December 16, 2025 00:17
@Aaronontheweb Aaronontheweb merged commit 9ba8eef into dev Dec 16, 2025
6 checks passed
@Aaronontheweb Aaronontheweb deleted the feature/remove-spectre-console branch December 16, 2025 00:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants