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:
@@ -0,0 +1,723 @@
|
||||
# API Reference
|
||||
|
||||
Complete API documentation for Clojure TUI.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [tui.core](#tuicore) - Full async runtime
|
||||
- [tui.simple](#tuisimple) - Sync runtime (Babashka)
|
||||
- [tui.render](#tuirender) - Hiccup rendering
|
||||
- [tui.input](#tuiinput) - Key input parsing
|
||||
- [tui.terminal](#tuiterminal) - Terminal control
|
||||
- [tui.ansi](#tuiansi) - ANSI escape codes
|
||||
|
||||
---
|
||||
|
||||
## tui.core
|
||||
|
||||
Full-featured async runtime using core.async. Use this when you need timers, async commands, or background operations.
|
||||
|
||||
### run
|
||||
|
||||
```clojure
|
||||
(run options)
|
||||
```
|
||||
|
||||
Run a TUI application synchronously (blocks until quit).
|
||||
|
||||
**Parameters:**
|
||||
|
||||
| Key | Type | Required | Description |
|
||||
|-----|------|----------|-------------|
|
||||
| `:init` | any | Yes | Initial model value |
|
||||
| `:update` | function | Yes | `(fn [model msg] [new-model cmd])` |
|
||||
| `:view` | function | Yes | `(fn [model] hiccup)` |
|
||||
| `:init-cmd` | command | No | Initial command to execute |
|
||||
| `:fps` | integer | No | Frames per second (default: 60) |
|
||||
| `:alt-screen` | boolean | No | Use alternate screen (default: true) |
|
||||
|
||||
**Returns:** Final model value after quit
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(require '[tui.core :as tui])
|
||||
|
||||
(tui/run {:init {:count 0}
|
||||
:update (fn [model msg]
|
||||
(if (tui/key= msg "q")
|
||||
[model tui/quit]
|
||||
[model nil]))
|
||||
:view (fn [{:keys [count]}]
|
||||
[:text (str "Count: " count)])
|
||||
:fps 30
|
||||
:alt-screen true})
|
||||
```
|
||||
|
||||
### quit
|
||||
|
||||
```clojure
|
||||
quit
|
||||
```
|
||||
|
||||
Constant value returned as a command to exit the application.
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(defn update-fn [model msg]
|
||||
(if (tui/key= msg "q")
|
||||
[model tui/quit]
|
||||
[model nil]))
|
||||
```
|
||||
|
||||
### tick
|
||||
|
||||
```clojure
|
||||
(tick ms)
|
||||
```
|
||||
|
||||
Create a command that sends `:tick` message after a delay.
|
||||
|
||||
**Parameters:**
|
||||
- `ms` - Delay in milliseconds
|
||||
|
||||
**Returns:** A tick command
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
;; Start a 1-second timer
|
||||
(defn update-fn [model msg]
|
||||
(case msg
|
||||
:tick [(update model :seconds inc) (tui/tick 1000)]
|
||||
[model nil]))
|
||||
|
||||
;; Initial tick
|
||||
(tui/run {:init {:seconds 0}
|
||||
:update update-fn
|
||||
:view view
|
||||
:init-cmd (tui/tick 1000)})
|
||||
```
|
||||
|
||||
### batch
|
||||
|
||||
```clojure
|
||||
(batch & cmds)
|
||||
```
|
||||
|
||||
Create a command that executes multiple commands in parallel.
|
||||
|
||||
**Parameters:**
|
||||
- `cmds` - Variable number of commands
|
||||
|
||||
**Returns:** A batch command
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
;; Execute two async operations in parallel
|
||||
(defn update-fn [model msg]
|
||||
(if (= msg :start)
|
||||
[model (tui/batch
|
||||
(fn [] (fetch-user))
|
||||
(fn [] (fetch-settings)))]
|
||||
[model nil]))
|
||||
```
|
||||
|
||||
### sequentially
|
||||
|
||||
```clojure
|
||||
(sequentially & cmds)
|
||||
```
|
||||
|
||||
Create a command that executes commands one after another.
|
||||
|
||||
**Parameters:**
|
||||
- `cmds` - Variable number of commands
|
||||
|
||||
**Returns:** A sequential command
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
;; First save, then notify
|
||||
(defn update-fn [model msg]
|
||||
(if (= msg :save)
|
||||
[model (tui/sequentially
|
||||
(fn [] (save-data model))
|
||||
(fn [] {:type :saved}))]
|
||||
[model nil]))
|
||||
```
|
||||
|
||||
### send-msg
|
||||
|
||||
```clojure
|
||||
(send-msg msg)
|
||||
```
|
||||
|
||||
Create a command that immediately sends a message.
|
||||
|
||||
**Parameters:**
|
||||
- `msg` - Message to send
|
||||
|
||||
**Returns:** A send-msg command
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(defn update-fn [model msg]
|
||||
(if (tui/key= msg "r")
|
||||
[{:status :resetting} (tui/send-msg :do-reset)]
|
||||
[model nil]))
|
||||
```
|
||||
|
||||
### key=
|
||||
|
||||
```clojure
|
||||
(key= msg pattern)
|
||||
```
|
||||
|
||||
Check if a key message matches a pattern.
|
||||
|
||||
**Parameters:**
|
||||
- `msg` - The message to check
|
||||
- `pattern` - Pattern to match (see below)
|
||||
|
||||
**Returns:** `true` if matches, `false` otherwise
|
||||
|
||||
**Pattern types:**
|
||||
|
||||
| Pattern | Matches |
|
||||
|---------|---------|
|
||||
| `"q"` | Character 'q' |
|
||||
| `:enter` | Enter key |
|
||||
| `:up` | Arrow up |
|
||||
| `[:ctrl \c]` | Ctrl+C |
|
||||
| `[:alt \x]` | Alt+X |
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(defn update-fn [model msg]
|
||||
(cond
|
||||
(tui/key= msg "q") [model tui/quit]
|
||||
(tui/key= msg :up) [(update model :y dec) nil]
|
||||
(tui/key= msg [:ctrl \c]) [model tui/quit]
|
||||
:else [model nil]))
|
||||
```
|
||||
|
||||
### key-str
|
||||
|
||||
```clojure
|
||||
(key-str msg)
|
||||
```
|
||||
|
||||
Convert a key message to a human-readable string.
|
||||
|
||||
**Parameters:**
|
||||
- `msg` - Key message
|
||||
|
||||
**Returns:** String representation
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(tui/key-str [:key {:char \a}]) ;; => "a"
|
||||
(tui/key-str [:key :up]) ;; => "up"
|
||||
(tui/key-str [:key {:ctrl true :char \c}]) ;; => "ctrl+c"
|
||||
```
|
||||
|
||||
### render
|
||||
|
||||
```clojure
|
||||
(render hiccup)
|
||||
```
|
||||
|
||||
Re-exported from `tui.render`. Render hiccup to ANSI string.
|
||||
|
||||
---
|
||||
|
||||
## tui.simple
|
||||
|
||||
Synchronous runtime compatible with Babashka. Same API as `tui.core` but without async commands.
|
||||
|
||||
### run
|
||||
|
||||
```clojure
|
||||
(run options)
|
||||
```
|
||||
|
||||
Run a TUI application synchronously.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
| Key | Type | Required | Description |
|
||||
|-----|------|----------|-------------|
|
||||
| `:init` | any | Yes | Initial model value |
|
||||
| `:update` | function | Yes | `(fn [model msg] [new-model cmd])` |
|
||||
| `:view` | function | Yes | `(fn [model] hiccup)` |
|
||||
| `:alt-screen` | boolean | No | Use alternate screen (default: true) |
|
||||
|
||||
**Returns:** Final model value after quit
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(require '[tui.simple :as tui])
|
||||
|
||||
(tui/run {:init 0
|
||||
:update (fn [model msg]
|
||||
(cond
|
||||
(tui/key= msg "q") [model tui/quit]
|
||||
(tui/key= msg "k") [(inc model) nil]
|
||||
:else [model nil]))
|
||||
:view (fn [count]
|
||||
[:text (str "Count: " count)])})
|
||||
```
|
||||
|
||||
### quit
|
||||
|
||||
Same as `tui.core/quit`.
|
||||
|
||||
### key=
|
||||
|
||||
Same as `tui.core/key=`.
|
||||
|
||||
### key-str
|
||||
|
||||
Same as `tui.core/key-str`.
|
||||
|
||||
### render
|
||||
|
||||
Re-exported from `tui.render`.
|
||||
|
||||
---
|
||||
|
||||
## tui.render
|
||||
|
||||
Converts hiccup data structures to ANSI-formatted strings.
|
||||
|
||||
### render
|
||||
|
||||
```clojure
|
||||
(render hiccup)
|
||||
(render hiccup ctx)
|
||||
```
|
||||
|
||||
Render hiccup to ANSI string.
|
||||
|
||||
**Parameters:**
|
||||
- `hiccup` - Hiccup data structure
|
||||
- `ctx` - Optional context map (for internal use)
|
||||
|
||||
**Returns:** String with ANSI escape codes
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(require '[tui.render :as r])
|
||||
|
||||
(r/render [:text {:fg :red :bold true} "Error!"])
|
||||
;; => "\e[31m\e[1mError!\e[0m"
|
||||
|
||||
(r/render [:col
|
||||
[:text "Line 1"]
|
||||
[:text "Line 2"]])
|
||||
;; => "Line 1\nLine 2"
|
||||
|
||||
(r/render [:box {:border :rounded} "Content"])
|
||||
;; => "╭─────────╮\n│Content │\n╰─────────╯"
|
||||
```
|
||||
|
||||
### text
|
||||
|
||||
```clojure
|
||||
(text & args)
|
||||
```
|
||||
|
||||
Helper function to create `:text` elements.
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(text "Hello") ;; => [:text "Hello"]
|
||||
(text {:fg :red} "Error") ;; => [:text {:fg :red} "Error"]
|
||||
```
|
||||
|
||||
### row
|
||||
|
||||
```clojure
|
||||
(row & args)
|
||||
```
|
||||
|
||||
Helper function to create `:row` elements.
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(row "A" "B" "C") ;; => [:row "A" "B" "C"]
|
||||
(row {:gap 2} "A" "B") ;; => [:row {:gap 2} "A" "B"]
|
||||
```
|
||||
|
||||
### col
|
||||
|
||||
```clojure
|
||||
(col & args)
|
||||
```
|
||||
|
||||
Helper function to create `:col` elements.
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(col "Line 1" "Line 2") ;; => [:col "Line 1" "Line 2"]
|
||||
(col {:gap 1} "A" "B") ;; => [:col {:gap 1} "A" "B"]
|
||||
```
|
||||
|
||||
### box
|
||||
|
||||
```clojure
|
||||
(box & args)
|
||||
```
|
||||
|
||||
Helper function to create `:box` elements.
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(box "Content") ;; => [:box "Content"]
|
||||
(box {:title "Info"} "Content") ;; => [:box {:title "Info"} "Content"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## tui.input
|
||||
|
||||
Parses raw terminal input into structured key messages.
|
||||
|
||||
### read-key
|
||||
|
||||
```clojure
|
||||
(read-key)
|
||||
```
|
||||
|
||||
Read a single key event from the terminal.
|
||||
|
||||
**Returns:** Key message vector `[:key ...]`
|
||||
|
||||
**Key message formats:**
|
||||
|
||||
```clojure
|
||||
;; Regular character
|
||||
[:key {:char \a}]
|
||||
|
||||
;; Special key
|
||||
[:key :enter]
|
||||
[:key :up]
|
||||
[:key :f1]
|
||||
|
||||
;; Control combination
|
||||
[:key {:ctrl true :char \c}]
|
||||
|
||||
;; Alt combination
|
||||
[:key {:alt true :char \x}]
|
||||
|
||||
;; Unknown sequence
|
||||
[:key :unknown "\e[xyz"]
|
||||
```
|
||||
|
||||
### Special Key Keywords
|
||||
|
||||
| Keyword | Key |
|
||||
|---------|-----|
|
||||
| `:up` | Arrow Up |
|
||||
| `:down` | Arrow Down |
|
||||
| `:left` | Arrow Left |
|
||||
| `:right` | Arrow Right |
|
||||
| `:home` | Home |
|
||||
| `:end` | End |
|
||||
| `:page-up` | Page Up |
|
||||
| `:page-down` | Page Down |
|
||||
| `:insert` | Insert |
|
||||
| `:delete` | Delete |
|
||||
| `:escape` | Escape |
|
||||
| `:tab` | Tab |
|
||||
| `:shift-tab` | Shift+Tab |
|
||||
| `:enter` | Enter |
|
||||
| `:backspace` | Backspace |
|
||||
| `:f1` - `:f12` | Function keys |
|
||||
|
||||
### key-match?
|
||||
|
||||
```clojure
|
||||
(key-match? msg pattern)
|
||||
```
|
||||
|
||||
Internal function used by `key=` to match patterns.
|
||||
|
||||
### key->str
|
||||
|
||||
```clojure
|
||||
(key->str msg)
|
||||
```
|
||||
|
||||
Convert key message to string representation.
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(key->str [:key {:char \a}]) ;; => "a"
|
||||
(key->str [:key :enter]) ;; => "enter"
|
||||
(key->str [:key {:ctrl true :char \c}]) ;; => "ctrl+c"
|
||||
(key->str [:key {:alt true :char \x}]) ;; => "alt+x"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## tui.terminal
|
||||
|
||||
Low-level terminal control functions.
|
||||
|
||||
### get-terminal-size
|
||||
|
||||
```clojure
|
||||
(get-terminal-size)
|
||||
```
|
||||
|
||||
Get the terminal dimensions.
|
||||
|
||||
**Returns:** Map with `:width` and `:height` keys
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(require '[tui.terminal :as term])
|
||||
|
||||
(term/get-terminal-size)
|
||||
;; => {:width 120 :height 40}
|
||||
```
|
||||
|
||||
### raw-mode!
|
||||
|
||||
```clojure
|
||||
(raw-mode!)
|
||||
```
|
||||
|
||||
Enter raw terminal mode. Disables echo and line buffering.
|
||||
|
||||
### restore!
|
||||
|
||||
```clojure
|
||||
(restore!)
|
||||
```
|
||||
|
||||
Restore terminal to original state.
|
||||
|
||||
### alt-screen!
|
||||
|
||||
```clojure
|
||||
(alt-screen!)
|
||||
```
|
||||
|
||||
Enter the alternate screen buffer.
|
||||
|
||||
### exit-alt-screen!
|
||||
|
||||
```clojure
|
||||
(exit-alt-screen!)
|
||||
```
|
||||
|
||||
Exit the alternate screen buffer.
|
||||
|
||||
### clear!
|
||||
|
||||
```clojure
|
||||
(clear!)
|
||||
```
|
||||
|
||||
Clear the screen and move cursor to home position.
|
||||
|
||||
### render!
|
||||
|
||||
```clojure
|
||||
(render! s)
|
||||
```
|
||||
|
||||
Render a string to the terminal.
|
||||
|
||||
**Parameters:**
|
||||
- `s` - String to render (typically from `tui.render/render`)
|
||||
|
||||
### Input Functions
|
||||
|
||||
```clojure
|
||||
(init-input!) ;; Initialize input reader
|
||||
(close-input!) ;; Close input reader
|
||||
(input-ready?) ;; Check if input available (non-blocking)
|
||||
(read-char) ;; Read single character (blocking)
|
||||
(read-available) ;; Read all available characters
|
||||
(read-char-timeout ms) ;; Read with timeout
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## tui.ansi
|
||||
|
||||
ANSI escape codes and text styling utilities.
|
||||
|
||||
### style
|
||||
|
||||
```clojure
|
||||
(style text & {:keys [fg bg bold dim italic underline inverse strike]})
|
||||
```
|
||||
|
||||
Apply multiple styles to text.
|
||||
|
||||
**Parameters:**
|
||||
- `text` - String to style
|
||||
- `:fg` - Foreground color
|
||||
- `:bg` - Background color
|
||||
- `:bold` - Boolean
|
||||
- `:dim` - Boolean
|
||||
- `:italic` - Boolean
|
||||
- `:underline` - Boolean
|
||||
- `:inverse` - Boolean
|
||||
- `:strike` - Boolean
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(require '[tui.ansi :as ansi])
|
||||
|
||||
(ansi/style "Error" :fg :red :bold true)
|
||||
(ansi/style "Warning" :fg :yellow :bg :black :underline true)
|
||||
```
|
||||
|
||||
### Color Functions
|
||||
|
||||
```clojure
|
||||
(fg color text) ;; Set foreground color
|
||||
(bg color text) ;; Set background color
|
||||
(fg-256 n text) ;; 256-color foreground (0-255)
|
||||
(bg-256 n text) ;; 256-color background (0-255)
|
||||
(fg-rgb r g b text) ;; True color foreground (24-bit)
|
||||
(bg-rgb r g b text) ;; True color background (24-bit)
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(ansi/fg :red "Error")
|
||||
(ansi/bg :yellow "Highlighted")
|
||||
(ansi/fg-256 208 "Orange")
|
||||
(ansi/fg-rgb 255 128 0 "True color orange")
|
||||
```
|
||||
|
||||
### String Utilities
|
||||
|
||||
```clojure
|
||||
(visible-length s) ;; Get visible length (excludes ANSI codes)
|
||||
(pad-right s width) ;; Pad with spaces on right
|
||||
(pad-left s width) ;; Pad with spaces on left
|
||||
(pad-center s width) ;; Center within width
|
||||
(truncate s max-width) ;; Truncate with ellipsis
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```clojure
|
||||
(ansi/visible-length "\e[31mRed\e[0m") ;; => 3
|
||||
(ansi/pad-right "Hi" 10) ;; => "Hi "
|
||||
(ansi/pad-center "Hi" 10) ;; => " Hi "
|
||||
(ansi/truncate "Hello World" 8) ;; => "Hello..."
|
||||
```
|
||||
|
||||
### Cursor Control
|
||||
|
||||
```clojure
|
||||
(cursor-to row col) ;; Move cursor to position (1-indexed)
|
||||
(cursor-up n) ;; Move cursor up n lines
|
||||
(cursor-down n) ;; Move cursor down n lines
|
||||
(cursor-forward n) ;; Move cursor right n columns
|
||||
(cursor-back n) ;; Move cursor left n columns
|
||||
```
|
||||
|
||||
### Constants
|
||||
|
||||
```clojure
|
||||
clear-screen ;; Clear entire screen
|
||||
clear-line ;; Clear current line
|
||||
clear-to-end ;; Clear from cursor to end of screen
|
||||
cursor-home ;; Move cursor to home (1,1)
|
||||
hide-cursor ;; Hide cursor
|
||||
show-cursor ;; Show cursor
|
||||
enter-alt-screen ;; Enter alternate screen buffer
|
||||
exit-alt-screen ;; Exit alternate screen buffer
|
||||
cursor-save ;; Save cursor position
|
||||
cursor-restore ;; Restore cursor position
|
||||
reset ;; Reset all attributes
|
||||
```
|
||||
|
||||
### Box Drawing Characters
|
||||
|
||||
```clojure
|
||||
ansi/box-chars
|
||||
;; => {:rounded {:tl "╭" :tr "╮" :bl "╰" :br "╯" :h "─" :v "│"}
|
||||
;; :single {:tl "┌" :tr "┐" :bl "└" :br "┘" :h "─" :v "│"}
|
||||
;; :double {:tl "╔" :tr "╗" :bl "╚" :br "╝" :h "═" :v "║"}
|
||||
;; :heavy {:tl "┏" :tr "┓" :bl "┗" :br "┛" :h "━" :v "┃"}
|
||||
;; :ascii {:tl "+" :tr "+" :bl "+" :br "+" :h "-" :v "|"}}
|
||||
```
|
||||
|
||||
### Color Maps
|
||||
|
||||
```clojure
|
||||
ansi/fg-colors ;; Map of color keywords to ANSI codes
|
||||
ansi/bg-colors ;; Map of color keywords to ANSI codes
|
||||
ansi/attrs ;; Map of attribute keywords to ANSI codes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Custom Async Commands
|
||||
|
||||
In `tui.core`, you can create custom async commands by returning functions:
|
||||
|
||||
```clojure
|
||||
;; Custom command that fetches data
|
||||
(defn fetch-data-cmd []
|
||||
(fn []
|
||||
;; This runs asynchronously
|
||||
(let [result (http/get "https://api.example.com/data")]
|
||||
{:type :data-loaded :data (:body result)})))
|
||||
|
||||
(defn update-fn [model msg]
|
||||
(cond
|
||||
(tui/key= msg "f")
|
||||
[{:loading true} (fetch-data-cmd)]
|
||||
|
||||
(= (:type msg) :data-loaded)
|
||||
[{:loading false :data (:data msg)} nil]
|
||||
|
||||
:else
|
||||
[model nil]))
|
||||
```
|
||||
|
||||
The function must:
|
||||
1. Take no arguments
|
||||
2. Return a message (any Clojure value)
|
||||
3. The returned message will be passed to your update function
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Namespace | Purpose |
|
||||
|-----------|---------|
|
||||
| `tui.core` | Full async runtime with all features |
|
||||
| `tui.simple` | Sync runtime for Babashka |
|
||||
| `tui.render` | Hiccup to ANSI rendering |
|
||||
| `tui.input` | Key input parsing |
|
||||
| `tui.terminal` | Low-level terminal control |
|
||||
| `tui.ansi` | ANSI codes and text utilities |
|
||||
Reference in New Issue
Block a user