235 lines
5.3 KiB
Markdown
235 lines
5.3 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.
|
|
|
|

|
|
|
|
## 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.
|
|
|
|

|
|
|
|
```bash
|
|
bb counter
|
|
```
|
|
|
|
### Timer
|
|
Countdown timer with pause/resume - demonstrates async commands.
|
|
|
|

|
|
|
|
```bash
|
|
bb timer
|
|
```
|
|
|
|
### List Selection
|
|
Multi-select list with cursor navigation.
|
|
|
|

|
|
|
|
```bash
|
|
bb list
|
|
```
|
|
|
|
### Spinner
|
|
Animated loading spinners with multiple styles.
|
|
|
|

|
|
|
|
```bash
|
|
bb spinner
|
|
```
|
|
|
|
### Views
|
|
State machine pattern with multiple views and navigation.
|
|
|
|

|
|
|
|
```bash
|
|
bb views
|
|
```
|
|
|
|
### HTTP
|
|
Async HTTP requests with loading states.
|
|
|
|

|
|
|
|
```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/after ms msg)` | Send `msg` 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
|
|
|