# Clojure TUI A terminal user interface framework for Clojure, inspired by [Bubbletea](https://github.com/charmbracelet/bubbletea) (Go). Build interactive CLI applications using the Elm Architecture with Hiccup-style views. ![Counter Example](assets/counter.gif) ## Features - **Elm Architecture** - Predictable state management with `init`, `update`, and `view` - **Hiccup Views** - Declarative UI with familiar Clojure syntax - **Async Commands** - Timers, batched operations, and custom async functions - **Rich Styling** - Colors (16, 256, true color), bold, italic, underline, and more - **Layout System** - Rows, columns, and boxes with borders - **Input Handling** - Full keyboard support including arrows, function keys, and modifiers - **Babashka Compatible** - Fast startup with Babashka or full Clojure ## Quick Start ### Installation Add to your `deps.edn`: ```clojure {:deps {io.github.yourname/clojure-tui {:git/tag "v0.1.0" :git/sha "..."}}} ``` ### Hello World ```clojure (ns myapp.core (:require [tui.core :as tui])) (defn update-fn [model msg] (if (tui/key= msg "q") [model tui/quit] [model nil])) (defn view [model _size] [:col [:text {:fg :cyan :bold true} "Hello, TUI!"] [:text {:fg :gray} "Press q to quit"]]) (tui/run {:init {} :update update-fn :view view}) ``` Output: ``` Hello, TUI! Press q to quit ``` ### Counter Example ```clojure (ns myapp.counter (:require [tui.core :as tui])) (defn update-fn [model msg] (cond (tui/key= msg "q") [model tui/quit] (tui/key= msg "k") [(inc model) nil] (tui/key= msg "j") [(dec model) nil] (tui/key= msg "r") [0 nil] :else [model nil])) (defn view [model _size] [:col [:box {:border :rounded :padding [0 2]} [:text {:fg :yellow :bold true} (str "Count: " model)]] [:text {:fg :gray} "j/k: change r: reset q: quit"]]) (tui/run {:init 0 :update update-fn :view view}) ``` Output: ``` ╭──────────────╮ │ Count: 0 │ ╰──────────────╯ j/k: change r: reset q: quit ``` ## Examples ### Counter Simple counter demonstrating basic Elm Architecture. ![Counter](assets/counter.gif) ```bash bb counter ``` ### Timer Countdown timer with pause/resume - demonstrates async commands. ![Timer](assets/timer.gif) ```bash bb timer ``` ### List Selection Multi-select list with cursor navigation. ![List Selection](assets/list.gif) ```bash bb list ``` ### Spinner Animated loading spinners with multiple styles. ![Spinner](assets/spinner.gif) ```bash bb spinner ``` ### Views State machine pattern with multiple views and navigation. ![Views](assets/views.gif) ```bash bb views ``` ### HTTP Async HTTP requests with loading states. ![HTTP](assets/http.gif) ```bash bb http ``` ### Running Examples Run examples with Babashka (recommended for fast startup): ```bash bb counter bb timer ``` Or with full Clojure: ```bash clojure -A:dev -M -m examples.counter ``` ## Documentation - [Getting Started](docs/getting-started.md) - Installation, first app, core concepts - [Hiccup Views](docs/hiccup-views.md) - View elements, styling, and layout - [API Reference](docs/api-reference.md) - Complete API documentation - [Examples](docs/examples.md) - Annotated example applications ## Architecture ``` View (hiccup) → Render (ANSI string) → Terminal (raw mode I/O) ↑ │ │ v Model ←──────── Update ←─────────── Input (key parsing) ``` The application follows the Elm Architecture: 1. **Model** - Your application state (any Clojure data structure) 2. **Update** - A pure function `(fn [model msg] [new-model cmd])` that handles messages 3. **View** - A pure function `(fn [model size] hiccup)` that renders the UI, where `size` is `{:width w :height h}` ## Hiccup View Elements | Element | Description | Example | |---------|-------------|---------| | `:text` | Styled text | `[:text {:fg :red :bold true} "Error"]` | | `:row` | Horizontal layout | `[:row "Left" "Right"]` | | `:col` | Vertical layout | `[:col "Line 1" "Line 2"]` | | `:box` | Bordered container | `[:box {:border :rounded} "Content"]` | | `:space` | Empty space | `[:space {:width 5}]` | ## Commands Commands are returned from your `update` function to trigger side effects: | Command | Description | |---------|-------------| | `tui/quit` | Exit the application | | `(tui/tick ms)` | Send `:tick` after ms milliseconds | | `(tui/batch c1 c2)` | Run commands in parallel | | `(tui/sequentially c1 c2)` | Run commands sequentially | | `(fn [] msg)` | Custom async function returning a message | ## Key Matching ```clojure (tui/key= msg "q") ;; Character (tui/key= msg :enter) ;; Special key (tui/key= msg :up) ;; Arrow (tui/key= msg [:ctrl \c]) ;; Control combo (tui/key= msg [:alt \x]) ;; Alt combo ``` ## Project Structure ``` src/ tui/ core.clj # Main runtime (Elm architecture + async commands) render.clj # Hiccup → ANSI terminal.clj # Raw mode, input/output input.clj # Key parsing ansi.clj # ANSI codes, colors examples/ counter.clj timer.clj list_selection.clj spinner.clj views.clj http.clj ``` ## License MIT