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,542 @@
|
||||
# Hiccup Views
|
||||
|
||||
Clojure TUI uses Hiccup-style syntax for declarative UI definitions. Views are pure functions that return nested data structures representing the UI.
|
||||
|
||||
## Basic Syntax
|
||||
|
||||
Views use vectors with keywords as element tags:
|
||||
|
||||
```clojure
|
||||
[:element-type {attributes} children...]
|
||||
```
|
||||
|
||||
Examples:
|
||||
```clojure
|
||||
[:text "Hello"] ;; Simple text
|
||||
[:text {:fg :red} "Error"] ;; Text with attributes
|
||||
[:col [:text "Line 1"] [:text "Line 2"]] ;; Nested elements
|
||||
```
|
||||
|
||||
## Elements
|
||||
|
||||
### `:text` - Styled Text
|
||||
|
||||
The basic building block for displaying text.
|
||||
|
||||
```clojure
|
||||
;; Simple text
|
||||
[:text "Hello, World!"]
|
||||
|
||||
;; Styled text
|
||||
[:text {:fg :cyan :bold true} "Important"]
|
||||
|
||||
;; Multiple style attributes
|
||||
[:text {:fg :white :bg :red :bold true :underline true} "Alert!"]
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
Hello, World!
|
||||
Important
|
||||
Alert!
|
||||
```
|
||||
|
||||
**Attributes:**
|
||||
|
||||
| Attribute | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `:fg` | keyword/int | Foreground color |
|
||||
| `:bg` | keyword/int | Background color |
|
||||
| `:bold` | boolean | Bold text |
|
||||
| `:dim` | boolean | Dimmed text |
|
||||
| `:italic` | boolean | Italic text |
|
||||
| `:underline` | boolean | Underlined text |
|
||||
| `:inverse` | boolean | Swap fg/bg colors |
|
||||
| `:strike` | boolean | Strikethrough text |
|
||||
|
||||
### `:row` - Horizontal Layout
|
||||
|
||||
Arranges children horizontally (left to right).
|
||||
|
||||
```clojure
|
||||
;; Basic row
|
||||
[:row "Left" "Middle" "Right"]
|
||||
|
||||
;; Row with gap
|
||||
[:row {:gap 2} "A" "B" "C"]
|
||||
|
||||
;; Nested elements in row
|
||||
[:row
|
||||
[:text {:fg :green} "Status:"]
|
||||
[:text {:bold true} "OK"]]
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
LeftMiddleRight
|
||||
|
||||
A B C
|
||||
|
||||
Status:OK
|
||||
```
|
||||
|
||||
**Attributes:**
|
||||
|
||||
| Attribute | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `:gap` | integer | Spaces between children (default: 0) |
|
||||
|
||||
### `:col` - Vertical Layout
|
||||
|
||||
Arranges children vertically (top to bottom).
|
||||
|
||||
```clojure
|
||||
;; Basic column
|
||||
[:col "Line 1" "Line 2" "Line 3"]
|
||||
|
||||
;; Column with gap
|
||||
[:col {:gap 1}
|
||||
[:text "Section 1"]
|
||||
[:text "Section 2"]]
|
||||
|
||||
;; Nested layouts
|
||||
[:col
|
||||
[:text {:bold true} "Header"]
|
||||
[:row "Col A" "Col B" "Col C"]
|
||||
[:text {:fg :gray} "Footer"]]
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
Line 1
|
||||
Line 2
|
||||
Line 3
|
||||
|
||||
Section 1
|
||||
|
||||
Section 2
|
||||
|
||||
Header
|
||||
Col ACol BCol C
|
||||
Footer
|
||||
```
|
||||
|
||||
**Attributes:**
|
||||
|
||||
| Attribute | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `:gap` | integer | Blank lines between children (default: 0) |
|
||||
|
||||
### `:box` - Bordered Container
|
||||
|
||||
Wraps content in a bordered box.
|
||||
|
||||
```clojure
|
||||
;; Simple box
|
||||
[:box "Content"]
|
||||
|
||||
;; Box with title
|
||||
[:box {:title "Settings"} "Options go here"]
|
||||
|
||||
;; Box with padding
|
||||
[:box {:padding 1} "Padded content"]
|
||||
|
||||
;; Box with custom border style
|
||||
[:box {:border :double} "Important!"]
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
╭─────────╮
|
||||
│Content │
|
||||
╰─────────╯
|
||||
|
||||
╭─Settings─╮
|
||||
│Options go here│
|
||||
╰──────────╯
|
||||
|
||||
╭──────────────────╮
|
||||
│ │
|
||||
│ Padded content │
|
||||
│ │
|
||||
╰──────────────────╯
|
||||
|
||||
╔═══════════╗
|
||||
║Important! ║
|
||||
╚═══════════╝
|
||||
```
|
||||
|
||||
**Attributes:**
|
||||
|
||||
| Attribute | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `:border` | keyword | Border style (see below) |
|
||||
| `:title` | string | Title in top border |
|
||||
| `:padding` | int/vec | Inner padding (see below) |
|
||||
| `:width` | integer | Fixed width |
|
||||
|
||||
**Border Styles:**
|
||||
|
||||
| Style | Characters | Example |
|
||||
|-------|------------|---------|
|
||||
| `:rounded` | `╭╮╰╯─│` | `╭───╮` (default) |
|
||||
| `:single` | `┌┐└┘─│` | `┌───┐` |
|
||||
| `:double` | `╔╗╚╝═║` | `╔═══╗` |
|
||||
| `:heavy` | `┏┓┗┛━┃` | `┏━━━┓` |
|
||||
| `:ascii` | `++--\|` | `+---+` |
|
||||
|
||||
**Padding:**
|
||||
|
||||
```clojure
|
||||
;; All sides
|
||||
[:box {:padding 2} "Content"]
|
||||
|
||||
;; Vertical and horizontal [v h]
|
||||
[:box {:padding [1 2]} "Content"]
|
||||
|
||||
;; Individual [top right bottom left]
|
||||
[:box {:padding [1 2 1 2]} "Content"]
|
||||
```
|
||||
|
||||
### `:space` - Empty Space
|
||||
|
||||
Creates empty space for layout purposes.
|
||||
|
||||
```clojure
|
||||
;; Default 1x1 space
|
||||
[:space]
|
||||
|
||||
;; Horizontal space
|
||||
[:row "Left" [:space {:width 10}] "Right"]
|
||||
|
||||
;; Vertical space
|
||||
[:col "Top" [:space {:height 3}] "Bottom"]
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
Left Right
|
||||
|
||||
Top
|
||||
|
||||
|
||||
|
||||
Bottom
|
||||
```
|
||||
|
||||
**Attributes:**
|
||||
|
||||
| Attribute | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `:width` | integer | Width in characters (default: 1) |
|
||||
| `:height` | integer | Height in lines (default: 1) |
|
||||
|
||||
## Colors
|
||||
|
||||
### Named Colors
|
||||
|
||||
Basic 16-color palette supported by all terminals:
|
||||
|
||||
| Color | Keyword | Bright Version |
|
||||
|-------|---------|----------------|
|
||||
| Black | `:black` | `:bright-black` |
|
||||
| Red | `:red` | `:bright-red` |
|
||||
| Green | `:green` | `:bright-green` |
|
||||
| Yellow | `:yellow` | `:bright-yellow` |
|
||||
| Blue | `:blue` | `:bright-blue` |
|
||||
| Magenta | `:magenta` | `:bright-magenta` |
|
||||
| Cyan | `:cyan` | `:bright-cyan` |
|
||||
| White | `:white` | `:bright-white` |
|
||||
| Default | `:default` | - |
|
||||
|
||||
Aliases: `:gray` and `:grey` map to `:bright-black`
|
||||
|
||||
```clojure
|
||||
[:text {:fg :red} "Error"]
|
||||
[:text {:fg :bright-green} "Success"]
|
||||
[:text {:bg :blue :fg :white} "Highlighted"]
|
||||
```
|
||||
|
||||
### 256 Colors
|
||||
|
||||
Use integers 0-255 for extended color support:
|
||||
|
||||
```clojure
|
||||
[:text {:fg 208} "Orange (256-color)"]
|
||||
[:text {:bg 236} "Dark gray background"]
|
||||
```
|
||||
|
||||
**Color ranges:**
|
||||
- 0-7: Standard colors
|
||||
- 8-15: Bright colors
|
||||
- 16-231: 6x6x6 color cube
|
||||
- 232-255: Grayscale (dark to light)
|
||||
|
||||
### True Color (24-bit)
|
||||
|
||||
For true color, use the `tui.ansi` namespace directly:
|
||||
|
||||
```clojure
|
||||
(require '[tui.ansi :as ansi])
|
||||
|
||||
[:text (ansi/fg-rgb 255 128 0 "Orange text")]
|
||||
[:text (ansi/bg-rgb 30 30 30 "Dark background")]
|
||||
```
|
||||
|
||||
## Text Styles
|
||||
|
||||
Combine multiple styles:
|
||||
|
||||
```clojure
|
||||
[:text {:bold true :underline true} "Bold and underlined"]
|
||||
[:text {:fg :red :bold true :inverse true} "Inverted error"]
|
||||
[:text {:dim true :italic true} "Subtle italic"]
|
||||
```
|
||||
|
||||
**Available styles:**
|
||||
|
||||
| Style | Attribute | Description |
|
||||
|-------|-----------|-------------|
|
||||
| Bold | `:bold true` | Heavier font weight |
|
||||
| Dim | `:dim true` | Lighter/faded text |
|
||||
| Italic | `:italic true` | Slanted text |
|
||||
| Underline | `:underline true` | Line under text |
|
||||
| Inverse | `:inverse true` | Swap foreground/background |
|
||||
| Strikethrough | `:strike true` | Line through text |
|
||||
|
||||
## Layout Examples
|
||||
|
||||
### Two-Column Layout
|
||||
|
||||
```clojure
|
||||
[:row {:gap 4}
|
||||
[:col
|
||||
[:text {:bold true} "Left Column"]
|
||||
[:text "Item 1"]
|
||||
[:text "Item 2"]]
|
||||
[:col
|
||||
[:text {:bold true} "Right Column"]
|
||||
[:text "Item A"]
|
||||
[:text "Item B"]]]
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
Left Column Right Column
|
||||
Item 1 Item A
|
||||
Item 2 Item B
|
||||
```
|
||||
|
||||
### Nested Boxes
|
||||
|
||||
```clojure
|
||||
[:box {:title "Outer" :padding 1}
|
||||
[:row {:gap 2}
|
||||
[:box {:border :single :title "Box A"}
|
||||
[:text "Content A"]]
|
||||
[:box {:border :single :title "Box B"}
|
||||
[:text "Content B"]]]]
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
╭─Outer────────────────────────────╮
|
||||
│ │
|
||||
│ ┌─Box A─────┐ ┌─Box B─────┐ │
|
||||
│ │Content A │ │Content B │ │
|
||||
│ └───────────┘ └───────────┘ │
|
||||
│ │
|
||||
╰──────────────────────────────────╯
|
||||
```
|
||||
|
||||
### Status Bar
|
||||
|
||||
```clojure
|
||||
[:col
|
||||
[:box {:border :rounded :padding [0 1]}
|
||||
[:text {:bold true} "My Application"]]
|
||||
[:space {:height 1}]
|
||||
[:text "Main content here..."]
|
||||
[:space {:height 1}]
|
||||
[:row {:gap 2}
|
||||
[:text {:fg :gray} "Status: Ready"]
|
||||
[:text {:fg :gray} "|"]
|
||||
[:text {:fg :gray} "Press q to quit"]]]
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
╭──────────────────╮
|
||||
│ My Application │
|
||||
╰──────────────────╯
|
||||
|
||||
Main content here...
|
||||
|
||||
Status: Ready | Press q to quit
|
||||
```
|
||||
|
||||
### Menu with Selection
|
||||
|
||||
```clojure
|
||||
(defn menu-item [label selected?]
|
||||
[:row
|
||||
[:text (if selected? "> " " ")]
|
||||
[:text {:fg (if selected? :cyan :white)
|
||||
:bold selected?}
|
||||
label]])
|
||||
|
||||
(defn view [{:keys [items cursor]}]
|
||||
[:col
|
||||
[:text {:bold true} "Select an option:"]
|
||||
[:space {:height 1}]
|
||||
[:col {:gap 0}
|
||||
(for [[idx item] (map-indexed vector items)]
|
||||
(menu-item item (= idx cursor)))]])
|
||||
```
|
||||
|
||||
**Output (cursor on second item):**
|
||||
```
|
||||
Select an option:
|
||||
|
||||
First Option
|
||||
> Second Option
|
||||
Third Option
|
||||
```
|
||||
|
||||
### Progress Indicator
|
||||
|
||||
```clojure
|
||||
(defn progress-bar [percent width]
|
||||
(let [filled (int (* width (/ percent 100)))
|
||||
empty (- width filled)]
|
||||
[:row
|
||||
[:text "["]
|
||||
[:text {:fg :green} (apply str (repeat filled "="))]
|
||||
[:text {:fg :gray} (apply str (repeat empty "-"))]
|
||||
[:text "]"]
|
||||
[:text " "]
|
||||
[:text (str percent "%")]]))
|
||||
|
||||
(defn view [{:keys [progress]}]
|
||||
[:col
|
||||
[:text "Downloading..."]
|
||||
(progress-bar progress 20)])
|
||||
```
|
||||
|
||||
**Output (at 65%):**
|
||||
```
|
||||
Downloading...
|
||||
[=============-------] 65%
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
The `tui.render` namespace provides helper functions:
|
||||
|
||||
```clojure
|
||||
(require '[tui.render :refer [text row col box]])
|
||||
|
||||
;; These are equivalent:
|
||||
[:text {:fg :red} "Error"]
|
||||
(text {:fg :red} "Error")
|
||||
|
||||
[:row {:gap 2} "A" "B"]
|
||||
(row {:gap 2} "A" "B")
|
||||
|
||||
[:col [:text "Line 1"] [:text "Line 2"]]
|
||||
(col (text "Line 1") (text "Line 2"))
|
||||
|
||||
[:box {:title "Info"} "Content"]
|
||||
(box {:title "Info"} "Content")
|
||||
```
|
||||
|
||||
## Conditional Rendering
|
||||
|
||||
Use standard Clojure conditionals:
|
||||
|
||||
```clojure
|
||||
(defn view [{:keys [loading? error data]}]
|
||||
[:col
|
||||
[:text {:bold true} "Status"]
|
||||
(cond
|
||||
loading?
|
||||
[:text {:fg :yellow} "Loading..."]
|
||||
|
||||
error
|
||||
[:text {:fg :red} (str "Error: " error)]
|
||||
|
||||
:else
|
||||
[:text {:fg :green} (str "Data: " data)])])
|
||||
```
|
||||
|
||||
## Dynamic Views with `for`
|
||||
|
||||
Generate repeated elements:
|
||||
|
||||
```clojure
|
||||
(defn view [{:keys [items selected]}]
|
||||
[:col
|
||||
(for [[idx item] (map-indexed vector items)]
|
||||
[:row
|
||||
[:text (if (= idx selected) "> " " ")]
|
||||
[:text {:fg (if (= idx selected) :cyan :white)} item]])])
|
||||
```
|
||||
|
||||
## String Shortcuts
|
||||
|
||||
Plain strings are automatically wrapped in `:text`:
|
||||
|
||||
```clojure
|
||||
;; These are equivalent:
|
||||
[:col "Line 1" "Line 2"]
|
||||
[:col [:text "Line 1"] [:text "Line 2"]]
|
||||
|
||||
;; In rows too:
|
||||
[:row "A" "B" "C"]
|
||||
[:row [:text "A"] [:text "B"] [:text "C"]]
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Styled Labels
|
||||
|
||||
```clojure
|
||||
(defn label [text]
|
||||
[:text {:fg :gray} (str text ": ")])
|
||||
|
||||
(defn value [text]
|
||||
[:text {:fg :white :bold true} text])
|
||||
|
||||
[:row (label "Name") (value "John")]
|
||||
```
|
||||
|
||||
### Conditional Styling
|
||||
|
||||
```clojure
|
||||
(defn status-text [status]
|
||||
[:text {:fg (case status
|
||||
:ok :green
|
||||
:warning :yellow
|
||||
:error :red
|
||||
:white)
|
||||
:bold (= status :error)}
|
||||
(name status)])
|
||||
```
|
||||
|
||||
### Reusable Components
|
||||
|
||||
```clojure
|
||||
(defn card [{:keys [title]} & children]
|
||||
[:box {:border :rounded :title title :padding [0 1]}
|
||||
(into [:col] children)])
|
||||
|
||||
;; Usage
|
||||
(card {:title "User Info"}
|
||||
[:row (label "Name") (value "Alice")]
|
||||
[:row (label "Email") (value "alice@example.com")])
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [API Reference](api-reference.md) - Complete API documentation
|
||||
- [Examples](examples.md) - Full example applications
|
||||
Reference in New Issue
Block a user