mirror of
https://github.com/mjsarfatti/beddu.git
synced 2025-06-27 01:18:01 +02:00
Initial commit - v1.0
This commit is contained in:
commit
76d136f64c
20 changed files with 1350 additions and 0 deletions
13
src/00.utils/_symbols.sh
Normal file
13
src/00.utils/_symbols.sh
Normal file
|
@ -0,0 +1,13 @@
|
|||
#! /usr/bin/env bash
|
||||
# @private
|
||||
|
||||
_q='?'
|
||||
_a='❯'
|
||||
_o='◌'
|
||||
_O='●'
|
||||
_mark='✓'
|
||||
_warn='!'
|
||||
_cross='✗'
|
||||
_spinner='⣷⣯⣟⡿⢿⣻⣽⣾' # See for alternatives: https://antofthy.gitlab.io/info/ascii/Spinners.txt
|
||||
|
||||
export _q _a _o _O _mark _warn _cross _spinner
|
52
src/00.utils/movements.sh
Normal file
52
src/00.utils/movements.sh
Normal file
|
@ -0,0 +1,52 @@
|
|||
#! /usr/bin/env bash
|
||||
# movements.sh - Cursor helper functions
|
||||
|
||||
# Move cursor up one line
|
||||
up() {
|
||||
printf "\033[A"
|
||||
}
|
||||
|
||||
# Move cursor down one line
|
||||
down() {
|
||||
printf "\033[B"
|
||||
}
|
||||
|
||||
# Move cursor to beginning of line
|
||||
bol() {
|
||||
printf "\r"
|
||||
}
|
||||
|
||||
# Move cursor to end of line
|
||||
eol() {
|
||||
printf "\033[999C"
|
||||
}
|
||||
|
||||
# Clear entire line
|
||||
cl() {
|
||||
printf "\033[2K"
|
||||
}
|
||||
|
||||
# Clear line above
|
||||
upclear() {
|
||||
up
|
||||
bol
|
||||
cl
|
||||
}
|
||||
|
||||
# Print a single newline
|
||||
line() {
|
||||
printf "\n"
|
||||
}
|
||||
|
||||
# Show cursor
|
||||
show_cursor() {
|
||||
printf "\033[?25h"
|
||||
}
|
||||
|
||||
# Hide cursor
|
||||
hide_cursor() {
|
||||
printf "\033[?25l"
|
||||
}
|
||||
|
||||
# Export the functions so they are available to subshells
|
||||
export -f up down bol eol cl line show_cursor hide_cursor
|
62
src/01.core/pen.sh
Normal file
62
src/01.core/pen.sh
Normal file
|
@ -0,0 +1,62 @@
|
|||
#! /usr/bin/env bash
|
||||
# pen.sh - Print pretty text
|
||||
|
||||
# @depends on:
|
||||
# - _symbols.sh
|
||||
|
||||
# Print text with ANSI color codes and text formatting
|
||||
#
|
||||
# Usage:
|
||||
# pen [options] text
|
||||
# Options:
|
||||
# -n: No newline after text
|
||||
# bold: Bold text
|
||||
# italic: Italic text
|
||||
# underline: Underline text
|
||||
# black|red|green|yellow|blue|purple|cyan|white|grey|gray: Color text
|
||||
# [0-9]: ANSI 256 color number
|
||||
# *: Any other text is printed as is
|
||||
# Examples:
|
||||
# pen "Hello, world!"
|
||||
# pen -n "Hello, world!"
|
||||
# pen bold "Hello, world!"
|
||||
# pen italic blue "Hello, world!"
|
||||
# pen -n 219 underline "Hello, world!"
|
||||
pen() {
|
||||
local new_line="\n"
|
||||
local text="${*: -1}" # Get the last argument as the text
|
||||
local args=("${@:1:$#-1}") # Get all arguments except the last one
|
||||
local format_code=""
|
||||
local reset_code="\033[0m"
|
||||
|
||||
for arg in "${args[@]}"; do
|
||||
arg=${arg,,} # Convert to lowercase
|
||||
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]*)
|
||||
# Check if this is a valid ANSI 256 color number
|
||||
if [[ "$arg" =~ ^[0-9]+$ ]] && [ "$arg" -ge 0 ] && [ "$arg" -le 255 ]; then
|
||||
format_code+="\033[38;5;${arg}m"
|
||||
fi
|
||||
;;
|
||||
*) ;; # Ignore invalid arguments
|
||||
esac
|
||||
done
|
||||
|
||||
printf "%b%s%b%b" "${format_code}" "${text}" "${reset_code}" "${new_line}"
|
||||
}
|
||||
|
||||
# Export the pen function so it's available to subshells
|
||||
export -f pen
|
59
src/01.core/run.sh
Normal file
59
src/01.core/run.sh
Normal file
|
@ -0,0 +1,59 @@
|
|||
#! /usr/bin/env bash
|
||||
# run.sh - Execute commands with output/error capture
|
||||
|
||||
# Execute a command with stdout and stderr capture capabilities
|
||||
#
|
||||
# Usage:
|
||||
# run --out output_var --err error_var command [args...]
|
||||
# run command [args...]
|
||||
# Examples:
|
||||
# run --out output --err error echo "Hello, world!"
|
||||
# pen "You said: $output"
|
||||
run() {
|
||||
local outvar_name errvar_name
|
||||
local -n outvar errvar # Declare namerefs (will be assigned below if needed)
|
||||
local cmd
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--out)
|
||||
outvar_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
--err)
|
||||
errvar_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
cmd=("$@")
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Set up namerefs if variable names are provided
|
||||
[[ -n "${outvar_name}" ]] && local -n outvar="${outvar_name}"
|
||||
[[ -n "${errvar_name}" ]] && local -n errvar="${errvar_name}"
|
||||
|
||||
# Temporary files for capture
|
||||
local stdout_file stderr_file
|
||||
stdout_file=$(mktemp)
|
||||
stderr_file=$(mktemp)
|
||||
|
||||
# Execute command with redirection
|
||||
"${cmd[@]}" >"${stdout_file}" 2>"${stderr_file}"
|
||||
local exit_code=$?
|
||||
|
||||
# Assign outputs if requested
|
||||
# shellcheck disable=SC2034
|
||||
[[ -n "${outvar_name}" ]] && outvar="$(<"$stdout_file")"
|
||||
# shellcheck disable=SC2034
|
||||
[[ -n "${errvar_name}" ]] && errvar="$(<"$stderr_file")"
|
||||
|
||||
rm -f "${stdout_file}" "${stderr_file}"
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
# Export the run function so it's available to subshells
|
||||
export -f run
|
38
src/02.ui/check.sh
Normal file
38
src/02.ui/check.sh
Normal file
|
@ -0,0 +1,38 @@
|
|||
#! /usr/bin/env bash
|
||||
# check.sh - Print a success message
|
||||
|
||||
# @depends on:
|
||||
# - pen.sh
|
||||
# - movements.sh
|
||||
# - _symbols.sh
|
||||
|
||||
# Print a checkmark with a message, and stop and replace the
|
||||
# spinner if it's running (relies on the spinner being the last
|
||||
# thing printed)
|
||||
#
|
||||
# Usage:
|
||||
# check [options] text
|
||||
# Options:
|
||||
# [same as pen.sh]
|
||||
# Examples:
|
||||
# check "Success, world!"
|
||||
# check bold "Success, world!"
|
||||
# --or--
|
||||
# spin "Installing dependencies..."
|
||||
# sleep 2
|
||||
# check "Dependancies installed."
|
||||
check() {
|
||||
# If there is a spinner running, stop it and clear the line
|
||||
if spinning; then
|
||||
spop
|
||||
up
|
||||
bol
|
||||
cl
|
||||
fi
|
||||
|
||||
pen -n green "${_mark:-✓} "
|
||||
pen "$@"
|
||||
}
|
||||
|
||||
# Export the check function so it can be used in other scripts
|
||||
export -f check
|
25
src/02.ui/repen.sh
Normal file
25
src/02.ui/repen.sh
Normal file
|
@ -0,0 +1,25 @@
|
|||
#! /usr/bin/env bash
|
||||
# repen.sh - Overwrite the previous line with new text
|
||||
|
||||
# @depends on:
|
||||
# - pen.sh
|
||||
# - movements.sh
|
||||
|
||||
# Move up one line, move to the beginning, clear the line, and print the text.
|
||||
#
|
||||
# Usage:
|
||||
# repen [options] text
|
||||
# Options:
|
||||
# [same as pen.sh]
|
||||
# Examples:
|
||||
# repen "Hello, world!"
|
||||
# repen bold "Hello, world!"
|
||||
# --or--
|
||||
# repen "Hello, world!"
|
||||
repen() {
|
||||
upclear
|
||||
pen "$@"
|
||||
}
|
||||
|
||||
# Export the repen function so it can be used in other scripts
|
||||
export -f repen
|
97
src/02.ui/spin.sh
Normal file
97
src/02.ui/spin.sh
Normal file
|
@ -0,0 +1,97 @@
|
|||
#! /usr/bin/env bash
|
||||
# spin.sh - Print a spinner with a message
|
||||
|
||||
# @depends on:
|
||||
# - pen.sh
|
||||
# - movements.sh
|
||||
# - _symbols.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_frame_duration=0.1
|
||||
_spinner_pid=""
|
||||
|
||||
# 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=("$@")
|
||||
_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 "show_cursor; 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 $_spinner_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 $_spinner_frame_duration
|
||||
|
||||
# Ensure it's really gone
|
||||
if ps -p "${_spinner_pid}" >/dev/null 2>&1; then
|
||||
kill "${_spinner_pid}" 2>/dev/null
|
||||
# Manually clean up display, unless asked not to do so
|
||||
if [[ "$keep_cursor_hidden" == false ]]; then
|
||||
show_cursor
|
||||
fi
|
||||
fi
|
||||
|
||||
_spinner_pid=""
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if a spinner is running
|
||||
spinning() {
|
||||
[[ -n "${_spinner_pid}" ]]
|
||||
}
|
||||
|
||||
# Export the functions so they are available to subshells
|
||||
export -f spin spop spinning
|
38
src/02.ui/throw.sh
Normal file
38
src/02.ui/throw.sh
Normal file
|
@ -0,0 +1,38 @@
|
|||
#! /usr/bin/env bash
|
||||
# throw.sh - Print an throw message
|
||||
|
||||
# @depends on:
|
||||
# - pen.sh
|
||||
# - movements.sh
|
||||
# - _symbols.sh
|
||||
|
||||
# Print an throwmark with a message, and stop and replace the
|
||||
# spinner if it's running (relies on the spinner being the last
|
||||
# thing printed)
|
||||
#
|
||||
# Usage:
|
||||
# throw [options] text
|
||||
# Options:
|
||||
# [same as pen.sh]
|
||||
# Examples:
|
||||
# throw "Failed, world!"
|
||||
# throw bold "Failed, world!"
|
||||
# --or--
|
||||
# spin "Installing dependencies..."
|
||||
# sleep 2
|
||||
# throw "Did you forget to feed the cat?"
|
||||
throw() {
|
||||
# If there is a spinner running, stop it and clear the line
|
||||
if spinning; then
|
||||
spop
|
||||
up
|
||||
bol
|
||||
cl
|
||||
fi
|
||||
|
||||
pen -n red "${_cross:-✗} "
|
||||
pen "$@"
|
||||
}
|
||||
|
||||
# Export the throw function so it can be used in other scripts
|
||||
export -f throw
|
38
src/02.ui/warn.sh
Normal file
38
src/02.ui/warn.sh
Normal file
|
@ -0,0 +1,38 @@
|
|||
#! /usr/bin/env bash
|
||||
# warn.sh - Print a warning message
|
||||
|
||||
# @depends on:
|
||||
# - pen.sh
|
||||
# - movements.sh
|
||||
# - _symbols.sh
|
||||
|
||||
# Print a "!" with a message, and stop and replace the
|
||||
# spinner if it's running (relies on the spinner being
|
||||
# the last thing printed)
|
||||
#
|
||||
# Usage:
|
||||
# warn [options] text
|
||||
# Options:
|
||||
# [same as pen.sh]
|
||||
# Examples:
|
||||
# warn "Failed, world!"
|
||||
# warn bold "Failed, world!"
|
||||
# --or--
|
||||
# spin "Installing dependencies..."
|
||||
# sleep 2
|
||||
# warn "Did you forget to feed the cat?"
|
||||
warn() {
|
||||
# If there is a spinner running, stop it and clear the line
|
||||
if spinning; then
|
||||
spop
|
||||
up
|
||||
bol
|
||||
cl
|
||||
fi
|
||||
|
||||
pen -n yellow bold italic "${_warn:-!} "
|
||||
pen italic "$@"
|
||||
}
|
||||
|
||||
# Export the warn function so it can be used in other scripts
|
||||
export -f warn
|
47
src/03.prompt/ask.sh
Normal file
47
src/03.prompt/ask.sh
Normal file
|
@ -0,0 +1,47 @@
|
|||
#! /usr/bin/env bash
|
||||
# ask.sh - Get free text input from the user
|
||||
|
||||
# @depends on:
|
||||
# - pen.sh
|
||||
# - _symbols.sh
|
||||
# - cursor.sh
|
||||
|
||||
# Ask a question and get a free text answer from the user
|
||||
#
|
||||
# Usage:
|
||||
# ask outvar text
|
||||
# Example:
|
||||
# ask name "What is your name?"
|
||||
# echo "Hello, $name!"
|
||||
ask() {
|
||||
local -n outvar="$1" # Declare nameref
|
||||
local prompt
|
||||
local answer
|
||||
|
||||
# Set prompt with default indicator
|
||||
prompt=$(
|
||||
pen -n blue "${_q:-?} "
|
||||
pen "${2}"
|
||||
pen -n blue "${_a:-❯} "
|
||||
)
|
||||
|
||||
show_cursor
|
||||
|
||||
# Get response
|
||||
while true; do
|
||||
read -r -p "$prompt" answer
|
||||
case "$answer" in
|
||||
"")
|
||||
echo
|
||||
warn "Please type your answer."
|
||||
;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
outvar="$answer"
|
||||
}
|
||||
|
||||
# Export the ask function so it's available to subshells
|
||||
export -f ask
|
81
src/03.prompt/choose.sh
Normal file
81
src/03.prompt/choose.sh
Normal file
|
@ -0,0 +1,81 @@
|
|||
#! /usr/bin/env bash
|
||||
# choose.sh - Choose from a menu of options
|
||||
|
||||
# @depends on:
|
||||
# - pen.sh
|
||||
# - _symbols.sh
|
||||
# - cursor.sh
|
||||
|
||||
# Print an interactive menu of options and return the selected option
|
||||
#
|
||||
# Usage:
|
||||
# choose outvar text [choices...]
|
||||
# Example:
|
||||
# choose color "What is your favorite color?" "Red" "Blue" "Green"
|
||||
# pen "You chose $color!"
|
||||
choose() {
|
||||
local -n outvar="$1"
|
||||
local prompt
|
||||
local options=("${@:3}") # Get options from third argument onwards
|
||||
|
||||
local current=0
|
||||
local count=${#options[@]}
|
||||
|
||||
# Set prompt with default indicator
|
||||
prompt=$(
|
||||
pen -n blue "${_q:-?} "
|
||||
pen -n "${2}"
|
||||
pen gray "[↑↓]"
|
||||
)
|
||||
|
||||
# Hide cursor for cleaner UI
|
||||
hide_cursor
|
||||
trap 'show_cursor; return' INT TERM
|
||||
|
||||
# Display initial prompt
|
||||
pen "$prompt"
|
||||
|
||||
# Main loop
|
||||
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 a single key press
|
||||
read -s -r -n1 key
|
||||
|
||||
# Handle arrow/enter keys
|
||||
if [[ $key == $'\e' ]]; then
|
||||
read -s -r -n2 -t 0.0001 escape
|
||||
key+="$escape"
|
||||
fi
|
||||
|
||||
case "$key" in
|
||||
$'\e[A' | 'k') # Up arrow or k
|
||||
((current--))
|
||||
[[ $current -lt 0 ]] && current=$((count - 1))
|
||||
;;
|
||||
$'\e[B' | 'j') # Down arrow or j
|
||||
((current++))
|
||||
[[ $current -ge "$count" ]] && current=0
|
||||
;;
|
||||
'') # Enter
|
||||
break
|
||||
;;
|
||||
esac
|
||||
|
||||
# Clear screen and repeat
|
||||
echo -en "\e[${count}A\e[J"
|
||||
done
|
||||
|
||||
# Pass selected option back to caller
|
||||
# shellcheck disable=SC2034
|
||||
outvar="${options[$current]}"
|
||||
}
|
79
src/03.prompt/confirm.sh
Normal file
79
src/03.prompt/confirm.sh
Normal file
|
@ -0,0 +1,79 @@
|
|||
#! /usr/bin/env bash
|
||||
# confirm.sh - Read a yes/no confirmation from the user
|
||||
|
||||
# @depends on:
|
||||
# - pen.sh
|
||||
# - _symbols.sh
|
||||
# - movements.sh
|
||||
|
||||
# Ask a question and get a yes/no answer from the user
|
||||
#
|
||||
# Usage:
|
||||
# confirm text
|
||||
# Options:
|
||||
# --default-yes: Answer 'yes' on ENTER (default)
|
||||
# --default-no: Answer 'no' on ENTER
|
||||
# Example:
|
||||
# if confirm "Would you like to continue?"; then
|
||||
# pen "Great!"
|
||||
# else
|
||||
# pen "Ok, bye!"
|
||||
# fi
|
||||
confirm() {
|
||||
local default="y"
|
||||
local hint="[Y/n]"
|
||||
local prompt
|
||||
local response
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--default-no)
|
||||
default="n"
|
||||
hint="[y/N]"
|
||||
shift
|
||||
;;
|
||||
--default-yes)
|
||||
shift
|
||||
;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Set prompt with default indicator
|
||||
prompt=$(
|
||||
pen -n blue "${_q:-?} "
|
||||
pen -n "$1"
|
||||
pen gray " $hint"
|
||||
pen -n blue "${_a:-❯} "
|
||||
)
|
||||
|
||||
show_cursor
|
||||
|
||||
# Get response
|
||||
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 the confirm function so it's available to subshells
|
||||
export -f confirm
|
Loading…
Add table
Add a link
Reference in a new issue