Skip to content

Instantly share code, notes, and snippets.

@tomdavidson
Created February 12, 2026 02:22
Show Gist options
  • Select an option

  • Save tomdavidson/45a7f4cb23cfe9749490214b7df4cc9a to your computer and use it in GitHub Desktop.

Select an option

Save tomdavidson/45a7f4cb23cfe9749490214b7df4cc9a to your computer and use it in GitHub Desktop.
Few bash UI functions.
#!/bin/bash
# ============================================
# UI Library - User Interface Functions
# No dependencies - pure output/input
# ============================================
# Color codes
readonly COLOR_RESET='\033[0m'
readonly COLOR_RED='\033[0;31m'
readonly COLOR_GREEN='\033[0;32m'
readonly COLOR_YELLOW='\033[1;33m'
readonly COLOR_BLUE='\033[0;34m'
readonly COLOR_CYAN='\033[0;36m'
# ============================================
# Output Functions
# ============================================
lib_print_info() {
local message="$1"
echo -e "${COLOR_BLUE}[INFO]${COLOR_RESET} $message"
}
lib_print_success() {
local message="$1"
echo -e "${COLOR_GREEN}[SUCCESS]${COLOR_RESET} $message"
}
lib_print_warn() {
local message="$1"
echo -e "${COLOR_YELLOW}[WARN]${COLOR_RESET} $message" >&2
}
lib_print_error() {
local message="$1"
local exit_code="${2:-}"
echo -e "${COLOR_RED}[ERROR]${COLOR_RESET} $message" >&2
if [[ -n "$exit_code" ]]; then
exit "$exit_code"
fi
}
lib_print_step() {
local step="$1"
local message="$2"
echo -e "\n${COLOR_CYAN}==>${COLOR_RESET} ${COLOR_GREEN}Step $step:${COLOR_RESET} $message\n"
}
lib_print_header() {
local message="$1"
local line=$(printf '=%.0s' {1..60})
echo -e "\n${COLOR_CYAN}$line${COLOR_RESET}"
echo -e "${COLOR_CYAN}$message${COLOR_RESET}"
echo -e "${COLOR_CYAN}$line${COLOR_RESET}\n"
}
# ============================================
# Input Functions
# ============================================
lib_prompt_yes_no() {
local prompt="$1"
local default="${2:-n}" # Default to 'n' if not specified
local response
if [[ "$default" == "y" ]]; then
prompt="$prompt [Y/n]: "
else
prompt="$prompt [y/N]: "
fi
while true; do
read -rp "$prompt" response
response="${response:-$default}" # Use default if empty
response=$(echo "$response" | tr '[:upper:]' '[:lower:]')
case "$response" in
y|yes)
return 0
;;
n|no)
return 1
;;
*)
lib_print_warn "Please answer 'y' or 'n'"
;;
esac
done
}
lib_prompt_input() {
local prompt="$1"
local default="$2"
local response
if [[ -n "$default" ]]; then
read -rp "$prompt [$default]: " response
echo "${response:-$default}"
else
read -rp "$prompt: " response
echo "$response"
fi
}
lib_prompt_select() {
local prompt="$1"
shift
local options=("$@")
local choice
echo "$prompt"
for i in "${!options[@]}"; do
echo " $((i+1))) ${options[$i]}"
done
while true; do
read -rp "Enter selection [1-${#options[@]}]: " choice
if [[ "$choice" =~ ^[0-9]+$ ]] && \
[[ "$choice" -ge 1 ]] && \
[[ "$choice" -le "${#options[@]}" ]]; then
echo "${options[$((choice-1))]}"
return 0
else
lib_print_warn "Invalid selection. Please enter a number between 1 and ${#options[@]}"
fi
done
}
lib_prompt_confirm_destructive() {
local action="$1"
local target="$2"
lib_print_warn "DESTRUCTIVE OPERATION"
lib_print_warn "Action: $action"
lib_print_warn "Target: $target"
lib_print_warn "This operation CANNOT be undone!"
echo ""
if ! lib_prompt_yes_no "Are you absolutely sure you want to proceed?" "n"; then
lib_print_info "Operation cancelled by user"
return 1
fi
# Double confirmation for extra safety
lib_print_warn "Type 'yes' to confirm (not just 'y'):"
local confirmation
read -rp "> " confirmation
if [[ "$confirmation" != "yes" ]]; then
lib_print_info "Operation cancelled"
return 1
fi
return 0
}
# ============================================
# Progress Functions
# ============================================
lib_spinner() {
local pid=$1
local message="${2:-Processing}"
local delay=0.1
local spinstr='|/-\'
echo -n "$message "
while kill -0 "$pid" 2>/dev/null; do
local temp=${spinstr#?}
printf "[%c]" "$spinstr"
spinstr=$temp${spinstr%"$temp"}
sleep $delay
printf "\b\b\b"
done
printf "\b\b\b \b\b\b"
}
lib_progress_bar() {
local current=$1
local total=$2
local width=50
local percentage=$((current * 100 / total))
local filled=$((width * current / total))
local empty=$((width - filled))
printf "\rProgress: ["
printf "%${filled}s" | tr ' ' '='
printf "%${empty}s" | tr ' ' '-'
printf "] %3d%%" "$percentage"
if [[ $current -eq $total ]]; then
echo ""
fi
}
# ============================================
# Formatting Functions
# ============================================
lib_format_size() {
local size_bytes=$1
local units=("B" "KB" "MB" "GB" "TB")
local unit_index=0
local size=$size_bytes
while [[ $size -gt 1024 && $unit_index -lt 4 ]]; do
size=$((size / 1024))
((unit_index++))
done
echo "${size}${units[$unit_index]}"
}
lib_format_list() {
local title="$1"
shift
local items=("$@")
echo "$title:"
for item in "${items[@]}"; do
echo " • $item"
done
}
lib_print_key_value() {
local key="$1"
local value="$2"
printf " %-25s : %s\n" "$key" "$value"
}
# ============================================
# Validation Helper (display only)
# ============================================
lib_display_validation_error() {
local field="$1"
local error="$2"
lib_print_error "Validation failed for '$field': $error"
}
# ============================================
# Box Drawing
# ============================================
lib_print_box() {
local message="$1"
local length=${#message}
local border=$(printf '─%.0s' $(seq 1 $((length + 2))))
echo "┌${border}┐"
echo "│ ${message} │"
echo "└${border}┘"
}
lib_print_summary() {
local title="$1"
shift
local items=("$@")
lib_print_box "$title"
for item in "${items[@]}"; do
echo " $item"
done
echo ""
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment