Architecture

Serial ownership, explicit composition.

Flyology TUI separates application transitions, component state, layout, rendering, and terminal ownership. Each boundary uses typed values and bounded storage.

One event produces one transition.

  1. The backend reads terminal input.
  2. The runner places a typed event in its bounded queue.
  3. The application model owner updates the model during serial event dispatch.
  4. After each dispatch, the application model owner presents a complete view. If the transition requests a command, the runner also queues it for the bounded command worker.
  5. The renderer compares cells and writes the terminal delta.

Flyology_TUI.Runners coordinates this flow. It does not move model ownership to the input or command tasks.

Components own bounded local state.

A component model can retain selection, focus, viewport position, text, undo history, or capture state. Capacity is visible in its generic parameters, constructor, or error contract.

Containers do not own heterogeneous child models. The application renders and routes those children through published regions. This rule keeps chat bodies, accordion bodies, window content, dock content, and table cell content typed.

Presentation is a value.

Flyology_TUI.Surfaces stores styled terminal cells. Layout joins, clips, layers, and frames surfaces. Responsive components can publish immutable geometry with the same regions used for render and hit testing.

Appearance, theme, skin, and terminal color profile are borrowed during presentation. They do not become model identity.

The backend owns terminal lifecycle.

Flyology_TUI.Backends defines the terminal contract. The POSIX implementation owns raw mode, alternate-screen lifecycle, size observation, waiting, input, and output.

Flyology_TUI.Renderers owns frame differencing and color adaptation. It does not own the terminal or application state.

Alternative implementation models stay outside UI ownership.

Pure renderers can remain stateless. Synchronous retained controllers can own bounded local state. External tasks or actors can perform concurrent work.

Every external worker posts only detached typed snapshots into the bounded event queue. It never owns, mutates, or renders live UI state.

This constraint supports task-based services without introducing a second application loop.