#!/usr/bin/env bash # shellcheck disable=SC1091 # spin.sh - Print a spinner with a message [[ $BEDDU_SPIN_LOADED ]] && return readonly BEDDU_SPIN_LOADED=true SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")" source "$SCRIPT_DIR/../00.utils/_symbols.sh" source "$SCRIPT_DIR/../00.utils/movements.sh" source "$SCRIPT_DIR/../01.core/pen.sh" # Make sure the cursor is shown and the spinner stopped if the script exits abnormally trap spop EXIT INT TERM # Module state variables _spinner_pid="" _frame_duration="${_spinner_frame_duration:-0.1}" # Print a message with a spinner at the beginning # # Usage: # spin [options] text # Options: # [same as pen.sh] # Examples: # spin "Installing dependencies..." # sleep 2 # spop # pen "Let's do something else now..." # --or, better-- # spin "Installing dependencies..." # sleep 2 # check "Dependancies installed." spin() { local message=("$@") local spinner="${_spinner:-⣷⣯⣟⡿⢿⣻⣽⣾}" # If there is already a spinner running, stop it if spinning; then spop --keep-cursor-hidden fi # Run the spinner in the background ( hide_cursor # Use a trap to catch USR1 signal for clean shutdown trap "exit 0" USR1 # Print the first frame of the spinner pen -n cyan "${spinner:0:1} " pen "${message[@]}" while true; do for ((i = 0; i < ${#spinner}; i++)); do frame="${spinner:$i:1}" up bol pen -n cyan "${frame} " pen "${message[@]}" sleep "$_frame_duration" done done ) & _spinner_pid=$! } # Stop the spinner spop() { local keep_cursor_hidden=false [[ "$1" == "--keep-cursor-hidden" ]] && keep_cursor_hidden=true if spinning; then # Signal spinner to exit gracefully kill -USR1 "${_spinner_pid}" 2>/dev/null # Wait briefly for cleanup sleep "$_frame_duration" # Ensure it's really gone if ps -p "${_spinner_pid}" >/dev/null 2>&1; then kill "${_spinner_pid}" 2>/dev/null fi # Manually clean up display, unless asked not to do so if [[ "$keep_cursor_hidden" == false ]]; then show_cursor fi _spinner_pid="" fi } # Check if a spinner is running spinning() { [[ -n "${_spinner_pid}" ]] }