a5d6041764
A Rust terminal chat client with OpenAI-compatible provider support, real-time SSE streaming, markdown rendering with syntax highlighting, and persistent chat history. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4.4 KiB
4.4 KiB
Meow - Rust Terminal AI Chat Client
A high-performance terminal UI (TUI) AI chat client written in Rust, supporting custom OpenAI-compatible providers (DeepSeek, Kimi, etc.).
Overview
Meow is a pure-text terminal chat application. It features:
- OpenAI-compatible provider abstraction layer
- Real-time streaming (SSE) chat output
- Markdown rendering with syntax highlighting
- Persistent chat history in JSON Lines format
- Mouse scroll support for message history
Tech Stack
| Component | Crate |
|---|---|
| Async Runtime | tokio |
| HTTP Client | reqwest (with streaming/SSE) |
| TUI Framework | ratatui + crossterm |
| Markdown Parser | pulldown-cmark |
| Syntax Highlighting | syntect (fancy-regex backend) |
| Serialization | serde + serde_json + toml |
| Configuration Path | dirs (cross-platform home dir) |
Architecture
Layered Design
TUI Layer (ratatui widgets)
├── chat.rs (implicit in app.rs)
├── model_select.rs (popup)
├── provider_config.rs (form)
├── input.rs (multi-line text area)
└── markdown.rs (rendering pipeline)
App Layer (state machine)
└── app.rs - coordinates all modules, event loop, async stream handling
Provider Layer
├── providers/mod.rs - Provider trait
└── providers/openai.rs - OpenAI-compatible implementation
Storage Layer
├── config.rs - ~/.meow/config.toml
├── storage.rs - ~/.meow/data/sessions/*.jsonl
└── message.rs - core data types
Data Flow
- User types message in input box
EntertriggersApp::send_message()- User message is saved to disk via
Storage::append_message() - A tokio task spawns to call
Provider::chat_stream() - SSE chunks flow back through
tokio::sync::mpsc::UnboundedChannel - Main event loop polls channel via
App::poll_stream()and redraws - When stream ends (
__STREAM_END__), the full assistant message is persisted
Project Structure
src/
├── main.rs # Entry point: initializes tokio, Tui, App, event loop
├── app.rs # App state machine: Chat / ModelSelect / ProviderConfig
├── config.rs # Config & ProviderConfig structs, load/save to ~/.meow/config.toml
├── message.rs # Role enum, Message, ChatSession
├── storage.rs # JSON Lines persistence for chat sessions
├── providers/
│ ├── mod.rs # Provider trait, Model struct, ProviderError
│ └── openai.rs # OpenAICompatibleProvider: /v1/models, /v1/chat/completions SSE
└── tui/
├── mod.rs # Tui struct: terminal init/restore, raw mode, panic hooks
├── input.rs # InputState: multi-line editor with cursor movement
└── markdown.rs # MarkdownRenderer: pulldown-cmark -> ratatui Lines + syntect highlighting
Configuration
Config is stored at ~/.meow/config.toml (cross-platform via dirs::home_dir()).
Example:
[[providers]]
name = "DeepSeek"
base_url = "https://api.deepseek.com"
api_key = "sk-..."
default_model = "deepseek-chat"
Chat sessions are stored as JSON Lines at ~/.meow/data/sessions/<uuid>.jsonl.
Keybindings
| Key | Action |
|---|---|
Enter |
Send message |
Shift+Enter |
Newline in input |
Ctrl+P |
Open model selector |
Ctrl+S |
Open provider config |
Ctrl+N |
Start new chat session |
Ctrl+B |
Toggle sidebar |
Ctrl+R |
Reset current chat |
Ctrl+C |
Stop streaming (while AI is typing) |
Ctrl+Q |
Quit |
Mouse Scroll |
Scroll message history |
Provider Protocol
The Provider trait (async_trait) defines:
name() -> &strconfig() -> &ProviderConfiglist_models() -> Result<Vec<Model>>chat_stream(model, messages) -> Result<BoxStream<Result<String>>>
OpenAICompatibleProvider implements this for any OpenAI API-compatible endpoint.
Markdown Support
Rendered elements:
- Bold / Italic
Inline code(dark background)- Code blocks with syntax highlighting (syntect themes)
- Bullet / numbered lists
- Blockquotes (│ prefix)
- Horizontal rules
First-Time Setup
On first run, if no providers exist, the app automatically enters the Add Provider form. Fill in name, base URL, and API key to get started.
Build
cargo build --release
The binary will be at target/release/meow.exe (Windows) or target/release/meow (Unix).