▌ntui

every frame is a grid · only the changed cells repaint

ntui / src / element.rs

The five shapes a terminal tree can take

Every component — including everything in the first-partyntui::widgetsmodule — eventually lowers to one of five node kinds, reconciled into a fiber tree and painted as a grid of cells. That's the whole vocabulary.

┌─────────┐
│ ▢  ▢  ▢ │
└─────────┘

Node::View

View

A flexbox layout box, laid out by Taffy, holding any number of children. This is the only node that occupies space by its own rules — everything else is positioned inside one.

Element::view(props: ViewProps, children: Vec<Element>)
▬▬▬ ▬▬▬▬▬
▬▬ ▬▬▬▬ ▬▬

Node::Text

Text

A leaf run of styled glyphs. It measures itself against the terminal's character grid and writes directly into cells — there is no nested markup inside a Text node.

Element::text(props: TextProps)
 ▢    ▢    ▢
 (no box of
  its own)

Node::Fragment

Fragment

Groups sibling elements for the reconciler without introducing a layout box. Useful when a component needs to return more than one child without changing the flexbox tree.

Element::fragment(children: Vec<Element>)
⎡ ▢  ▢  ▢ ⎤
   context

Node::Provider

Provider

Makes one value available to every descendant component’s use_context, scoped to its children. Nothing renders differently — it only changes what’s reachable below it.

Element::provider::<T>(value: T, children: Vec<Element>)
┌ ─ ─ ─ ┐
│ ⟨ C ⟩ │
└ ─ ─ ─ ┘

Node::Component

Component

A nested instance of another component, boxed and type-erased, rendered on its own fiber with its own hooks. This is how the four nodes above ever get to compose at all.

Element::component::<C>(props: C::Props)

Any element can also carry a reconciliation key, so the fiber tree can tell a moved sibling from a replaced one across renders — mainly for list items:el.with_key("row-3")

No sixth primitive is coming. The first-party widget layer —ntui::widgets, with buttons, selects, tables, modals, and the rest — is built entirely out of these five node kinds, with no special engine access. Your own components compose from the exact same vocabulary. (Mouse support remains out of scope by design.)