A minimalist, typography-driven TUI library with Kitty graphics support.
- Good form: Typography-first visual hierarchy, avoiding heavy borders
- Hybrid rendering: Retained component structure + immediate mode rendering
- Performance: Built for audio/music applications with real-time requirements
- Graphics: Native support for Kitty, Sixel, and fallback to Unicode blocks
mkui/
├── terminal.rs # Terminal geometry & capability detection
├── render.rs # Rendering backend with multi-backend graphics
├── event.rs # Keyboard, mouse, and terminal events
├── layout.rs # Flex-based layout system
├── component.rs # Component trait & lifecycle
├── focus.rs # Focus management with Tab navigation
├── modal/ # Vim-style modal editing
├── style.rs # Type-safe CSS-like styling
├── theme/ # Theming with color degradation
├── i18n.rs # Internationalization & RTL support
├── slots.rs # Priority-layered slot system
├── context.rs # Render context (theme, locale, a11y)
└── components/ # Built-in components
├── container.rs # Flex container
├── text.rs # Text with styling & alignment
├── text_input.rs # Editable text input
├── header.rs # Top bar
├── status_bar.rs # Bottom bar
├── slotted_bar.rs # Slot-based responsive bar
├── list.rs # Navigable list with Vim keys
├── split.rs # Split pane layout
├── scrollable.rs # Scrollable viewport
├── popup/ # Modal popups & confirmation dialogs
├── command_palette.rs # Vim-style command line
└── graphics_components.rs # Image & Animation
cargo run --example demoPress q or ESC to quit.
use anyhow::Result;
use mkui::{
components::{Header, StatusBar},
component::Component,
context::RenderContext,
event::{Event, EventPoller, Key},
layout::{FlexDirection, FlexLayout, Rect, Size},
render::Renderer,
slots::Slots,
terminal::TerminalCapabilities,
theme::Theme,
};
fn main() -> Result<()> {
let mut renderer = Renderer::new()?;
renderer.enter_alt_screen()?;
let caps = TerminalCapabilities::detect();
let theme = Theme::new(caps);
let slots = Slots::new();
let ctx = RenderContext::new(&theme, &slots);
let events = EventPoller::new()?;
let mut header = Header::new();
let mut status = StatusBar::with_text("My App", "", &theme);
loop {
let (cols, rows) = renderer.context().char_dimensions();
let bounds = Rect::fullscreen(cols, rows);
let layout = FlexLayout::new(FlexDirection::Column);
let rects = layout.layout(bounds, &[Size::Fixed(1), Size::Flex(1), Size::Fixed(1)]);
renderer.begin_frame()?;
renderer.clear()?;
header.render(&mut renderer, rects[0], &ctx)?;
// ... render content in rects[1]
status.render(&mut renderer, rects[2], &ctx)?;
renderer.end_frame()?;
match events.read()? {
Event::Key(Key::Char('q') | Key::Esc) => break,
Event::Resize(_, _) => renderer.refresh_geometry()?,
_ => {}
}
}
Ok(())
}Flex-based layout container supporting row/column direction, gaps, and padding.
Styled text with alignment (left, center, right) and ANSI color codes.
Editable text field with cursor movement, word navigation, and selection.
Top bar with centered title and inverse video styling.
Bottom bar with left, center, and right text sections.
Responsive bar that allocates space to slots based on priority and available width.
Navigable list with Vim-style j/k navigation, selection modes, and virtual scrolling.
Vim-style split panes with horizontal/vertical splits and Ctrl-w navigation.
Viewport manager for scrolling through content larger than the visible area.
Vim-style command line with completion, history, and mode prompts.
Modal popup dialogs with configurable position, border style, and actions.
Graphics components using the best available backend (Kitty, Sixel, Unicode blocks).
The renderer automatically detects and uses the best available graphics backend (in priority order):
- Linux framebuffer (
/dev/fb0) - Direct rendering in TTYs without X/Wayland - Kitty graphics - High-performance PNG rendering with chunking and tmux passthrough
- Sixel - Fallback for xterm, mlterm, iTerm2, and compatible terminals
- Unicode blocks - Universal fallback using
░▒▓█characters
Auto-detection happens on Renderer::new():
let renderer = Renderer::new()?;
println!("Using: {}", renderer.graphics_backend().name());Force a specific backend:
let renderer = Renderer::with_backend(GraphicsBackend::Blocks)?;// Raw RGB image data
let image_data: Vec<u8> = generate_rgb_image(256, 256);
renderer.render_image(
&image_data,
256, // width in pixels
256, // height in pixels
10, // column position
5, // row position
Some(40), // width in character cells (optional)
Some(20), // height in character cells (optional)
)?;The headless renderer allows testing components without a live terminal:
use mkui::render::Renderer;
let mut renderer = Renderer::headless(); // 80x24, no I/O
// Use renderer in tests as normal- Pixel dimensions are estimated from typical monospace font metrics rather than queried from the terminal (CSI 14t / 16t not yet implemented)
- Framebuffer rendering is a basic implementation
- Sixel encoding uses a simple built-in encoder (no libsixel integration)