This commit is contained in:
2026-01-21 01:16:37 -05:00
commit a990076b03
17 changed files with 2713 additions and 0 deletions
+152
View File
@@ -0,0 +1,152 @@
# 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