# 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