summary refs log tree commit diff
path: root/bin/libfuzl.sh
diff options
context:
space:
mode:
authorRobert Günzler <r@gnzler.io>2026-04-08 13:18:14 +0900
committerRobert Günzler <r@gnzler.io>2026-04-08 13:18:14 +0900
commitb61b58ce2c1371d6f904f108fa7feed3abfbe5c7 (patch)
tree008a5cf54ecbfffb97ddac30e44c8ab224395ab0 /bin/libfuzl.sh
parentd4620f3ac119263fee90bd819eadaf84fc8d50b7 (diff)
*
Automated-commit-by: dots
Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to 'bin/libfuzl.sh')
-rw-r--r--bin/libfuzl.sh114
1 files changed, 114 insertions, 0 deletions
diff --git a/bin/libfuzl.sh b/bin/libfuzl.sh
new file mode 100644
index 0000000..a28c861
--- /dev/null
+++ b/bin/libfuzl.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+# local is well supported
+# shellcheck disable=3043
+
+set -eu
+
+# pick up configuration
+FUZL_PROMPT=${FUZL_PROMPT:-fuzl}
+FUZL_ICON=${FUZL_ICON:-󰈲}
+
+fuzl_try() {
+	[ "$1" == "--" ] && shift 1
+	if ! { stderr=$(
+		set +x
+		eval "$*" 2>&1 1>&3
+	); } 3>&1; then
+		fuzl_notification "$* failed :(" "${stderr:--}" -c error
+		exit 1
+	fi
+}
+
+export FUZL_MENU_FILTER=filter
+export FUZL_MENU_PASSWORD=passw
+export FUZL_MENU_MESSAGE=msg
+export FUZL_MENU_INPUT=input
+
+# Shows a menu
+fuzl_menu() {
+	local form="$1"
+	shift 1
+
+	local prompt options_list maxw
+
+	# If a prompt_icon is provided, prepend it.
+	prompt="$FUZL_PROMPT "
+	[ -n "$FUZL_ICON" ] && prompt="$FUZL_ICON $FUZL_PROMPT "
+
+	options_list=$(cat)
+	maxw=$(echo -e "$prompt\n$options_list" | wc -L)
+	[ "$maxw" -lt 15 ] && maxw=15
+	[ "$maxw" -gt 150 ] && maxw=100
+
+	case "$form" in
+	"$FUZL_MENU_FILTER") # Selection menu with a list of options passed via STDIN
+		echo -ne "$options_list" | fuzzel -d -w "$maxw" -p "$prompt" "$@"
+		;;
+	"$FUZL_MENU_PASSWORD") # Password input menu (minimalist)
+		echo -n | fuzzel -d -w "$maxw" -p "$prompt" --password
+		;;
+	"$FUZL_MENU_MESSAGE") # Message box menu (no input bar)
+		echo -ne "$options_list" | fuzzel -d -w "$maxw" -p "$prompt" "$@"
+		;;
+	"$FUZL_MENU_INPUT") # Text input menu (no list view)
+		echo -n | fuzzel -d -p "$prompt" "$@" --lines 0
+		;;
+	esac
+}
+
+FUZL_NOTIFICATION_ID=
+
+fuzl_notification() {
+	local summary="${1:?missing summary}"
+	local body="${2:?missing body}"
+	shift 2
+
+	FUZL_NOTIFICATION_ID=$(
+		fyi "$@" -p -a "${FUZL_PROMPT}" "${FUZL_PROMPT}: $summary" "$body" |
+			cut -d= -f2
+	)
+}
+fuzl_notification_cancel() {
+	fyi --close "$FUZL_NOTIFICATION_ID" || true
+	FUZL_NOTIFICATION_ID=
+}
+
+FUZL_LOADING_PID=
+
+fuzl_loading() {
+	local summary="${1:?missing summary}"
+	echo -e "..." | FUZL_PROMPT="$summary" fuzl_menu $FUZL_MENU_MESSAGE &
+	FUZL_LOADING_PID=$!
+}
+fuzl_loading_cancel() {
+	local pgid
+	pgid="$(ps -o pgid= "$FUZL_LOADING_PID" || return)"
+	kill -- -"$pgid" || true
+	FUZL_LOADING_PID=
+}
+
+FUZL_ACTIONS=()
+
+fuzl_action_add() {
+	local action="${1:?}" icon="${2:?}"
+	shift 2
+	# prevent multiple occurences
+	printf '%s\n' "${FUZL_ACTIONS[@]}" | grep -q "^$action" && return
+	FUZL_ACTIONS+=("${action}\t${icon}\t${*}")
+}
+
+fuzl_action_menu() {
+	local action
+
+	action=$(printf '%s\n' "${FUZL_ACTIONS[@]}" |
+		FUZL_PROMPT="$FUZL_PROMPT: $*" fuzl_menu $FUZL_MENU_FILTER --with-nth='{2}  {3..}' --accept-nth=1)
+
+	# reset actions
+	FUZL_ACTIONS=()
+
+	test -n "$action" || return
+	eval "${FUZL_PROMPT}_${action}" "'$*'"
+}
+
+# setup common debug helper
+[ -z "${DEBUG:-}" ] || set -x