mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 23:01:30 +00:00
270 lines
6.4 KiB
Markdown
270 lines
6.4 KiB
Markdown
# @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
|
|
|
|
```typescript
|
|
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.
|
|
|
|
```typescript
|
|
const tui = new TUI(terminal);
|
|
tui.addChild(component);
|
|
tui.removeChild(component);
|
|
tui.start();
|
|
tui.stop();
|
|
tui.requestRender(); // Request a re-render
|
|
```
|
|
|
|
### Component Interface
|
|
|
|
All components implement:
|
|
|
|
```typescript
|
|
interface Component {
|
|
render(width: number): string[];
|
|
handleInput?(data: string): void;
|
|
}
|
|
```
|
|
|
|
## Built-in Components
|
|
|
|
### Container
|
|
|
|
Groups child components.
|
|
|
|
```typescript
|
|
const container = new Container();
|
|
container.addChild(component);
|
|
container.removeChild(component);
|
|
```
|
|
|
|
### Text
|
|
|
|
Displays multi-line text with word wrapping and padding.
|
|
|
|
```typescript
|
|
const text = new Text("Hello World", paddingX, paddingY); // defaults: 1, 1
|
|
text.setText("Updated text");
|
|
```
|
|
|
|
### Input
|
|
|
|
Single-line text input with horizontal scrolling.
|
|
|
|
```typescript
|
|
const input = new Input();
|
|
input.onSubmit = (value) => console.log(value);
|
|
input.setValue("initial");
|
|
```
|
|
|
|
### Editor
|
|
|
|
Multi-line text editor with autocomplete, file completion, and paste handling.
|
|
|
|
```typescript
|
|
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` - Submit
|
|
- `Shift+Enter`, `Ctrl+Enter`, or `Alt+Enter` - New line (terminal-dependent, Alt+Enter most reliable)
|
|
- `Tab` - Autocomplete
|
|
- `Ctrl+K` - Delete line
|
|
- `Ctrl+A` / `Ctrl+E` - Line start/end
|
|
- Arrow keys, Backspace, Delete work as expected
|
|
|
|
### Markdown
|
|
|
|
Renders markdown with syntax highlighting and optional background colors.
|
|
|
|
```typescript
|
|
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.
|
|
|
|
```typescript
|
|
const loader = new Loader(tui, "Loading...");
|
|
loader.start();
|
|
loader.stop();
|
|
```
|
|
|
|
### SelectList
|
|
|
|
Interactive selection list with keyboard navigation.
|
|
|
|
```typescript
|
|
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 items
|
|
```
|
|
|
|
**Controls:**
|
|
- Arrow keys: Navigate
|
|
- Enter or Tab: Select
|
|
- Escape: Cancel
|
|
|
|
### Spacer
|
|
|
|
Empty lines for vertical spacing.
|
|
|
|
```typescript
|
|
const spacer = new Spacer(2); // 2 empty lines (default: 1)
|
|
```
|
|
|
|
## Autocomplete
|
|
|
|
### CombinedAutocompleteProvider
|
|
|
|
Supports both slash commands and file paths.
|
|
|
|
```typescript
|
|
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 `Tab` for file path completion
|
|
- Works with `~/`, `./`, `../`, and `@` prefix
|
|
- Filters to attachable files for `@` prefix
|
|
|
|
## Differential Rendering
|
|
|
|
The TUI uses three rendering strategies:
|
|
|
|
1. **First Render**: Output all lines without clearing scrollback
|
|
2. **Width Changed or Change Above Viewport**: Clear screen and full re-render
|
|
3. **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:
|
|
|
|
```typescript
|
|
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` - Uses `process.stdin/stdout`
|
|
- `VirtualTerminal` - 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:
|
|
```bash
|
|
npx tsx test/chat-simple.ts
|
|
```
|
|
|
|
## Development
|
|
|
|
```bash
|
|
# Install dependencies (from monorepo root)
|
|
npm install
|
|
|
|
# Run type checking
|
|
npm run check
|
|
|
|
# Run the demo
|
|
npx tsx test/chat-simple.ts
|
|
```
|