Paragraph
Text display with optional wrapping, alignment, and scrolling.
%Paragraph{
text: "Hello, world!",
style: %Style{fg: :green},
alignment: :center, # :left | :center | :right
wrap: true, # enable word wrapping
scroll: {0, 0}, # {vertical, horizontal} offset
block: %Block{title: " Info ", borders: [:all]}
}Block
Container with borders, title, and padding. Wraps other widgets via their :block field.
%Block{
title: " My Panel ",
borders: [:all], # [:all] | [:top, :bottom, :left, :right]
border_type: :rounded, # :plain | :rounded | :double | :thick
border_style: %Style{fg: :cyan},
style: %Style{},
padding: {0, 0, 0, 0} # {top, right, bottom, left}
}List
Selectable list of text items with highlight support.
%List{
items: ["Item 1", "Item 2", "Item 3"],
selected: 0,
highlight_style: %Style{fg: :yellow, modifiers: [:bold]},
highlight_symbol: ">> ",
style: %Style{},
block: %Block{title: " Menu ", borders: [:all]}
}Table
Multi-column data grid with configurable column widths.
%Table{
header: ["Name", "Status", "Score"],
rows: [
["Alice", "Active", "95"],
["Bob", "Idle", "87"]
],
widths: [{:percentage, 40}, {:percentage, 30}, {:percentage, 30}],
selected: 0,
highlight_style: %Style{fg: :yellow},
column_spacing: 1,
block: %Block{title: " Scores ", borders: [:all]}
}Widths accept {:length, n}, {:percentage, n}, {:min, n}, {:max, n}, {:ratio, n, d}.
Gauge
Filled progress bar with optional label.
%Gauge{
ratio: 0.65, # 0.0 to 1.0
label: "65%",
gauge_style: %Style{fg: :green},
block: %Block{title: " Progress ", borders: [:all]}
}LineGauge
Horizontal progress bar with filled/unfilled styling.
%LineGauge{
ratio: 0.4,
label: "Loading...",
filled_style: %Style{fg: :blue},
unfilled_style: %Style{fg: :dark_gray},
block: %Block{borders: [:all]}
}Tabs
Tab navigation bar with selected highlight.
%Tabs{
titles: ["Overview", "Details", "Settings"],
selected: 0,
highlight_style: %Style{fg: :yellow, modifiers: [:bold]},
divider: " | ",
padding: {1, 1},
block: %Block{borders: [:bottom]}
}Scrollbar
Decorative scrollbar indicator for scrollable content.
%Scrollbar{
orientation: :vertical_right, # :vertical_right | :vertical_left | :horizontal_bottom | :horizontal_top
content_length: 100,
position: 25,
viewport_content_length: 20
}Checkbox
Boolean toggle with customizable symbols.
%Checkbox{
label: "Enable notifications",
checked: true,
style: %Style{},
checked_style: %Style{fg: :green},
checked_symbol: "[x]",
unchecked_symbol: "[ ]",
block: nil
}TextInput
Single-line text editor. Requires a NIF-managed state reference.
# Create state once, then reuse across renders
state = ExRatatui.text_input_new()
%TextInput{
state: state,
placeholder: "Type here...",
style: %Style{fg: :white},
cursor_style: %Style{bg: :white, fg: :black},
placeholder_style: %Style{fg: :dark_gray},
block: %Block{title: " Input ", borders: [:all]}
}
# Handle key events
ExRatatui.text_input_handle_key(state, key_event)
# Read current value
value = ExRatatui.text_input_get_value(state)Textarea
Multi-line text editor with cursor line highlighting.
state = ExRatatui.textarea_new()
%Textarea{
state: state,
placeholder: "Enter message...",
style: %Style{fg: :white},
cursor_style: %Style{bg: :white, fg: :black},
cursor_line_style: %Style{bg: {40, 40, 40}},
line_number_style: %Style{fg: :dark_gray}, # nil to hide line numbers
placeholder_style: %Style{fg: :dark_gray},
block: %Block{title: " Editor ", borders: [:all]}
}
# Handle key events (Enter inserts newline, Ctrl+Enter submits)
ExRatatui.textarea_handle_key(state, key_event)
# Read current value
value = ExRatatui.textarea_get_value(state)Clear
Fills the area with blank cells. Useful for clearing regions before redrawing.
%Clear{}Markdown
Renders markdown text with syntax highlighting for headings, bold, italic, code, and lists.
%Markdown{
content: "# Title\n\nSome **bold** and *italic* text.\n\n- Item 1\n- Item 2",
style: %Style{},
wrap: true,
scroll: {0, 0},
block: %Block{borders: [:all]}
}Throbber
Animated loading spinner. Caller must increment :step to animate.
%Throbber{
label: "Loading",
throbber_set: :braille, # :braille | :dots | :line | :ascii
step: state.tick, # increment this to animate
style: %Style{},
throbber_style: %Style{fg: :cyan},
block: nil
}Use Subscription.interval/3 (reducer) or Process.send_after/3 (callback) to increment the step.
Popup
Centered overlay. Wraps a content widget and renders it on top of existing content.
%Popup{
content: %Paragraph{text: "Are you sure?"},
percent_width: 50, # percentage of terminal width
percent_height: 30,
# OR use fixed dimensions:
# fixed_width: 40,
# fixed_height: 10,
block: %Block{title: " Confirm ", borders: [:all], border_type: :double}
}WidgetList
Scrollable container for heterogeneous widgets with row-based scrolling and partial clipping.
%WidgetList{
items: [
{%Paragraph{text: "Row 1"}, 3}, # {widget, height}
{%Paragraph{text: "Row 2"}, 3},
{%Paragraph{text: "Row 3"}, 5}
],
selected: 0,
scroll_offset: 0, # row index of first visible item
highlight_style: %Style{bg: {50, 50, 50}},
block: %Block{title: " Items ", borders: [:all]}
}SlashCommands
Autocomplete overlay for slash commands. Combines SlashCommands functions with a Popup.
alias ExRatatui.Widgets.SlashCommands
alias ExRatatui.Widgets.SlashCommands.Command
commands = [
%Command{name: "help", description: "Show help", aliases: ["h", "?"]},
%Command{name: "quit", description: "Exit the app", aliases: ["q"]}
]
# Parse user input for a slash prefix
case SlashCommands.parse(input_text) do
{:slash, query} ->
matches = SlashCommands.match_commands(commands, query)
# Render matches in a Popup via your render/2 callback
:no_slash ->
# Normal input, no autocomplete needed
end