@mariozechner/pi-tui
v0.12.14
Published
Terminal User Interface library with differential rendering for efficient text-based applications
Downloads
11,155
Maintainers
Readme
@mariozechner/pi-tui
Minimal terminal UI framework with differential rendering and synchronized output for flicker-free interactive CLI applications.
Features
- Differential Rendering: Three-strategy rendering system that only updates what changed
- Synchronized Output: Uses CSI 2026 for atomic screen updates (no flicker)
- Bracketed Paste Mode: Handles large pastes correctly with markers for >10 line pastes
- Component-based: Simple Component interface with render() method
- Built-in Components: Text, Input, Editor, Markdown, Loader, SelectList, Spacer
- Autocomplete Support: File paths and slash commands
Quick Start
import { TUI, Text, Editor, ProcessTerminal } from "@mariozechner/pi-tui";
// Create terminal
const terminal = new ProcessTerminal();
// Create TUI
const tui = new TUI(terminal);
// Add components
tui.addChild(new Text("Welcome to my app!"));
const editor = new Editor();
editor.onSubmit = (text) => {
console.log("Submitted:", text);
tui.addChild(new Text(`You said: ${text}`));
};
tui.addChild(editor);
// Start
tui.start();Core API
TUI
Main container that manages components and rendering.
const tui = new TUI(terminal);
tui.addChild(component);
tui.removeChild(component);
tui.start();
tui.stop();
tui.requestRender(); // Request a re-renderComponent Interface
All components implement:
interface Component {
render(width: number): string[];
handleInput?(data: string): void;
}Built-in Components
Container
Groups child components.
const container = new Container();
container.addChild(component);
container.removeChild(component);Text
Displays multi-line text with word wrapping and padding.
const text = new Text("Hello World", paddingX, paddingY); // defaults: 1, 1
text.setText("Updated text");Input
Single-line text input with horizontal scrolling.
const input = new Input();
input.onSubmit = (value) => console.log(value);
input.setValue("initial");Key Bindings:
Enter- SubmitCtrl+A/Ctrl+E- Line start/endCtrl+WorOption+Backspace- Delete word backwardsCtrl+U- Delete to start of lineCtrl+K- Delete to end of line- Arrow keys, Backspace, Delete work as expected
Editor
Multi-line text editor with autocomplete, file completion, and paste handling.
const editor = new Editor();
editor.onSubmit = (text) => console.log(text);
editor.onChange = (text) => console.log("Changed:", text);
editor.disableSubmit = true; // Disable submit temporarily
editor.setAutocompleteProvider(provider);Features:
- Multi-line editing with word wrap
- Slash command autocomplete (type
/) - File path autocomplete (press
Tab) - Large paste handling (>10 lines creates
[paste #1 +50 lines]marker) - Horizontal lines above/below editor
- Fake cursor rendering (hidden real cursor)
Key Bindings:
Enter- SubmitShift+Enter,Ctrl+Enter, orAlt+Enter- New line (terminal-dependent, Alt+Enter most reliable)Tab- AutocompleteCtrl+K- Delete lineCtrl+A/Ctrl+E- Line start/end- Arrow keys, Backspace, Delete work as expected
Markdown
Renders markdown with syntax highlighting and optional background colors.
const md = new Markdown(
"# Hello\n\nSome **bold** text",
bgColor, // optional: "bgRed", "bgBlue", etc.
fgColor, // optional: "white", "cyan", etc.
customBgRgb, // optional: { r: 52, g: 53, b: 65 }
paddingX, // optional: default 1
paddingY // optional: default 1
);
md.setText("Updated markdown");Features:
- Headings, bold, italic, code blocks, lists, links, blockquotes
- Syntax highlighting with chalk
- Optional background colors (including custom RGB)
- Padding support
- Render caching for performance
Loader
Animated loading spinner.
const loader = new Loader(tui, "Loading...");
loader.start();
loader.stop();SelectList
Interactive selection list with keyboard navigation.
const list = new SelectList([
{ value: "opt1", label: "Option 1", description: "First option" },
{ value: "opt2", label: "Option 2", description: "Second option" },
], 5); // maxVisible
list.onSelect = (item) => console.log("Selected:", item);
list.onCancel = () => console.log("Cancelled");
list.setFilter("opt"); // Filter itemsControls:
- Arrow keys: Navigate
- Enter or Tab: Select
- Escape: Cancel
Spacer
Empty lines for vertical spacing.
const spacer = new Spacer(2); // 2 empty lines (default: 1)Autocomplete
CombinedAutocompleteProvider
Supports both slash commands and file paths.
import { CombinedAutocompleteProvider } from "@mariozechner/pi-tui";
const provider = new CombinedAutocompleteProvider(
[
{ name: "help", description: "Show help" },
{ name: "clear", description: "Clear screen" },
{ name: "delete", description: "Delete last message" },
],
process.cwd() // base path for file completion
);
editor.setAutocompleteProvider(provider);Features:
- Type
/to see slash commands - Press
Tabfor file path completion - Works with
~/,./,../, and@prefix - Filters to attachable files for
@prefix
Differential Rendering
The TUI uses three rendering strategies:
- First Render: Output all lines without clearing scrollback
- Width Changed or Change Above Viewport: Clear screen and full re-render
- Normal Update: Move cursor to first changed line, clear to end, render changed lines
All updates are wrapped in synchronized output (\x1b[?2026h ... \x1b[?2026l) for atomic, flicker-free rendering.
Terminal Interface
The TUI works with any object implementing the Terminal interface:
interface Terminal {
start(onInput: (data: string) => void, onResize: () => void): void;
stop(): void;
write(data: string): void;
get columns(): number;
get rows(): number;
moveBy(lines: number): void;
hideCursor(): void;
showCursor(): void;
clearLine(): void;
clearFromCursor(): void;
clearScreen(): void;
}Built-in implementations:
ProcessTerminal- Usesprocess.stdin/stdoutVirtualTerminal- For testing (uses@xterm/headless)
Example
See test/chat-simple.ts for a complete chat interface example with:
- Markdown messages with custom background colors
- Loading spinner during responses
- Editor with autocomplete and slash commands
- Spacers between messages
Run it:
npx tsx test/chat-simple.tsDevelopment
# Install dependencies (from monorepo root)
npm install
# Run type checking
npm run check
# Run the demo
npx tsx test/chat-simple.ts