Files
meow/CLAUDE.md
T
MangoFanFanw a5d6041764 Initial commit: Meow TUI AI chat client
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>
2026-05-11 13:18:07 +08:00

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

  1. User types message in input box
  2. Enter triggers App::send_message()
  3. User message is saved to disk via Storage::append_message()
  4. A tokio task spawns to call Provider::chat_stream()
  5. SSE chunks flow back through tokio::sync::mpsc::UnboundedChannel
  6. Main event loop polls channel via App::poll_stream() and redraws
  7. 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() -> &str
  • config() -> &ProviderConfig
  • list_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).