Files
clojure-tui/README.md
Adam Jeniski dab0a27e4d add comprehensive documentation for external users
Includes getting started guide, hiccup views reference,
full API documentation, and annotated example walkthroughs
with ASCII output examples.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 11:37:16 -05:00

218 lines
5.8 KiB
Markdown

# 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: 42 │ │
│ ╰────────────────────╯ │
│ │
│ j/k: change r: reset q: quit │
│ │
╰──────────────────────────────────────╯
```
## Features
- **Elm Architecture** - Predictable state management with `init`, `update`, and `view`
- **Hiccup Views** - Declarative UI with familiar Clojure syntax
- **Two Runtimes** - Full async (`tui.core`) or simple sync (`tui.simple` for Babashka)
- **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
## 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.simple :as tui]))
(defn update-fn [model msg]
(if (tui/key= msg "q")
[model tui/quit]
[model nil]))
(defn view [model]
[: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.simple :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]
[: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
```
## Running Examples
```bash
# With Babashka (simple sync runtime)
bb counter
bb timer
bb list
bb spinner
bb views
bb http
# With Clojure (full async support)
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] hiccup)` that renders the UI
## Two Runtimes
### `tui.simple` - Synchronous (Babashka-compatible)
Best for simple applications. No async support, but works with Babashka.
```clojure
(require '[tui.simple :as tui])
(tui/run {:init model :update update-fn :view view-fn})
```
### `tui.core` - Asynchronous (Full Clojure)
Full-featured runtime with async commands like timers and batched operations.
```clojure
(require '[tui.core :as tui])
(defn update-fn [model msg]
(case msg
:tick [(update model :seconds inc) (tui/tick 1000)]
[model nil]))
(tui/run {:init {:seconds 0}
:update update-fn
:view view-fn
:init-cmd (tui/tick 1000)})
```
## 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 # 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
```
## License
MIT