A simple bash library for creating text-based user interfaces with immediate keypress response, ANSI color support, and YAML-based configuration.
- YAML-based configuration for menu layouts
- Immediate keypress handling (no need to press Enter)
- ANSI colors and styling (DTS-compatible color scheme)
- Serial port compatible
- Dynamic content from environment variables
- Conditional section/entry display
- Callback system for menu actions
- Full clear & redraw rendering
- 80-column terminal support with auto-wrapping footer
- Helper functions for custom rendering
- Bash 4.0 or later
yq- YAML processor (mikefarah/yq v4+)- Optional:
batsfor running tests
Create a file my-app.yaml:
header:
title: " My Application ${APP_VERSION} "
subtitle: " by Your Name "
link: "https://github.com/yourusername/yourproject"
sections:
- label: "SYSTEM INFORMATION"
entries:
- label: "Hostname"
value: "${HOSTNAME}"
- label: "User"
value: "${USER}"
menu:
- key: "1"
label: "Run backup"
callback: "callbacks/backup.sh"
- key: "2"
label: "Check status"
callback: "callbacks/status.sh"
footer:
- key: "Q"
label: "quit"
callback: "callbacks/quit.sh"
- key: "R"
label: "reboot"
callback: "callbacks/reboot.sh"Create callbacks/backup.sh:
#!/bin/bash
echo "Running backup..."
sleep 2
echo "Backup completed!"
exit 0Make it executable:
chmod +x callbacks/backup.shCreate my-app.sh:
#!/bin/bash
# Get script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Source the TUI library
source "${SCRIPT_DIR}/lib/tui-lib.sh"
# Set environment variables
export APP_VERSION="1.0.0"
# Run the TUI
tui_run "${SCRIPT_DIR}/my-app.yaml"chmod +x my-app.sh
./my-app.shheader:
title: "Application Title" # Main title (supports env vars)
subtitle: "Optional subtitle" # Subtitle text (supports env vars)
link: "https://example.com" # Optional link (supports env vars)sections:
- label: "SECTION NAME" # Section header
condition: "${SHOW_SECTION}" # Optional: show/hide section
entries:
- label: "Field Name"
value: "${FIELD_VALUE}" # Supports env vars
condition: "${SHOW_FIELD}" # Optional: show/hide entrymenu:
- key: "1" # Single character key
label: "Menu option text" # Display text (supports env vars)
callback: "path/to/script.sh" # Script to execute
condition: "${SHOW_OPTION}" # Optional: show/hide optionfooter:
- key: "Q" # Single character key (case-insensitive)
label: "quit" # Action description
callback: "path/to/script.sh" # Script to execute
condition: "${SHOW_ACTION}" # Optional: show/hide actionUse standard bash variable syntax in your YAML:
# Simple variable
value: "${MY_VAR}"
# Variable with default
value: "${MY_VAR:-default value}"
# Command substitution
value: "$(hostname)"Control visibility using environment variables:
sections:
- label: "ADMIN SECTION"
condition: "${IS_ADMIN}" # Only shown if IS_ADMIN is non-empty and not "false" or "0"
entries:
- label: "Secret"
value: "${SECRET_VALUE}"In your script:
# Show section
export IS_ADMIN="true"
# Hide section
export IS_ADMIN=""
# or
export IS_ADMIN="false"
# or
export IS_ADMIN="0"Callback scripts are regular bash scripts that:
- Are executed when a menu/footer option is selected
- Must be executable (
chmod +x) - Have full screen for output
- Return to menu after user presses any key
Example callback:
#!/bin/bash
echo "=== My Action ==="
echo ""
echo "Performing action..."
# Do your work here
sleep 1
echo "Action completed!"
# Exit code is preserved
exit 0The library automatically:
- Clears screen before running callback
- Waits for user keypress after callback completes
- Returns to menu
- Redraws menu
Main entry point. Loads configuration and starts the TUI loop.
Stop the TUI loop and exit cleanly.
Renders the complete menu (header, sections, menu, footer).
Renders only the header section.
Renders all information sections.
Renders menu options.
Renders footer actions.
Loads a YAML configuration file and parses it into bash arrays for fast rendering.
Calculate visible text length (stripping ANSI escape codes).
Generate a border string of specified length (default char: *).
Print colored text using ANSI escape codes.
Print styled status messages.
Print a full-width border line (respects TUI_MAX_WIDTH=80).
Print a section header with borders.
Print a section entry (label: value format).
Print a menu option line.
Expand environment variables in a string.
Check if a condition evaluates to true.
Clear the terminal screen.
Control cursor visibility.
Read a single keypress without waiting for Enter.
Install bats:
Ubuntu/Debian:
sudo apt-get install batsmacOS:
brew install bats-coreFrom source:
git clone https://github.com/bats-core/bats-core.git
cd bats-core
sudo ./install.sh /usr/localRun tests:
./tests/run-tests.shOr run bats directly:
bats tests/tui-lib.batsSee the examples/ directory for a complete example based on the Dasharo Tools Suite:
cd examples
./demo.shThis demonstrates:
- Multiple information sections
- Dynamic content from environment variables
- Conditional section display
- Menu options with callbacks
- Footer actions
The library is designed to work over serial ports:
- Uses ANSI escape codes (widely supported)
- Full clear & redraw (avoids complex cursor positioning)
- Immediate keypress handling
- No advanced terminal features required
Tested on:
- Local terminals (bash, zsh, etc.)
- SSH sessions
- Serial consoles (ttyS0, etc.)
- Simplicity: Easy to create new TUI tools with YAML + callback scripts
- Reliability: Full clear/redraw approach works everywhere
- Flexibility: Dynamic content and conditional display
- Maintainability: Separation of UI (YAML) and logic (callbacks)
The library uses yq (mikefarah/yq) to parse YAML configuration files directly:
- Config is loaded once using
yq evalcommands - Data is stored in bash arrays for fast access
- Rendering uses pure bash (zero external process calls after config load)
Performance:
- Config loading: Multiple
yqcalls at startup (acceptable overhead) - Rendering: Zero external processes = instant (critical for responsive UI)
This approach prioritizes rendering performance while using a single, widely-available YAML processor.
- Menu limited to 0-10 options (single keypress)
- Footer limited to 0-10 actions (single keypress)
- No mouse support
- No complex layouts beyond defined sections
- Full screen redraw (brief flicker possible)