████████╗███████╗██████╗ ███╗   ███╗██╗███╗   ██╗██╗   ██╗███████╗
╚══██╔══╝██╔════╝██╔══██╗████╗ ████║██║████╗  ██║██║   ██║██╔════╝
   ██║   █████╗  ██████╔╝██╔████╔██║██║██╔██╗ ██║██║   ██║███████╗
   ██║   ██╔══╝  ██╔══██╗██║╚██╔╝██║██║██║╚██╗██║██║   ██║╚════██║
   ██║   ███████╗██║  ██║██║ ╚═╝ ██║██║██║ ╚████║╚██████╔╝███████║
   ╚═╝   ╚══════╝╚═╝  ╚═╝╚═╝     ╚═╝╚═╝╚═╝  ╚═══╝ ╚═════╝ ╚══════╝

A React-inspired TUI framework for Java 21

jdk 21 zero runtime deps 60 fps MIT
Get started Launch deck → GitHub
// what_is_terminus

A zero-dependency TUI framework
that brings the component model
from React and Flutter to the terminal.

you compose
Components.
Layouts. State. Children. Terminus handles the rest.
terminus handles
Raw mode.
ANSI escapes, signal handling, terminal state — all managed.
double-buffered
Diff rendering.
Only changed cells hit the wire. Zero flicker at any framerate.
event loop
60 fps.
Virtual threads for I/O. UI thread owns state. Unidirectional.
Component model Composite pattern — everything is a Component. Leaf nodes render; Containers hold children.
Declarative layout Flex row/column with gap, padding, alignment. No manual coordinate math.
Virtual scrolling Tables with 100k+ rows render in O(visible rows). Numeric & lexical sort built in.
Keyboard + mouse Full ANSI escape parser, SGR mouse. TextInput: history, kill rings, word-jump, selection.
Zero runtime deps Only JNA for raw terminal mode. No logging framework, no JSON library, no HTTP client.
Java 21 Records, sealed classes, virtual threads throughout. Preview features enabled by default.
// hello_world

A complete TUI app
in ~10 lines.

Build a Layout, add components, call TerminusApp.run(). Raw mode, the event loop, diff rendering — all handled for you. No boilerplate, no lifecycle annotations, no XML.

App.java
// imports omitted
public class App {
  public static void main(String[] args) {
    Layout root = Layout.column().padding(1).build();

    root.add(Text.bold("My App", 0x7F77DD));
    root.addFlex(Table.builder(model, columns).build());
    root.add(statusBar);

    TerminusApp.run(root);
  }
}
// the_component_model

Everything is a Component.

terminus-core/
Component (abstract)
├── Leaf          // renders directly
│   ├── Text
│   ├── ProgressBar
│   ├── TextInput
│   ├── Table
│   └── [your component]
└── Container
    └── Layout (row | column)
        └── gap · padding · flex

Text

Wrap, align, truncate, color. Word wrap with max-lines and ellipsis. The atom of every layout.

ProgressBar

Four styles — eighths Unicode, solid block, ASCII, and braille. All animate at 60fps.

TextInput

Cursor, history, selection, kill rings. Ctrl+K, Ctrl+U, word-jump, SGR-bracketed paste.

Table

100k+ rows. Virtual scroll. Sortable columns. Typed model. Selection callbacks.

// virtual_scrolling

100,000 rows.
Render only what's visible.

100k+
rows supported
O(v)
visible-only render
Built-in
  • Typed model, per-column extractors
  • Numeric & lexical sort, multi-cycle
  • Flex + fixed columns with alignment
  • Selection callback, focus state
  • Custom row colors and highlight
Keyboard navigation
↑ ↓move selection
Home / Endfirst / last row
Page Up / Downscroll by page
scycle sort column
Sreverse sort direction
rreset to original order
Enterfire onSelect callback
// architecture

stdin events diff ANSI.

01 · ingest

EventLoop

  • stdin reader on a virtual thread
  • BlockingQueue<Event>
  • UI thread drains and dispatches
  • Sealed: Key · Mouse · Resize · State

02 · dispatch

EventDispatcher

  • Chain of Responsibility
  • Bubbles up the focus path
  • focused → parent → root
  • onEvent() mutates state, never UI

03 · render

RenderPipeline

  • LayoutEngine measures + places
  • Renderer walks the tree
  • ScreenDiffer computes deltas
  • AnsiWriter — atomic flush
// The golden rule — render() is pure. State mutations live in onEvent(). Unidirectional data flow.
// design_patterns

Built on 13 classic patterns.

PatternWhere
CompositeComponent / Container / Leaf
VisitorRenderer walking the tree
Chain of ResponsibilityEventDispatcher
ObservermarkDirty() bubbling
BuilderEvery component API
Template MethodLayoutEngine
StrategyFlexConfig, TableModel
PatternWhere
CommandEvent sealed hierarchy
Double BufferScreenBuffer
RepositoryTableModel
FacadeTerminusApp, RenderPipeline
State MachineKeyParser
Value ObjectCell, Bounds, Constraint
// install

One dependency.
Full framework.

Add terminus-core to your build. No annotation processors, no code generation, no configuration files. Just components.

github.com/P0intMaN/terminus →
Gradle
dependencies {
  implementation 'io.terminus:terminus-core:1.0.0'
}
Maven
<dependency>
  <groupId>io.terminus</groupId>
  <artifactId>terminus-core</artifactId>
  <version>1.0.0</version>
</dependency>
Run the demo
$ git clone https://github.com/P0intMaN/terminus.git
$ cd terminus
$ ./gradlew :terminus-demo:shadowJar
$ ./run-demo.sh   # a real terminal — not via gradle
// roadmap

What's next.

· ScrollView — scrollable content pane with keyboard nav
· Tree — expandable tree view component
· Modal — overlay with focus trap
· Sparkline — inline time-series mini-chart
· Mouse routing — click events to component under cursor
· Live reflow — terminal resize with zero flicker
· macOS support — alternate termios struct layout for Darwin
· GraalVM native-image — zero-startup-time binaries
PRs welcome — open an issue first, add tests, keep terminus-core dependency-free. Open an issue →