Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Revised Tech Stack: TUI + WASM Focus

Overview

Given the TUI-first, dual-target architecture (native terminal + WASM web), the tech stack shifts to support:

  1. Ratatui ecosystem for terminal UI
  2. WASM compatibility for web deployment
  3. Shared core between targets
  4. Async runtime that works in both environments

Core Framework

ComponentChoiceRationale
TUI FrameworkRatatuiMature, fast, WASM-compatible via ratzilla
Terminal BackendcrosstermCross-platform, async-ready
Web BackendratzillaDOM-based rendering for WASM
Async Runtimetokio (native) / wasm-bindgen-futures (web)Best in class for each target
Error Handlingthiserror + anyhowErgonomic, works everywhere

Target-Specific Stacks

Native Terminal Stack

┌──────────────────────────────────────┐
│        Native Application            │
├──────────────────────────────────────┤
│  robit-tui                         │
│  • ratatui widgets                   │
│  • crossterm backend                 │
│  • tokio runtime                     │
├──────────────────────────────────────┤
│  robit-core (shared)               │
│  • Business logic                    │
│  • LLM abstractions                  │
│  • Tool system                       │
├──────────────────────────────────────┤
│  Platform APIs                       │
│  • reqwest (HTTP)                    │
│  • sqlx (database)                   │
│  • directories (config)              │
└──────────────────────────────────────┘

Key crates:

  • ratatui = "0.29" - TUI framework
  • crossterm = "0.28" - Terminal backend
  • tokio = { version = "1", features = ["full"] } - Async runtime
  • reqwest = { version = "0.12", features = ["json", "stream"] } - HTTP client
  • sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite"] } - Database
  • directories = "5" - Config/cache directories

WebAssembly Stack

┌──────────────────────────────────────┐
│        Web Application               │
├──────────────────────────────────────┤
│  robit-web                         │
│  • ratzilla backend                  │
│  • ratatui widgets (same as native)  │
│  • wasm-bindgen-futures              │
├──────────────────────────────────────┤
│  robit-core (shared)               │
│  • Same business logic               │
│  • Conditional compilation for I/O   │
├──────────────────────────────────────┤
│  Web APIs                            │
│  • web-sys (browser APIs)            │
│  • gloo (ergonomic wrappers)         │
│  • LocalStorage/IndexedDB            │
└──────────────────────────────────────┘

Key crates:

  • ratzilla = "0.3" - DOM-based ratatui backend
  • wasm-bindgen = "0.2" - Rust/JS interop
  • wasm-bindgen-futures = "0.4" - Async in WASM
  • web-sys = "0.3" - Browser API bindings
  • gloo = "0.11" - Ergonomic web APIs
  • js-sys = "0.3" - JavaScript bindings

Shared Core Stack

Components that work in both environments via conditional compilation:

Serialization

  • serde = { version = "1.0", features = ["derive"] } - Serialization
  • serde_json = "1.0" - JSON handling
  • toml = "0.8" - Config files

Async Utilities

  • futures = "0.3" - Async utilities
  • async-trait = "0.1" - Async traits
  • pin-project = "1" - Pin projections

LLM Integration

  • async-openai = "0.26" - OpenAI client (optional, native only)
  • reqwest for custom HTTP implementations
  • Custom trait system for multi-provider support

Text Processing

  • pulldown-cmark = "0.12" - Markdown parsing
  • syntect = "5" - Syntax highlighting (native)
  • tree-sitter = "0.24" - Language parsing (optional)

Validation

  • validator = { version = "0.19", features = ["derive"] } - Input validation
  • regex = "1" - Pattern matching

Workspace Structure

robit/
├── Cargo.toml                 # Workspace root
├── book.toml                  # Documentation
├── docs/                      # mdbook documentation
├── crates/
│   ├── robit-core/          # Shared business logic
│   │   ├── Cargo.toml
│   │   └── src/
│   │       ├── lib.rs
│   │       ├── conversation.rs
│   │       ├── llm/
│   │       │   ├── mod.rs
│   │       │   ├── provider.rs
│   │       │   └── openai.rs
│   │       ├── tools/
│   │       │   ├── mod.rs
│   │       │   └── registry.rs
│   │       └── config.rs
│   │
│   ├── robit-tui/           # Native terminal app
│   │   ├── Cargo.toml
│   │   └── src/
│   │       ├── main.rs
│   │       ├── app.rs
│   │       ├── ui/
│   │       │   ├── mod.rs
│   │       │   ├── conversation_view.rs
│   │       │   ├── input.rs
│   │       │   └── sidebar.rs
│   │       ├── event.rs
│   │       └── widgets/
│   │
│   ├── robit-web/           # WASM web app
│   │   ├── Cargo.toml
│   │   ├── index.html
│   │   └── src/
│   │       ├── main.rs
│   │       ├── app.rs
│   │       └── storage.rs
│   │
│   └── robit-cli/           # Command-line interface
│       ├── Cargo.toml
│       └── src/
│           └── main.rs
│
├── examples/                  # Usage examples
├── scripts/                   # Build scripts
└── tests/                     # Integration tests

Build Configuration

Native Build

# crates/robit-tui/Cargo.toml
[package]
name = "robit-tui"
version = "0.1.0"
edition = "2021"

[[bin]]
name = "robit"
path = "src/main.rs"

[dependencies]
robit-core = { path = "../robit-core" }

# TUI
crossterm = "0.28"
ratatui = "0.29"

# Async
tokio = { version = "1", features = ["full"] }
tokio-stream = "0.1"

# HTTP
reqwest = { version = "0.12", features = ["json", "stream", "rustls-tls"] }

# Database
sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite"] }

# Config
directories = "5"
config = "0.14"

# UI
syntect = "5"  # Syntax highlighting
unicode-width = "0.1"
textwrap = "0.16"

# CLI
clap = { version = "4.5", features = ["derive"] }

WASM Build

# crates/robit-web/Cargo.toml
[package]
name = "robit-web"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
robit-core = { path = "../robit-core" }

# WebAssembly
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
web-sys = "0.3"
gloo = "0.11"
console_error_panic_hook = "0.1"

# TUI (same widgets as native)
ratzilla = "0.3"
ratatui = "0.29"  # Shared version with native

# Async (browser-compatible)
wasm-bindgen-futures = "0.4"
futures = "0.3"

# Storage
gloo-storage = "0.3"
indexed_db_futures = "0.4"  # Optional IndexedDB wrapper

[dependencies.web-sys]
version = "0.3"
features = [
  "console",
  "Window",
  "Document",
  "Element",
  "HtmlElement",
  "Storage",
  "IdbDatabase",  # IndexedDB
]

Conditional Compilation

The shared core uses cfg attributes for platform-specific code:

#![allow(unused)]
fn main() {
// crates/robit-core/src/storage.rs

#[cfg(not(target_arch = "wasm32"))]
mod native {
    use sqlx::SqlitePool;
    
    pub struct DatabaseStorage {
        pool: SqlitePool,
    }
    
    impl Storage for DatabaseStorage {
        // SQLite implementation
    }
}

#[cfg(target_arch = "wasm32")]
mod web {
    use gloo_storage::{LocalStorage, Storage};
    
    pub struct WebStorage;
    
    impl Storage for WebStorage {
        // LocalStorage/IndexedDB implementation
    }
}

#[cfg(not(target_arch = "wasm32"))]
pub use native::DatabaseStorage as StorageImpl;

#[cfg(target_arch = "wasm32")]
pub use web::WebStorage as StorageImpl;
}

HTTP Client Abstraction

Different HTTP clients for each target:

#![allow(unused)]
fn main() {
// crates/robit-core/src/http.rs

#[cfg(not(target_arch = "wasm32"))]
pub type HttpClient = reqwest::Client;

#[cfg(target_arch = "wasm32"))]
pub type HttpClient = reqwest_wasm::Client;  // Or custom fetch-based client

// Unified interface
#[async_trait]
pub trait HttpClientExt {
    async fn get(&self, url: &str) -> Result<Response>;
    async fn post(&self, url: &str, body: Value) -> Result<Response>;
}
}

Development Tools

Native Development

# Run TUI app
cargo run -p robit-tui

# With logging
RUST_LOG=debug cargo run -p robit-tui

# Release build
cargo build -p robit-tui --release

Web Development

# Install trunk
cargo install trunk

# Add WASM target
rustup target add wasm32-unknown-unknown

# Serve with hot reload
cd crates/robit-web && trunk serve

# Build for production
trunk build --release

Cross-compilation Testing

# Test native
cargo test --workspace --exclude robit-web

# Check WASM compiles
cargo check -p robit-web --target wasm32-unknown-unknown

# Run WASM tests with wasm-pack
wasm-pack test --headless --firefox

Comparison: Before vs After

Original (API-first)

Axum (HTTP) → Core → Database
            ↓
      Sandboxed Executor

Revised (TUI + WASM)

┌─────────────────────────────────────────┐
│   Terminal (ratatui + crossterm)        │
│   Web (ratzilla + wasm-bindgen)         │
└─────────────────┬───────────────────────┘
                  │
         ┌────────▼────────┐
         │  Shared Core    │
         │  • Conversations│
         │  • LLM providers│
         │  • Tools        │
         └────────┬────────┘
                  │
    ┌─────────────┼─────────────┐
    │             │             │
┌───▼───┐  ┌──────▼──────┐  ┌───▼───┐
│SQLite │  │HTTP Client  │  │Config │
│(native)│  │(reqwest)    │  │(dirs) │
└───────┘  └─────────────┘  └───────┘

┌─── Web Alternative ───┐
│LocalStorage/IndexedDB │
│fetch API              │
└───────────────────────┘

Migration Path

If we later want to add the API/server approach:

  1. Extract server crate: harness-server with Axum
  2. Add client modes: TUI can connect to local or remote server
  3. Web app options: WASM (standalone) or web client (connects to server)

This keeps options open without over-engineering from the start.

Summary

The revised stack prioritizes:

  1. Developer experience: Ratatui provides excellent TUI ergonomics
  2. Code sharing: Single core works across targets
  3. Deployment flexibility: Native binary or static web hosting
  4. Performance: Native speed in terminal, near-native in browser

The WASM approach via ratzilla is particularly elegant because it renders the same ratatui widgets to DOM elements, giving us a true terminal aesthetic in the browser without maintaining separate UI code.