fix: TUI mit drei Backends (gum/whiptail/basic) + gum auto-install auf frischen Systemen
- tui.sh: komplett überarbeitet mit _strip_format() für basic mode
- whiptail als mittleres Fallback-Backend hinzugefügt
- Alle #{bold}/#{normal}-Markups entfernt, saubere ANSI-API (tui_info/tui_bold/...)
- install.sh: detect_environment() installiert gum vor allen Prompts
- Kein seq-Dependency mehr (printf -v statt seq)
- packages.sh/preflight.sh/homelab.sh/optional.sh auf neue TUI-API migriert
This commit is contained in:
@@ -40,7 +40,7 @@ log_step() {
|
||||
log_section() {
|
||||
local message="$1"
|
||||
local line
|
||||
line="$(printf '━%.0s' $(seq 1 "${#message}"))"
|
||||
printf -v line '%*s' "${#message}" '' && line="${line// /━}"
|
||||
printf '\n%s\n%s\n%s\n' "$line" "$message" "$line"
|
||||
__log_write "SECTION" "$message"
|
||||
}
|
||||
|
||||
249
lib/tui.sh
249
lib/tui.sh
@@ -1,55 +1,123 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
TUI_STYLE="${TUI_STYLE:-gum}"
|
||||
OMERON_HAS_GUM=0
|
||||
OMERON_HAS_WHIPTAIL=0
|
||||
OMERON_TUI_MODE="basic"
|
||||
|
||||
tui_check() {
|
||||
if [[ "$TUI_STYLE" == "gum" ]] && ! command -v gum >/dev/null 2>&1; then
|
||||
printf "gum is not installed. Install it or set TUI_STYLE=basic\n" >&2
|
||||
return 1
|
||||
tui_detect() {
|
||||
OMERON_HAS_GUM=0
|
||||
OMERON_HAS_WHIPTAIL=0
|
||||
|
||||
command -v gum >/dev/null 2>&1 && OMERON_HAS_GUM=1
|
||||
command -v whiptail >/dev/null 2>&1 && OMERON_HAS_WHIPTAIL=1
|
||||
|
||||
if ((OMERON_HAS_GUM)); then
|
||||
OMERON_TUI_MODE="gum"
|
||||
elif ((OMERON_HAS_WHIPTAIL)); then
|
||||
OMERON_TUI_MODE="whiptail"
|
||||
else
|
||||
OMERON_TUI_MODE="basic"
|
||||
fi
|
||||
}
|
||||
|
||||
tui_style() {
|
||||
if command -v gum >/dev/null 2>&1; then
|
||||
TUI_STYLE="gum"
|
||||
else
|
||||
TUI_STYLE="basic"
|
||||
tui_install_gum() {
|
||||
if ((OMERON_HAS_GUM)); then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! have pacman; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '\033[1;36mInstalling gum for a better TUI experience...\033[0m\n'
|
||||
if sudo_run pacman -S --needed --noconfirm gum >/dev/null 2>&1; then
|
||||
command -v gum >/dev/null 2>&1 && OMERON_HAS_GUM=1
|
||||
if ((OMERON_HAS_GUM)); then
|
||||
OMERON_TUI_MODE="gum"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
_strip_format() {
|
||||
local text="$1"
|
||||
text="${text//#\{bold\}/}"
|
||||
text="${text//#\{normal\}/}"
|
||||
text="${text//#\{italic\}/}"
|
||||
text="${text//#\{green\}/}"
|
||||
text="${text//#\{yellow\}/}"
|
||||
text="${text//#\{red\}/}"
|
||||
text="${text//#\{blue\}/}"
|
||||
text="${text//#\{cyan\}/}"
|
||||
text="${text//#\{magenta\}/}"
|
||||
text="${text//#\{white\}/}"
|
||||
text="${text//#\{underline\}/}"
|
||||
printf '%s\n' "$text"
|
||||
}
|
||||
|
||||
tui_choose() {
|
||||
local prompt="$1"
|
||||
shift
|
||||
|
||||
if [[ "$TUI_STYLE" == "gum" ]]; then
|
||||
if [[ "$OMERON_TUI_MODE" == "gum" ]]; then
|
||||
gum choose "$@"
|
||||
else
|
||||
select __choice in "$@"; do
|
||||
printf '%s\n' "$__choice"
|
||||
break
|
||||
elif [[ "$OMERON_TUI_MODE" == "whiptail" ]]; then
|
||||
local i=0
|
||||
local items=()
|
||||
for item in "$@"; do
|
||||
items+=("$i" "$item")
|
||||
((i++))
|
||||
done
|
||||
whiptail --menu "$prompt" 20 60 10 "${items[@]}" 3>&1 1>&2 2>&3
|
||||
else
|
||||
printf '\n==============================\n'
|
||||
printf ' %s\n' "$(_strip_format "$prompt")"
|
||||
printf '==============================\n'
|
||||
local i=0
|
||||
for item in "$@"; do
|
||||
printf ' [%d] %s\n' "$i" "$item"
|
||||
((i++))
|
||||
done
|
||||
printf ' [x] Cancel\n'
|
||||
printf '==============================\n'
|
||||
printf ' Choice: '
|
||||
read -r choice
|
||||
if [[ "$choice" == "x" ]]; then
|
||||
return 1
|
||||
fi
|
||||
if [[ "$choice" =~ ^[0-9]+$ ]] && ((choice < ${#@})); then
|
||||
printf '%s\n' "${!choice}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
tui_confirm() {
|
||||
local prompt="$1"
|
||||
prompt="$(_strip_format "$prompt")"
|
||||
|
||||
if [[ "$TUI_STYLE" == "gum" ]]; then
|
||||
if [[ "$OMERON_TUI_MODE" == "gum" ]]; then
|
||||
gum confirm "$prompt"
|
||||
elif [[ "$OMERON_TUI_MODE" == "whiptail" ]]; then
|
||||
whiptail --yesno "$prompt" 10 60
|
||||
else
|
||||
printf '%s [y/N]: ' "$prompt" >&2
|
||||
printf '\n>>> %s [Y/n]: ' "$prompt"
|
||||
read -r response
|
||||
response="${response:-Y}"
|
||||
[[ "$response" =~ ^[yY](es)?$ ]]
|
||||
fi
|
||||
}
|
||||
|
||||
tui_input() {
|
||||
local prompt="$1"
|
||||
prompt="$(_strip_format "$prompt")"
|
||||
|
||||
if [[ "$TUI_STYLE" == "gum" ]]; then
|
||||
if [[ "$OMERON_TUI_MODE" == "gum" ]]; then
|
||||
gum input --prompt "$prompt "
|
||||
elif [[ "$OMERON_TUI_MODE" == "whiptail" ]]; then
|
||||
whiptail --inputbox "$prompt" 10 60 3>&1 1>&2 2>&3
|
||||
else
|
||||
printf '%s: ' "$prompt" >&2
|
||||
printf '%s: ' "$prompt"
|
||||
read -r response
|
||||
printf '%s\n' "$response"
|
||||
fi
|
||||
@@ -57,11 +125,14 @@ tui_input() {
|
||||
|
||||
tui_password() {
|
||||
local prompt="$1"
|
||||
prompt="$(_strip_format "$prompt")"
|
||||
|
||||
if [[ "$TUI_STYLE" == "gum" ]]; then
|
||||
if [[ "$OMERON_TUI_MODE" == "gum" ]]; then
|
||||
gum input --password --prompt "$prompt "
|
||||
elif [[ "$OMERON_TUI_MODE" == "whiptail" ]]; then
|
||||
whiptail --passwordbox "$prompt" 10 60 3>&1 1>&2 2>&3
|
||||
else
|
||||
printf '%s: ' "$prompt" >&2
|
||||
printf '%s: ' "$prompt"
|
||||
read -rs response
|
||||
printf '\n'
|
||||
printf '%s\n' "$response"
|
||||
@@ -71,21 +142,29 @@ tui_password() {
|
||||
tui_multiselect() {
|
||||
local prompt="$1"
|
||||
shift
|
||||
prompt="$(_strip_format "$prompt")"
|
||||
|
||||
if [[ "$TUI_STYLE" == "gum" ]]; then
|
||||
if [[ "$OMERON_TUI_MODE" == "gum" ]]; then
|
||||
gum choose --no-limit "$@"
|
||||
else
|
||||
printf '%s (space-separated indices):\n' "$prompt" >&2
|
||||
printf '\n==============================\n'
|
||||
printf ' %s (space-separated indices)\n' "$prompt"
|
||||
printf '==============================\n'
|
||||
local i=0
|
||||
local items=("$@")
|
||||
for item in "${items[@]}"; do
|
||||
printf ' [%d] %s\n' "$i" "$item" >&2
|
||||
printf ' [%d] %s\n' "$i" "$item"
|
||||
((i++))
|
||||
done
|
||||
printf '> ' >&2
|
||||
printf ' [x] Done\n'
|
||||
printf '==============================\n'
|
||||
printf ' Select: '
|
||||
read -ra selections
|
||||
for idx in "${selections[@]}"; do
|
||||
printf '%s\n' "${items[$idx]}"
|
||||
local selection
|
||||
for selection in "${selections[@]}"; do
|
||||
if [[ "$selection" =~ ^[0-9]+$ ]] && ((selection < ${#items[@]})); then
|
||||
printf '%s\n' "${items[$selection]}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
@@ -93,69 +172,115 @@ tui_multiselect() {
|
||||
tui_spin() {
|
||||
local title="$1"
|
||||
shift
|
||||
title="$(_strip_format "$title")"
|
||||
|
||||
if [[ "$TUI_STYLE" == "gum" ]]; then
|
||||
if [[ "$OMERON_TUI_MODE" == "gum" ]]; then
|
||||
gum spin --title "$title" -- "$@"
|
||||
else
|
||||
printf '%s... ' "$title" >&2
|
||||
printf ' ▶ %s ... ' "$title"
|
||||
"$@"
|
||||
printf 'done\n' >&2
|
||||
local rc=$?
|
||||
if ((rc == 0)); then
|
||||
printf '\033[1;32mOK\033[0m\n'
|
||||
else
|
||||
printf '\033[1;31mFAILED\033[0m\n'
|
||||
fi
|
||||
return $rc
|
||||
fi
|
||||
}
|
||||
|
||||
tui_header() {
|
||||
local title="$1"
|
||||
local title="$(_strip_format "$1")"
|
||||
local len="${#title}"
|
||||
|
||||
if [[ "$TUI_STYLE" == "gum" ]]; then
|
||||
if [[ "$OMERON_TUI_MODE" == "gum" ]]; then
|
||||
gum style --foreground 212 --border-foreground 212 --border double --align center --width 60 --margin "1 2" --padding "1 2" "$title"
|
||||
else
|
||||
printf '╔══════════════════════════════════════════════════╗\n'
|
||||
printf '║ %s\n' "$title"
|
||||
printf '╚══════════════════════════════════════════════════╝\n'
|
||||
local width=50
|
||||
local pad=$(( (width - len) / 2 ))
|
||||
[[ $pad -lt 2 ]] && pad=2
|
||||
|
||||
local line
|
||||
printf -v line '%*s' "$width" '' && line="${line// /═}"
|
||||
printf '\n'
|
||||
printf '\033[1;36m%s\033[0m\n' "$line"
|
||||
printf '\033[1;36m║\033[0m%*s%s%*s\033[1;36m║\033[0m\n' $pad '' "$title" $((width - pad - len)) ''
|
||||
printf '\033[1;36m%s\033[0m\n' "$line"
|
||||
printf '\n'
|
||||
fi
|
||||
}
|
||||
|
||||
tui_status() {
|
||||
local ok="$1"
|
||||
local message="$2"
|
||||
message="$(_strip_format "$message")"
|
||||
|
||||
if [[ "$TUI_STYLE" == "gum" ]]; then
|
||||
if [[ "$ok" == "0" ]]; then
|
||||
gum style --foreground 10 "✓ $message"
|
||||
if [[ "$OMERON_TUI_MODE" == "gum" ]]; then
|
||||
if ((ok == 0)); then
|
||||
gum style --foreground 10 " ✓ $message"
|
||||
else
|
||||
gum style --foreground 9 "✗ $message"
|
||||
gum style --foreground 9 " ✗ $message"
|
||||
fi
|
||||
else
|
||||
if [[ "$ok" == "0" ]]; then
|
||||
printf '✓ %s\n' "$message"
|
||||
if ((ok == 0)); then
|
||||
printf ' \033[1;32m✓\033[0m %s\n' "$message"
|
||||
else
|
||||
printf '✗ %s\n' "$message"
|
||||
printf ' \033[1;31m✗\033[0m %s\n' "$message"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
tui_file_pick() {
|
||||
local directory="$1"
|
||||
local pattern="$2"
|
||||
|
||||
if [[ "$TUI_STYLE" == "gum" ]]; then
|
||||
find "$directory" -maxdepth 1 -type f -name "$pattern" -printf '%f\n' | sort | gum choose
|
||||
else
|
||||
local files=()
|
||||
while IFS= read -r -d '' f; do
|
||||
files+=("$(basename "$f")")
|
||||
done < <(find "$directory" -maxdepth 1 -type f -name "$pattern" -print0 | sort -z)
|
||||
select __file in "${files[@]}"; do
|
||||
printf '%s\n' "$__file"
|
||||
break
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
tui_format() {
|
||||
if [[ "$TUI_STYLE" == "gum" ]]; then
|
||||
if [[ "$OMERON_TUI_MODE" == "gum" ]]; then
|
||||
gum format "$@"
|
||||
else
|
||||
printf '%s\n' "$*"
|
||||
local line
|
||||
for line in "$@"; do
|
||||
_strip_format "$line"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
tui_info() {
|
||||
local message="$(_strip_format "$1")"
|
||||
printf ' \033[1;34m▶\033[0m %s\n' "$message"
|
||||
}
|
||||
|
||||
tui_success() {
|
||||
local message="$(_strip_format "$1")"
|
||||
printf ' \033[1;32m✓\033[0m %s\n' "$message"
|
||||
}
|
||||
|
||||
tui_warn() {
|
||||
local message="$(_strip_format "$1")"
|
||||
printf ' \033[1;33m⚠\033[0m %s\n' "$message" >&2
|
||||
}
|
||||
|
||||
tui_error() {
|
||||
local message="$(_strip_format "$1")"
|
||||
printf ' \033[1;31m✗\033[0m %s\n' "$message" >&2
|
||||
}
|
||||
|
||||
tui_bold() {
|
||||
if [[ "$OMERON_TUI_MODE" == "gum" ]]; then
|
||||
gum style --bold "$1"
|
||||
else
|
||||
printf '\033[1m%s\033[0m' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
tui_separator() {
|
||||
if [[ "$OMERON_TUI_MODE" == "gum" ]]; then
|
||||
gum style --foreground 240 "────────────────────────────────────────"
|
||||
else
|
||||
printf ' \033[2m────────────────────────────────────────\033[0m\n'
|
||||
fi
|
||||
}
|
||||
|
||||
tui_list() {
|
||||
local items=("$@")
|
||||
local item
|
||||
for item in "${items[@]}"; do
|
||||
printf ' \033[1;36m•\033[0m %s\n' "$(_strip_format "$item")"
|
||||
done
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user