A modern terminal user interface library for Java, inspired by ratatui.
- Immediate-mode rendering — widgets render to a
Buffer, double-buffered to eliminate flicker - Multiple backends — JLine3 (default), Aesh (zero-dependency), Lanterna, FFM API (JDK 22+)
- Terminal image rendering — Kitty, iTerm2, and Sixel protocol support
- Declarative layout — percentage, fixed, proportional, and minimum constraints
- Rich widget library — Block, Paragraph, List, Table, Sparkline, BarChart, Gauge, Tabs, Image
- Component tree — managed event loop with focus management and key bindings
- Fluent DSL — toolkit layer for declarative UI composition
- Unicode-aware — correct display width for CJK characters and emoji
- JDK 21+ (FFM backend requires JDK 22+)
ottertui/
├── ottertui-core ← Buffer, Style, Widget, Layout, Text model, InputEvent
├── ottertui-widgets ← Block, Paragraph, List, Table, Sparkline, Gauge, Image, ...
├── ottertui-tui ← Component, TuiRunner, KeyBindings, BackendSelector
├── ottertui-toolkit ← Fluent DSL, StyleSheet, ThemeManager
├── ottertui-backend-jline ← JLine3 backend (recommended, best cross-platform)
├── ottertui-backend-aesh ← Aesh backend (zero-dependency, SSH/Telnet built-in)
├── ottertui-backend-lanterna ← Lanterna backend (JDK 8+ compatible)
├── ottertui-backend-ffm ← FFM API backend (JDK 22+, direct POSIX syscalls)
├── ottertui-examples ← Demo applications
└── ottertui-integration-tests ← Cross-module integration tests
# Build all modules
./gradlew build
# Run all tests
./gradlew test
# Run tests with coverage report
./gradlew test jacocoTestReportCoverage reports are generated per module at build/reports/jacoco/test/html/index.html.
| Module | Line Coverage |
|---|---|
| ottertui-core | 100% |
| ottertui-widgets | 98% |
| ottertui-tui | 91% |
| ottertui-toolkit | 96% |
# Build and run from a real terminal (not an IDE)
gradle :ottertui-examples:installDist
ottertui-examples/build/install/ottertui-examples/bin/ottertui-examplesPress q to quit, arrow keys to navigate.
Level 1 — Immediate mode (widgets only):
var backend = new JLineBackend();
backend.enterRawMode();
var size = backend.size();
var buffer = new Buffer(size.width(), size.height());
var block = Block.bordered().title("Hello");
block.render(new Rect(0, 0, size.width(), size.height()), buffer);
backend.flush(buffer);
backend.exitRawMode();Level 2 — Managed event loop:
var backend = new JLineBackend();
var root = new MyComponent();
var runner = new TuiRunner(backend, root);
runner.keyBindings().bind(KeyCode.CHAR, Set.of(), 'q', runner::stop);
runner.run();Level 3 — Declarative DSL:
var app = Toolkit.build(root -> root
.vertical(
b -> b.text("Header").widget(new MyWidget())
)
);
app.run();var layout = Layout.horizontal(List.of(
Constraint.percentage(30),
Constraint.fixed(20),
Constraint.proportional(1)
));
var areas = layout.split(rect);Render images directly in the terminal using Kitty, iTerm2, or Sixel protocols:
// From file
var img = ImageWidget.fromFile("logo.png", 40, 20, TerminalImage.Protocol.KITTY);
// From classpath resource (auto-detect protocol)
var img = ImageWidget.fromResource("/banner.jpg", 30, 15);
// From raw bytes
var img = ImageWidget.fromBytes(pngBytes, 24, 12, TerminalImage.Protocol.SIXEL);
// From BufferedImage
var img = ImageWidget.fromImage(bufferedImage, 20, 10);
// Render to buffer
img.render(new Rect(0, 0, 40, 20), buffer);Supported protocols (auto-detected from environment):
| Protocol | Format | Quality |
|---|---|---|
| Kitty | APC escape (\033_G...\033\) |
24-bit PNG |
| iTerm2 | OSC 1337 (\033]1337;File=...\007) |
24-bit PNG |
| Sixel | DCS escape (\033Pq...\033\) |
256-color palette |
// Auto-select (JLine3 → Aesh → Lanterna)
var backend = BackendSelector.create();
// Explicit
var backend = new JLineBackend();
var backend = new AeshBackend();
var backend = new LanternaBackend();
var backend = new FfmBackend(); // JDK 22+, --enable-native-access=ALL-UNNAMED
// Via system property
// -Dottertui.backend=jline
// -Dottertui.backend=aesh
// -Dottertui.backend=lanterna
// -Dottertui.backend=ffm| Backend | JDK | Dependencies | Highlights |
|---|---|---|---|
| JLine3 | 21+ | JLine | Best cross-platform, mature |
| Aesh | 8+ | none | Zero-dependency, SSH/Telnet built-in |
| Lanterna | 8+ | Lanterna | Compatible with older JDKs |
| FFM | 22+ | Jansi | Direct POSIX syscalls, minimal overhead |
Contributions are welcome. See CONTRIBUTING.md for setup instructions, code style guidelines, and the pull request process.
MIT
