#!/bin/bash # Catppuccin Mocha theme colors (only define if not already set) if [[ -z "${PURPLE:-}" ]]; then readonly PURPLE="#cba6f7" # mauve readonly GREEN="#a6e3a1" readonly RED="#f38ba8" readonly YELLOW="#f9e2af" readonly CYAN="#89dceb" # sky readonly ORANGE="#fab387" # peach readonly PINK="#f5c2e7" fi # Gum wrapper functions for common UI patterns # Execute command with gum spinner gum_execute() { local -r CMDS="$1" local -r MSG="${2:-$1}" local -r SPINNER="${3:-dot}" if cmd_exists "gum"; then gum spin --spinner="$SPINNER" --title="$MSG" -- bash -c "$CMDS" else echo "⚙️ $MSG" eval "$CMDS" fi } # Convenient aliases for different spinner types gum_execute_pulse() { gum_execute "$1" "$2" "pulse" } gum_execute_points() { gum_execute "$1" "$2" "points" } gum_execute_moon() { gum_execute "$1" "$2" "moon" } show_header() { local title="$1" local description="$2" if cmd_exists "gum"; then if [ -n "$description" ]; then gum style \ --foreground="$PURPLE" \ --border="rounded" \ --padding="1 2" \ --margin="1 0" \ "$title" \ "" \ "$description" else gum style \ --foreground="$PURPLE" \ --border="rounded" \ --padding="1 2" \ --margin="1 0" \ "$title" fi else echo "$title" [ -n "$description" ] && echo "$description" fi } show_section() { local title="$1" local color="${2:-$YELLOW}" echo "" if cmd_exists "gum"; then gum style \ --foreground="$color" \ --bold \ --border="rounded" \ --padding="0 1" \ "$title" else echo "$title" fi } show_completion() { local title="$1" local message="$2" local additional="${3:-}" echo "" if cmd_exists "gum"; then if [ -n "$additional" ]; then gum style \ --foreground="$GREEN" \ --border="double" \ --padding="1 2" \ --margin="1 0" \ "$title" \ "" \ "$message" \ "$additional" else gum style \ --foreground="$GREEN" \ --border="double" \ --padding="1 2" \ --margin="1 0" \ "$title" \ "" \ "$message" fi else echo "$title" echo "$message" [ -n "$additional" ] && echo "$additional" fi } show_message() { local message="$1" local color="${2:-$CYAN}" if cmd_exists "gum"; then gum style --foreground="$color" "$message" else echo "$message" fi } show_bold_message() { local message="$1" local color="${2:-$GREEN}" if cmd_exists "gum"; then gum style --foreground="$color" --bold "$message" else echo "$message" fi } answer_is_yes() { [[ $REPLY =~ ^[Yy]$ ]] && return 0 || return 1 } ask() { if cmd_exists "gum"; then REPLY=$(gum input --placeholder "$1") else print_question "$1" read -r fi } ask_for_confirmation() { if cmd_exists "gum"; then gum confirm "$1" && REPLY="y" || REPLY="n" else print_question "$1 (y/n) " read -r -n 1 printf "\n" fi } ask_for_sudo() { # Ask for the administrator password upfront. sudo -v &>/dev/null # Update existing `sudo` time stamp # until this script has finished. # # https://gist.github.com/cowboy/3118588 while true; do sudo -n true sleep 60 kill -0 "$$" || exit done &>/dev/null & } cmd_exists() { command -v "$1" &>/dev/null } kill_all_subprocesses() { local i="" for i in $(jobs -p); do kill "$i" wait "$i" &>/dev/null done } execute() { local -r CMDS="$1" local -r MSG="${2:-$1}" local -r TMP_FILE="$(mktemp /tmp/XXXXX)" local exitCode=0 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # If the current process is ended, # also end all its subprocesses. set_trap "EXIT" "kill_all_subprocesses" # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if cmd_exists "gum"; then # Use gum's built-in spinner with error capture gum spin \ --spinner="dot" \ --title="$MSG" \ -- bash -c "$CMDS >/dev/null 2>'$TMP_FILE'" exitCode=$? else # Fallback to original implementation eval "$CMDS" \ >/dev/null \ 2>"$TMP_FILE" & local cmdsPID=$! show_spinner "$cmdsPID" "$CMDS" "$MSG" wait "$cmdsPID" &>/dev/null exitCode=$? fi # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Print output based on what happened. print_result $exitCode "$MSG" if [ $exitCode -ne 0 ]; then print_error_stream <"$TMP_FILE" fi rm -f "$TMP_FILE" # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - return $exitCode } print_error() { if cmd_exists "gum"; then gum style --foreground="$RED" --bold "❌ $1 $2" else print_in_red " [✖] $1 $2\n" fi } print_error_stream() { while read -r line; do print_error "↳ ERROR: $line" done } print_in_color() { printf "%b" \ "$(tput setaf "$2" 2>/dev/null)" \ "$1" \ "$(tput sgr0 2>/dev/null)" } print_info() { if cmd_exists "gum"; then gum style --foreground="$CYAN" "ℹ️ $1" else print_in_color "$1" 6 fi } print_in_green() { print_in_color "$1" 2 } print_in_purple() { if cmd_exists "gum"; then gum style --foreground="$PURPLE" --bold "$1" else print_in_color "$1" 5 fi } print_in_red() { print_in_color "$1" 1 } print_in_yellow() { print_in_color "$1" 3 } print_question() { if cmd_exists "gum"; then gum style --foreground="$YELLOW" "❓ $1" else print_in_yellow " [?] $1" fi } print_result() { if [ "$1" -eq 0 ]; then print_success "$2" else print_error "$2" fi return "$1" } print_success() { if cmd_exists "gum"; then gum style --foreground="$GREEN" --bold "✅ $1" else print_in_green " [✔] $1\n" fi } print_warning() { if cmd_exists "gum"; then gum style --foreground="$ORANGE" "⚠️ $1" else print_in_yellow " [!] $1\n" fi } set_trap() { trap -p "$1" | grep "$2" &>/dev/null || trap '$2' "$1" } show_spinner() { local -r CMDS="$2" local -r MSG="$3" local -r PID="$1" if cmd_exists "gum"; then # Wait for the background process to complete while showing nothing # (gum spin will be used in execute function instead) wait "$PID" &>/dev/null else # Fallback to original spinner local -r FRAMES='/-\|' local -r NUMBER_OR_FRAMES=${#FRAMES} local i=0 local frameText="" while kill -0 "$PID" &>/dev/null; do frameText=" [${FRAMES:i++%NUMBER_OR_FRAMES:1}] $MSG" printf "%s" "$frameText" sleep 0.2 printf "\r" done fi }