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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user