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>
This commit is contained in:
2026-01-21 11:37:16 -05:00
parent 9fe0ac2c6e
commit dab0a27e4d
5 changed files with 2712 additions and 97 deletions
+162 -97
View File
@@ -1,112 +1,186 @@
# 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.
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
```
┌─────────────────────────────────────┐
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
└─────────────────────────────────────┘
View (hiccup) → Render (ANSI string) → Terminal (raw mode I/O)
│ v
Model ←──────── Update ←─────────── Input (key parsing)
```
## The Elm Architecture
The application follows the Elm Architecture:
Every app has three parts:
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
```clojure
;; Model - your application state
(def initial-model {:count 0})
## Two Runtimes
;; 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]))
### `tui.simple` - Synchronous (Babashka-compatible)
;; 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`:
Best for simple applications. No async support, but works with Babashka.
```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})
(tui/run {:init model :update update-fn :view view-fn})
```
## Built-in Commands
### `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 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 |
| `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
@@ -138,15 +212,6 @@ examples/
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