-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
Cursorial is a stack, not a monolith. Each layer builds on the one below, and each is usable on its own — you
reference only what you intend to use. This page is the map: what each layer adds, how the pieces fit, and how the
Cursorial.UI namespaces are organized for anyone arriving from WPF or Avalonia.
The guiding rule is use as much as you need. Terminal development has a long tail of hard problems — capability detection, VT-vs-Win32 input, grapheme widths, wide-glyph alignment, raw-mode and signal-handler safety, per-terminal quirks. Cursorial absorbs that complexity once, at the bottom, and lets you stop climbing the stack wherever your needs are met:
- A tool that just wants better input parsing takes
Cursorial.Coreand nothing else. - A dashboard that draws its own cells adds
Cursorial.Renderingfor the diffing renderer. - Something that wants charts, gradients, and a scene compositor adds
Cursorial.Drawing. - A full declarative, data-bound application climbs all the way to
Cursorial.UIandCursorial.UI.Xaml.
Each layer up the stack depends on the ones below it but never the reverse — Cursorial.Core knows nothing about
widgets, and Cursorial.Rendering knows nothing about layout. The boundaries are real assembly boundaries, so the
dependency direction is enforced by the compiler.
Cursorial.Core input · output writers · capability negotiation · text utilities
└─ Cursorial.Rendering cell buffer · diffing renderer · rich text · images · blending
├─ Cursorial.Drawing scenes & compositor · brushes/gradients · pen/junction engine · charts
├─ Cursorial.Animation time-free IAnimation<T> · easings · interpolators (consumer owns the clock)
└─ Cursorial.UI property system · layout · controls · styling · binding · input/focus · windowing
└─ Cursorial.UI.Xaml runtime loader · source generator · compiled bindings · code-behind
Cursorial.Drawing and Cursorial.Animation are siblings above Cursorial.Rendering: Drawing is a retained
scene/compositor model, Animation is a pure time-to-value engine with no ambient clock. Cursorial.UI is the layer
that brings them together — it composites scenes and owns the clock that drives animations.
Cursorial.Core is the foundation: input parsing and delivery (InputEvent records, pull and push device
shapes), the byte-emitting output writers (SgrEncoder, CursorWriter, ScreenWriter, HyperlinkWriter,
TextSizingWriter), capability negotiation (VtTerminalNegotiator — an XTVERSION + DA1 handshake returning
realized capabilities), the orchestrated TerminalSession (raw mode, opt-in protocols, signal-safe restore), and
grapheme-aware text utilities. This is the only layer published on NuGet today and its surface is approaching stable.
Cursorial.Rendering adds the cell buffer and the diffing frame renderer. You write into a CellBuffer and hand
successive buffers to a stateful FrameRenderer that diffs them and emits the minimal byte stream to update the
terminal. Writes are grapheme-aware, wide-cell consistent, and capability-quantized before diffing, so a stable frame
produces an empty delta even on a 16-color terminal. On top of the buffer sit rich text, images (Kitty graphics →
iTerm2 → Sixel with glyph fallback), sized text, and blending/alpha compositing.
Cursorial.Drawing adds a retained-scene drawing layer: the unit of work is a Scene (a cached raster)
composited by a SceneCompositor that only repaints what changed. It brings brushes and gradients, a pen engine that
forms box-drawing junctions automatically where strokes meet, charts (Cursorial.Drawing.Charts), and the UI drawing
primitives — an intra-scene clip/translate stack, occluding fills, dither, titled panels, and drop/inner shadows.
Cursorial.Animation adds a time-free animation engine. IAnimation<T> maps elapsed time to a value, so the
consumer owns the clock and the layer stays deterministic and testable. It provides easings (cubic/quad/elastic/
bounce/cubic-bezier(…)), a process-global interpolator registry (double, int, Color, Point/Size/Rect,
Margins, brushes, pens), and combinators. Cursorial.UI drives these through a frame-aligned scheduler.
Cursorial.UI is the WPF/Avalonia-style framework: a dependency-property system with a priority-ordered value
store, an element tree with Measure/Arrange layout, retained render zones, routed input and focus, CSS-like
styling, data binding, resources and theming, windowing, storyboards and transitions, and a full control catalog. A
headless test harness (Cursorial.UI.Testing) makes the whole framework unit-testable without a TTY.
Cursorial.UI.Xaml adds declarative markup — define your UI in XAML, loaded at runtime or compiled by a Roslyn
source generator. The runtime loader handles {Binding}/{StaticResource}/ControlTemplates/resource dictionaries
with line+column diagnostics; the generator emits typed x:Name fields, InitializeComponent() code-behind, compiled
bindings, and an AOT-clean metadata provider.
Cursorial targets Windows, macOS, and Linux terminals as equal peers. There is no privileged platform: a design
choice that would bake in a single-platform assumption (VT sequences only, Win32 console only) needs an explicit story
for the others before it lands. In practice that means the negotiator identifies terminal families and enables only
what each honors; input decoding covers both VT/ANSI wire formats and Windows Input Mode; and TerminalSession
abstracts platform stdio (POSIX stty raw mode vs. Windows console-mode P/Invoke) behind one factory. The suite has
been exercised against kitty, Ghostty, iTerm2, WezTerm, Rio, Apple Terminal, Alacritty, GNOME Terminal, ConEmu/Cmder,
and Windows Terminal / Console Host.
The target framework is net10.0 for the runtime layers; the two XAML pieces shared with the source generator
(Cursorial.UI.Xaml.Frontend, Cursorial.UI.Xaml.Generator, Cursorial.Shared) target netstandard2.0 so the
same parser runs inside a Roslyn analyzer.
| Project | Target | Contents |
|---|---|---|
Cursorial.Core |
net10.0 | Input parsing & delivery, output writers, capability negotiation, terminal session, text utilities. |
Cursorial.Rendering |
net10.0 | Cell buffer, diffing frame renderer, rich text, images, blending & alpha compositing. |
Cursorial.Drawing |
net10.0 | Scenes & compositor, brushes/gradients, pen/junction engine, charts, UI drawing primitives. |
Cursorial.Animation |
net10.0 | Time-free IAnimation<T>, easings, interpolator registry. |
Cursorial.UI |
net10.0 | The framework: property system, layout, controls, styling, binding, input/focus, windowing, animation. |
Cursorial.UI.Xaml.Frontend |
netstandard2.0 | The shared XAML parser, the structure-of-arrays node model, markup-extension grammar, diagnostics, and type-system seams. No Cursorial.UI reference. |
Cursorial.UI.Xaml |
net10.0 | The runtime XAML loader: markup extensions, resource dictionaries, deferred content/templates. |
Cursorial.UI.Xaml.Generator |
netstandard2.0 | The Roslyn incremental source generator: symbol-backed parse, compiled bindings, code-behind, AOT-clean metadata provider. |
Cursorial.UI.Themes |
net10.0 | Bundled UI themes (the built-in control themes, also available as embedded XAML). |
Cursorial.UI.Testing |
net10.0 | The headless UITestHost + synthetic terminal — the integration substrate so no UI test needs a TTY. |
Cursorial.Shared |
netstandard2.0 | Markup attributes ([ContentProperty], [TypeConverter], …) shared by the loader and the generator. |
Cursorial.Demo |
net10.0 | An interactive REPL that drives every layer end-to-end. |
Cursorial.Gallery |
net10.0 | A standalone XAML-first MVVM control gallery — a full app, not a demo command. |
*.Tests |
net10.0 | xUnit suites per project. |
Cursorial.UI.Xaml.Frontend is the keystone of the dual loader/generator design: it is a netstandard2.0 assembly
with no reference to Cursorial.UI, holding the parser, node model, and type-system seams. The runtime loader and
the build-time generator are two backends over the same frontend, and a drift gate asserts they produce
byte-identical element trees.
The bottom — Cursorial.Core alone, no rendering:
// Raw mode, capability handshake, opt-in protocols, signal-safe restore — one call.
await using var session = await TerminalSession.OpenAsync();
await foreach (var evt in session.Input.ReadAllAsync())
if (evt is KeyEvent { Kind: KeyEventKind.Down, Text.Span: "q" }) break;The top — the full framework, built imperatively:
var app = UIApplication.CreateBuilder().Build();
return await app.RunAsync(() =>
{
var stack = new StackPanel { Spacing = 1, Margin = new Margins(2, 1) };
var button = new Button { Content = "_Click me" }; // _ marks the Alt access key
button.Click += (_, _) => button.Content = "Clicked!";
stack.Children.Add(new TextBlock { Text = "Hello, Cursorial.UI 👋" });
stack.Children.Add(button);
return stack;
});…or the same UI declared in XAML and loaded at runtime:
var root = XamlLoader.Shared.Load<StackPanel>(xaml);
root.DataContext = viewModel;The UI framework deliberately mirrors WPF's (and Avalonia's) namespace organization, so the mental model transfers
directly. The acronym UI is fully capitalized in type names — UIElement, UIProperty, UIApplication — never
UiElement.
-
Cursorial.UI— the core, analogous to WPF'sSystem.Windows. The base objects (UIObject/UIElement), the dependency-property system, element-level layout enums (Visibility, the alignment enums), render integration, and hosting (UIApplication).UIElementCollectionlives here too, besideUIElement— a deliberate deviation from WPF's placement. -
Cursorial.UI.Controls— analogous toSystem.Windows.Controls.Paneland the panels (StackPanel,DockPanel,GridwithGridLength/definitions,Canvas,WrapPanel), the presenters, every control fromControldown, and the panel-facingOrientation/Dockenums. -
Cursorial.UI.Input— routed events, the dispatcher, focus, and gestures.
The folder layout mirrors the namespaces (Cursorial.UI/Controls/, Cursorial.UI/Input/). Brushes and pens live one
layer down in Cursorial.Drawing.Media, with charts in Cursorial.Drawing.Charts.
One naming note worth knowing early: the UI styling type is Cursorial.UI.Style (the WPF Style analog), which is
distinct from the SGR Cursorial.Output.Style value type used for cell colors and attributes. Framework source
disambiguates with a using alias; in your own code, the Cursorial.UI.Style you reach for in styling and the
Style you pass to CellBuffer.Write are different types from different layers.
- Getting started — install, the first running app, and where to climb the stack.
- UI overview — the framework's object model, element tree, and frame loop in depth.
- Core input — the input event model and device shapes at the bottom of the stack.
- XAML — the runtime loader, the source generator, and how they share one parser.
Cursorial.Core
Cursorial.Rendering
Drawing & Animation
Cursorial.UI
- Overview
- Layout & panels
- Controls
- Styling & themes
- Data binding
- Input & focus
- Windowing
- Animation & transitions
Declarative