# Clojure TUI A Clojure TUI (Terminal User Interface) framework inspired by [Bubbletea](https://github.com/charmbracelet/bubbletea), using the Elm Architecture with Hiccup for views. ## Architecture ``` ┌─────────────────────────────────────┐ │ Hiccup DSL (view returns hiccup) │ ← User-facing API ├─────────────────────────────────────┤ │ Layout Engine (calculates sizes) │ ← Constraint solving ├─────────────────────────────────────┤ │ Render (hiccup → ANSI string) │ ← Colors, styles ├─────────────────────────────────────┤ │ Terminal (raw mode, input, output) │ ← Platform abstraction └─────────────────────────────────────┘ ``` ## The Elm Architecture Every app has three parts: ```clojure ;; Model - your application state (def initial-model {:count 0}) ;; Update - handle messages, return [new-model command] (defn update-model [model msg] (cond (tui/key= msg "q") [model tui/quit] (tui/key= msg :up) [(update model :count inc) nil] :else [model nil])) ;; View - render model as hiccup (defn view [{:keys [count]}] [:col [:text {:bold true} "Counter"] [:text (str "Count: " count)] [:text {:fg :gray} "Press up to increment, q to quit"]]) ``` ## Hiccup Elements | Element | Description | Attributes | |---------|-------------|------------| | `:text` | Styled text | `:fg` `:bg` `:bold` `:dim` `:italic` `:underline` `:inverse` | | `:row` | Horizontal layout | `:gap` | | `:col` | Vertical layout | `:gap` | | `:box` | Bordered container | `:border` `:title` `:padding` | | `:space` | Empty space | `:width` `:height` | ### Colors `:fg` and `:bg` accept: `:black` `:red` `:green` `:yellow` `:blue` `:magenta` `:cyan` `:white` `:gray` and bright variants. ### Borders `:border` accepts: `:rounded` `:single` `:double` `:heavy` `:ascii` ### Padding `:padding` accepts: `n` (all sides), `[v h]` (vertical, horizontal), or `[top right bottom left]` ## Running Examples ### With Clojure CLI ```bash # Counter - basic Elm architecture clojure -A:dev -M -m examples.counter # Timer - async commands (tick) clojure -A:dev -M -m examples.timer # List selection - cursor navigation, multi-select clojure -A:dev -M -m examples.list-selection # Spinner - animated loading clojure -A:dev -M -m examples.spinner # Views - multi-screen state machine clojure -A:dev -M -m examples.views # HTTP - async HTTP requests clojure -A:dev -M -m examples.http ``` ### With Babashka (limited) The full async runtime requires `core.async`. For Babashka, use `tui.simple`: ```clojure (require '[tui.simple :as tui]) ;; Same API, but no async commands (tick, http, etc.) (tui/run {:init initial-model :update update-model :view view}) ``` ## Built-in Commands | Command | Description | |---------|-------------| | `tui/quit` | Exit the program | | `(tui/tick ms)` | Send `:tick` message after ms | | `(tui/batch cmd1 cmd2)` | Run commands in parallel | | `(tui/sequentially cmd1 cmd2)` | Run commands in sequence | | `(fn [] msg)` | Custom async command | ## 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 # Full async runtime (core.async) simple.clj # Simple sync runtime (Babashka-compatible) 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 ``` ## Differences from Bubbletea | Bubbletea (Go) | Clojure TUI | |----------------|-------------| | String views | Hiccup views | | Lipgloss styling | Inline `:fg` `:bold` attrs | | `tea.Cmd` functions | Vector commands `[:tick 100]` | | Imperative builder | Declarative data | ## License MIT