beddu/build/beddu.sh
2025-05-11 01:40:54 +02:00

320 lines
7.1 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# shellcheck disable=all
#
# beddu.sh - A lightweight bash framework for interactive scripts and pretty output
# https://github.com/mjsarfatti/beddu
#
# Version: v0.0.3-dirty
# Generated on: Sun May 11 01:40:08 CEST 2025
readonly _q='?'
readonly _a=''
readonly _o='◌'
readonly _O='●'
readonly _mark='✓'
readonly _warn='!'
readonly _cross='✗'
readonly _spinner='⣷⣯⣟⡿⢿⣻⣽⣾' # See for alternatives: https://antofthy.gitlab.io/info/ascii/Spinners.txt
export _q _a _o _O _mark _warn _cross _spinner
up() {
printf "\033[A"
}
down() {
printf "\033[B"
}
bol() {
printf "\r"
}
eol() {
printf "\033[999C"
}
cl() {
printf "\033[2K"
}
upclear() {
up; bol; cl
}
line() {
printf "\n"
}
show_cursor() {
printf "\033[?25h"
}
hide_cursor() {
printf "\033[?25l"
}
export -f up down bol eol cl line show_cursor hide_cursor
pen() {
local new_line="\n"
local text="${*: -1}"
local args=("${@:1:$#-1}")
local format_code=""
local reset_code="\033[0m"
for arg in "${args[@]}"; do
arg=${arg,,}
case "$arg" in
-n) new_line="" ;;
bold) format_code+="\033[1m" ;;
italic) format_code+="\033[3m" ;;
underline) format_code+="\033[4m" ;;
black) format_code+="\033[30m" ;;
red) format_code+="\033[31m" ;;
green) format_code+="\033[32m" ;;
yellow) format_code+="\033[33m" ;;
blue) format_code+="\033[34m" ;;
purple) format_code+="\033[35m" ;;
cyan) format_code+="\033[36m" ;;
white) format_code+="\033[37m" ;;
grey | gray) format_code+="\033[90m" ;;
[0-9]*)
if [[ "$arg" =~ ^[0-9]+$ ]] && [ "$arg" -ge 0 ] && [ "$arg" -le 255 ]; then
format_code+="\033[38;5;${arg}m"
fi
;;
*) ;;
esac
done
printf "%b%s%b%b" "${format_code}" "${text}" "${reset_code}" "${new_line}"
}
export -f pen
run() {
local outvar_name errvar_name
local -n outvar errvar # Declare namerefs (will be assigned below if needed)
local cmd
while [[ $# -gt 0 ]]; do
case "$1" in
--out)
outvar_name="$2"
shift 2
;;
--err)
errvar_name="$2"
shift 2
;;
*)
cmd=("$@")
break
;;
esac
done
[[ -n "${outvar_name}" ]] && local -n outvar="${outvar_name}"
[[ -n "${errvar_name}" ]] && local -n errvar="${errvar_name}"
local stdout_file stderr_file
stdout_file=$(mktemp)
stderr_file=$(mktemp)
"${cmd[@]}" >"${stdout_file}" 2>"${stderr_file}"
local exit_code=$?
[[ -n "${outvar_name}" ]] && outvar="$(<"$stdout_file")"
[[ -n "${errvar_name}" ]] && errvar="$(<"$stderr_file")"
rm -f "${stdout_file}" "${stderr_file}"
return $exit_code
}
export -f run
check() {
if spinning; then
spop
upclear
fi
pen -n green "${_mark:-} "
pen "$@"
}
export -f check
repen() {
upclear
pen "$@"
}
export -f repen
trap spop EXIT INT TERM
_spinner_frame_duration=0.1
_spinner_pid=""
spin() {
local message=("$@")
_spinner="${_spinner:-⣷⣯⣟⡿⢿⣻⣽⣾}"
if spinning; then
spop --keep-cursor-hidden
fi
(
hide_cursor
trap "show_cursor; exit 0" USR1
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 $_spinner_frame_duration
done
done
) &
_spinner_pid=$!
}
spop() {
local keep_cursor_hidden=false
[[ "$1" == "--keep-cursor-hidden" ]] && keep_cursor_hidden=true
if spinning; then
kill -USR1 "${_spinner_pid}" 2>/dev/null
sleep $_spinner_frame_duration
if ps -p "${_spinner_pid}" >/dev/null 2>&1; then
kill "${_spinner_pid}" 2>/dev/null
if [[ "$keep_cursor_hidden" == false ]]; then
show_cursor
fi
fi
_spinner_pid=""
fi
}
spinning() {
[[ -n "${_spinner_pid}" ]]
}
export -f spin spop spinning
throw() {
if spinning; then
spop
upclear
fi
pen -n red "${_cross:-} "
pen "$@"
}
export -f throw
warn() {
if spinning; then
spop
upclear
fi
pen -n yellow bold italic "${_warn:-!} "
pen italic "$@"
}
export -f warn
ask() {
local -n outvar="$1"
local prompt
local answer
prompt=$(
pen -n blue "${_q:-?} "
pen "${2}"
pen -n blue "${_a:-} "
)
show_cursor
while true; do
read -r -p "$prompt" answer
case "$answer" in
"")
echo
warn "Please type your answer."
;;
*) break ;;
esac
done
outvar="$answer"
}
export -f ask
choose() {
local -n outvar="$1"
local prompt
local options=("${@:3}")
local current=0
local count=${#options[@]}
prompt=$(
pen -n blue "${_q:-?} "
pen -n "${2}"
pen gray "[↑↓]"
)
hide_cursor
trap 'show_cursor; return' INT TERM
pen "$prompt"
while true; do
local index=0
for item in "${options[@]}"; do
if ((index == current)); then
pen -n blue "${_O:-} "
pen "${item}"
else
pen gray "${_o:-} ${item}"
fi
((index++))
done
read -s -r -n1 key
if [[ $key == $'\e' ]]; then
read -s -r -n2 -t 0.0001 escape
key+="$escape"
fi
case "$key" in
$'\e[A' | 'k')
((current--))
[[ $current -lt 0 ]] && current=$((count - 1))
;;
$'\e[B' | 'j')
((current++))
[[ $current -ge "$count" ]] && current=0
;;
'')
break
;;
esac
echo -en "\e[${count}A\e[J"
done
outvar="${options[$current]}"
}
confirm() {
local default="y"
local hint="[Y/n]"
local prompt
local response
while [[ $# -gt 0 ]]; do
case "$1" in
--default-no)
default="n"
hint="[y/N]"
shift
;;
--default-yes)
shift
;;
*) break ;;
esac
done
prompt=$(
pen -n blue "${_q:-?} "
pen -n "$1"
pen gray " $hint"
pen -n blue "${_a:-} "
)
show_cursor
while true; do
read -r -p "$prompt" response
response="${response:-$default}"
case "$response" in
[Yy] | [Yy][Ee][Ss])
upclear
pen -n blue "${_a:-} "
pen "yes"
return 0
;;
[Nn] | [Nn][Oo])
upclear
pen -n blue "${_a:-} "
pen "no"
return 1
;;
*)
echo
warn "Please answer yes or no."
;;
esac
done
}
export -f confirm