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:
179
install.sh
179
install.sh
@@ -65,11 +65,9 @@ EOF
|
||||
|
||||
list_modules() {
|
||||
echo "Available modules:"
|
||||
echo ""
|
||||
for module in $(module_list); do
|
||||
local rel_path="${module#$OMERON_MODULE_DIR/}"
|
||||
rel_path="${rel_path%.sh}"
|
||||
|
||||
local description=""
|
||||
if source "$module" 2>/dev/null && declare -F "module_description" >/dev/null 2>&1; then
|
||||
description="$(module_description)"
|
||||
@@ -82,47 +80,59 @@ list_modules() {
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--fresh)
|
||||
OMERON_FRESH_INSTALL=1
|
||||
shift
|
||||
;;
|
||||
--modules)
|
||||
IFS=',' read -ra RUN_MODULES <<< "$2"
|
||||
shift 2
|
||||
;;
|
||||
--skip)
|
||||
IFS=',' read -ra SKIP_MODULES <<< "$2"
|
||||
shift 2
|
||||
;;
|
||||
--skip-packages)
|
||||
SKIP_MODULES+=("core/packages")
|
||||
shift
|
||||
;;
|
||||
--with-sddm)
|
||||
WITH_SDDM=1
|
||||
shift
|
||||
;;
|
||||
--list-modules)
|
||||
list_modules
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1" >&2
|
||||
usage
|
||||
;;
|
||||
--fresh) OMERON_FRESH_INSTALL=1; shift ;;
|
||||
--modules) IFS=',' read -ra RUN_MODULES <<< "$2"; shift 2 ;;
|
||||
--skip) IFS=',' read -ra SKIP_MODULES <<< "$2"; shift 2 ;;
|
||||
--skip-packages) SKIP_MODULES+=("core/packages"); shift ;;
|
||||
--with-sddm) WITH_SDDM=1; shift ;;
|
||||
--list-modules) list_modules ;;
|
||||
-h|--help) usage ;;
|
||||
*) echo "Unknown option: $1" >&2; usage ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
detect_environment() {
|
||||
tui_detect
|
||||
|
||||
if ((!OMERON_HAS_GUM)) && have pacman; then
|
||||
printf '\033[1;36m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n'
|
||||
printf '\033[1;36m Ꮎ Ꮇ Ꭼ Ꮢ Ꮎ Ꮑ — Modular System Setup Framework\033[0m\n'
|
||||
printf '\033[1;36m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n'
|
||||
printf '\n'
|
||||
printf ' \033[1;33mgum is not installed.\033[0m Installing it enables the full TUI experience.\n'
|
||||
printf '\n'
|
||||
|
||||
if tui_install_gum; then
|
||||
printf ' \033[1;32m✓ gum installed!\033[0m\n'
|
||||
else
|
||||
if ((OMERON_HAS_WHIPTAIL)); then
|
||||
printf ' \033[1;33musing whiptail as fallback\033[0m\n'
|
||||
else
|
||||
printf ' \033[1;33musing basic text mode (install gum for UI: pacman -S gum)\033[0m\n'
|
||||
fi
|
||||
fi
|
||||
printf '\n'
|
||||
fi
|
||||
|
||||
if ((!OMERON_FRESH_INSTALL)) && ((${#RUN_MODULES[@]} == 0)); then
|
||||
log_info "Checking for fresh install..."
|
||||
if is_fresh_install; then
|
||||
OMERON_FRESH_INSTALL=1
|
||||
export OMERON_FRESH_INSTALL
|
||||
fi
|
||||
fi
|
||||
|
||||
if ((OMERON_FRESH_INSTALL)) && ((!OMERON_HAS_GUM)) && have pacman; then
|
||||
tui_install_gum 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
collect_modules() {
|
||||
if ((${#RUN_MODULES[@]})); then
|
||||
local result=()
|
||||
for mod in "${RUN_MODULES[@]}"; do
|
||||
result+=("$OMERON_MODULE_DIR/$mod.sh")
|
||||
printf '%s\n' "$OMERON_MODULE_DIR/$mod.sh"
|
||||
done
|
||||
printf '%s\n' "${result[@]}"
|
||||
return
|
||||
fi
|
||||
|
||||
@@ -133,8 +143,6 @@ collect_modules() {
|
||||
modules=("${DEFAULT_MODULES[@]}")
|
||||
fi
|
||||
|
||||
local module_order=()
|
||||
|
||||
for mod in "${modules[@]}"; do
|
||||
local skip=0
|
||||
for skipped in "${SKIP_MODULES[@]}"; do
|
||||
@@ -143,16 +151,11 @@ collect_modules() {
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
[[ "$mod" == "core/sddm" && "$WITH_SDDM" -eq 0 && "$OMERON_FRESH_INSTALL" -eq 0 ]] && skip=1
|
||||
|
||||
((skip)) && continue
|
||||
|
||||
local module_file="$OMERON_MODULE_DIR/$mod.sh"
|
||||
[[ -f "$module_file" ]] && module_order+=("$module_file")
|
||||
[[ -f "$module_file" ]] && printf '%s\n' "$module_file"
|
||||
done
|
||||
|
||||
printf '%s\n' "${module_order[@]}"
|
||||
}
|
||||
|
||||
collect_all_interactive() {
|
||||
@@ -182,7 +185,7 @@ collect_all_interactive() {
|
||||
if declare -F "module_required" >/dev/null 2>&1; then
|
||||
source "$module_file"
|
||||
if module_required; then
|
||||
log_info "Required module - will run"
|
||||
tui_info "Required module — will run"
|
||||
module_order+=("$module_file")
|
||||
((idx++))
|
||||
continue
|
||||
@@ -194,7 +197,6 @@ collect_all_interactive() {
|
||||
else
|
||||
log_info "Skipped"
|
||||
fi
|
||||
|
||||
((idx++))
|
||||
done
|
||||
|
||||
@@ -203,44 +205,41 @@ collect_all_interactive() {
|
||||
|
||||
show_banner() {
|
||||
tui_header " Ꮎ Ꮇ Ꭼ Ꮢ Ꮎ Ꮑ "
|
||||
tui_format ""
|
||||
tui_format "#{bold}Modular System Setup Framework#{normal}"
|
||||
tui_format "#{italic}Arch / Hyprland / CachyOS#{normal}"
|
||||
tui_format ""
|
||||
tui_info "Modular System Setup Framework"
|
||||
tui_info "Arch / Hyprland / CachyOS"
|
||||
printf '\n'
|
||||
|
||||
if ((OMERON_FRESH_INSTALL)); then
|
||||
tui_format "#{bold}#{yellow}⚡ FRESH INSTALL MODE#{normal}"
|
||||
tui_format "Will install Hyprland, GPU drivers, and all dependencies."
|
||||
tui_format ""
|
||||
tui_warn "FRESH INSTALL MODE"
|
||||
tui_info "Will install Hyprland, GPU drivers, and all dependencies."
|
||||
printf '\n'
|
||||
fi
|
||||
}
|
||||
|
||||
show_summary() {
|
||||
local modules=("$@")
|
||||
|
||||
tui_format ""
|
||||
tui_format "#{bold}Installation Summary:#{normal}"
|
||||
tui_format " Modules to run: ${#modules[@]}"
|
||||
tui_separator
|
||||
tui_bold "Installation Summary"
|
||||
tui_info "$(tui_bold "${#modules[@]}") module(s) to run"
|
||||
|
||||
local gpu
|
||||
gpu="$(detect_gpu)"
|
||||
tui_format " Detected GPU: ${gpu}"
|
||||
tui_info "Detected GPU: $(tui_bold "$gpu")"
|
||||
|
||||
if ((${#modules[@]})); then
|
||||
tui_format ""
|
||||
tui_format "#{bold}Steps:#{normal}"
|
||||
printf '\n'
|
||||
tui_bold "Steps:"
|
||||
local i=1
|
||||
for mod in "${modules[@]}"; do
|
||||
local name
|
||||
name="$(basename "$mod" .sh)"
|
||||
tui_format " $i. ${name}"
|
||||
printf ' \033[1;36m%d.\033[0m %s\n' "$i" "$(basename "$mod" .sh)"
|
||||
((i++))
|
||||
done
|
||||
fi
|
||||
|
||||
tui_format ""
|
||||
printf '\n'
|
||||
if ! tui_confirm "Proceed with installation?"; then
|
||||
tui_format "#{bold}Installation cancelled by user.#{normal}"
|
||||
tui_bold "Installation cancelled."
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
@@ -251,23 +250,14 @@ main() {
|
||||
|
||||
parse_args "$@"
|
||||
|
||||
tui_style
|
||||
show_banner
|
||||
|
||||
mkdir -p "$(dirname "$OMERON_LOG_FILE")"
|
||||
log_info "Omeron installation started"
|
||||
log_info "Log file: $OMERON_LOG_FILE"
|
||||
log_info "Project: $OMERON_ROOT"
|
||||
log_info "Fresh install: $OMERON_FRESH_INSTALL"
|
||||
|
||||
if ((!OMERON_FRESH_INSTALL)) && ((${#RUN_MODULES[@]} == 0)); then
|
||||
log_info "Checking for fresh install..."
|
||||
if is_fresh_install; then
|
||||
OMERON_FRESH_INSTALL=1
|
||||
export OMERON_FRESH_INSTALL
|
||||
log_info "Fresh system detected — switching to full setup mode"
|
||||
fi
|
||||
fi
|
||||
detect_environment
|
||||
|
||||
show_banner
|
||||
|
||||
local modules_to_run=()
|
||||
if ((${#RUN_MODULES[@]})); then
|
||||
@@ -277,7 +267,7 @@ main() {
|
||||
fi
|
||||
|
||||
if ((${#modules_to_run[@]} == 0)); then
|
||||
log_warn "No modules selected. Nothing to do."
|
||||
tui_warn "No modules selected. Nothing to do."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -291,47 +281,46 @@ main() {
|
||||
if source "$module_path" 2>/dev/null && declare -F "module_description" >/dev/null 2>&1; then
|
||||
description="$(module_description)"
|
||||
fi
|
||||
|
||||
printf '\n'
|
||||
log_step "$idx" "$total" "${description:-$(basename "$module_path" .sh)}"
|
||||
|
||||
module_run "$module_path" || {
|
||||
local rc=$?
|
||||
if declare -F "module_required" >/dev/null 2>&1; then
|
||||
source "$module_path"
|
||||
if module_required 2>/dev/null; then
|
||||
log_error "Required module failed. Aborting."
|
||||
tui_error "Required module failed. Aborting."
|
||||
exit $rc
|
||||
fi
|
||||
fi
|
||||
log_warn "Module completed with warnings"
|
||||
tui_warn "Module completed with warnings"
|
||||
}
|
||||
((idx++))
|
||||
done
|
||||
|
||||
printf '\n'
|
||||
tui_header " Ꮎ Ꮇ Ꭼ Ꮢ Ꮎ Ꮑ "
|
||||
tui_format ""
|
||||
tui_format "#{bold}#{green}Installation Complete!#{normal}"
|
||||
tui_format ""
|
||||
tui_format "Log: ${OMERON_LOG_FILE}"
|
||||
tui_format ""
|
||||
tui_success "Installation Complete!"
|
||||
printf '\n'
|
||||
tui_info "Log: ${OMERON_LOG_FILE}"
|
||||
printf '\n'
|
||||
|
||||
if ((OMERON_FRESH_INSTALL)); then
|
||||
tui_format "#{bold}Your system is ready for Hyprland!#{normal}"
|
||||
tui_format ""
|
||||
tui_format " Start Hyprland: Hyprland"
|
||||
tui_format " Or enable SDDM: sudo systemctl enable --now sddm"
|
||||
tui_format " Reload config: hyprctl reload"
|
||||
tui_bold "Your system is ready for Hyprland!"
|
||||
tui_list \
|
||||
"Start Hyprland: Hyprland" \
|
||||
"Or enable SDDM: sudo systemctl enable --now sddm" \
|
||||
"Reload config: hyprctl reload"
|
||||
else
|
||||
tui_format "What next?"
|
||||
tui_format " - Reload config with: hyprctl reload"
|
||||
tui_format " - Re-run installer: ./install.sh"
|
||||
tui_bold "What next?"
|
||||
tui_list \
|
||||
"Reload config: hyprctl reload" \
|
||||
"Re-run installer: ./install.sh"
|
||||
fi
|
||||
|
||||
tui_format ""
|
||||
printf '\n'
|
||||
|
||||
if have notify-send; then
|
||||
notify-send "Omeron" "Installation complete! ✨" >/dev/null 2>&1 || true
|
||||
notify-send "Omeron" "Installation complete!" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
245
lib/tui.sh
245
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
|
||||
if [[ "$OMERON_TUI_MODE" == "gum" ]]; then
|
||||
if ((ok == 0)); then
|
||||
gum style --foreground 10 " ✓ $message"
|
||||
else
|
||||
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
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ module_should_skip() { return 1; }
|
||||
module_prereqs() {
|
||||
require pacman pacman
|
||||
is_arch || {
|
||||
log_error "This module requires an Arch-based system"
|
||||
tui_error "This module requires an Arch-based system"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ module_main() {
|
||||
local all_packages=()
|
||||
|
||||
if ((OMERON_FRESH_INSTALL)); then
|
||||
tui_format "#{bold}Fresh install mode — installing everything#{normal}"
|
||||
tui_bold "Fresh install mode — installing everything"
|
||||
groups=(
|
||||
"hyprland"
|
||||
"gpu"
|
||||
@@ -34,7 +34,7 @@ module_main() {
|
||||
"development"
|
||||
)
|
||||
else
|
||||
tui_format "#{bold}Existing system — checking for missing packages#{normal}"
|
||||
tui_bold "Existing system — checking for missing packages"
|
||||
groups=(
|
||||
"hyprland"
|
||||
"audio"
|
||||
@@ -64,37 +64,38 @@ module_main() {
|
||||
fi
|
||||
done
|
||||
|
||||
tui_format ""
|
||||
tui_format "#{bold}Package Summary:#{normal}"
|
||||
tui_format " Total packages: ${total}"
|
||||
tui_format " Already installed: ${existing}"
|
||||
tui_format " To install: ${missing}"
|
||||
tui_format ""
|
||||
tui_separator
|
||||
tui_bold "Package Summary"
|
||||
tui_info "Total packages: $(tui_bold "$total")"
|
||||
tui_success "Already installed: $(tui_bold "$existing")"
|
||||
if ((missing > 0)); then
|
||||
tui_warn "To install: $(tui_bold "$missing")"
|
||||
fi
|
||||
printf '\n'
|
||||
|
||||
if ((missing == 0)); then
|
||||
log_success "All packages already installed"
|
||||
tui_success "All packages already installed"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! tui_confirm "Install ${missing} missing packages?"; then
|
||||
log_info "Package installation skipped by user"
|
||||
tui_info "Package installation skipped by user"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ((OMERON_FRESH_INSTALL)); then
|
||||
log_info "Running system update first..."
|
||||
tui_info "Running system update first..."
|
||||
sudo_run pacman -Syu --noconfirm 2>&1 | tail -3 || true
|
||||
fi
|
||||
|
||||
local pacman_pkgs=()
|
||||
local aur_pkgs=()
|
||||
local gpu_pkgs=()
|
||||
local pkg
|
||||
|
||||
for pkg in "${all_packages[@]}"; do
|
||||
if is_package_installed "$pkg"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if pacman -Si "$pkg" >/dev/null 2>&1; then
|
||||
pacman_pkgs+=("$pkg")
|
||||
else
|
||||
@@ -103,22 +104,22 @@ module_main() {
|
||||
done
|
||||
|
||||
if ((${#pacman_pkgs[@]})); then
|
||||
log_info "Installing ${#pacman_pkgs[@]} pacman packages..."
|
||||
tui_info "Installing ${#pacman_pkgs[@]} pacman packages..."
|
||||
tui_spin "Installing core packages..." sudo_run pacman -S --needed --noconfirm "${pacman_pkgs[@]}"
|
||||
log_success "Core packages installed"
|
||||
tui_success "Core packages installed"
|
||||
fi
|
||||
|
||||
if ((${#aur_pkgs[@]})); then
|
||||
if have paru || have yay; then
|
||||
log_info "Installing ${#aur_pkgs[@]} AUR packages..."
|
||||
install_aur "${aur_pkgs[@]}" || log_warn "Some AUR packages may not have been installed"
|
||||
tui_info "Installing ${#aur_pkgs[@]} AUR packages..."
|
||||
install_aur "${aur_pkgs[@]}" || tui_warn "Some AUR packages may not have been installed"
|
||||
else
|
||||
log_warn "No AUR helper found. Install manually: ${aur_pkgs[*]}"
|
||||
tui_warn "No AUR helper found. Install manually: ${aur_pkgs[*]}"
|
||||
fi
|
||||
fi
|
||||
|
||||
log_success "Package installation complete"
|
||||
log_info "Installed: $((missing - ${#aur_pkgs[@]})) | AUR/optional: ${#aur_pkgs[@]}"
|
||||
tui_success "Package installation complete"
|
||||
tui_info "Installed: $((missing - ${#aur_pkgs[@]})) | AUR/optional: ${#aur_pkgs[@]}"
|
||||
}
|
||||
|
||||
get_group_packages() {
|
||||
|
||||
@@ -13,7 +13,7 @@ module_should_skip() { return 1; }
|
||||
module_prereqs() {
|
||||
require pacman pacman
|
||||
is_arch || {
|
||||
log_error "This module requires an Arch-based system"
|
||||
tui_error "This module requires an Arch-based system"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
@@ -25,50 +25,47 @@ module_main() {
|
||||
export OMERON_DETECTED_GPU
|
||||
|
||||
system_summary
|
||||
printf '\n'
|
||||
|
||||
if is_fresh_install; then
|
||||
OMERON_FRESH_INSTALL=1
|
||||
export OMERON_FRESH_INSTALL
|
||||
|
||||
tui_format ""
|
||||
tui_format "#{bold}#{yellow}⚠ Fresh system detected!#{normal}"
|
||||
tui_format "Hyprland is not installed and no desktop session is running."
|
||||
tui_format ""
|
||||
tui_warn "Fresh system detected!"
|
||||
tui_info "Hyprland is not installed and no desktop session is running."
|
||||
printf '\n'
|
||||
|
||||
local gpu_name
|
||||
gpu_name="$(lspci -nn 2>/dev/null | grep -i 'vga\|3d\|display' | sed 's/.*: //' | head -1 || echo "unknown")"
|
||||
gpu_name="$(lspci -nn 2>/dev/null | grep -E '\[0300\]|\[0302\]|\[0380\]' | sed 's/.*: //' | head -1 || echo "unknown")"
|
||||
|
||||
tui_format "#{bold}This installation will:#{normal}"
|
||||
tui_format " • Hyprland compositor + tools"
|
||||
tui_format " • GPU drivers for: ${gpu_name}"
|
||||
tui_format " • Audio system (PipeWire)"
|
||||
tui_format " • Network services (NetworkManager)"
|
||||
tui_format " • Fonts and icon themes"
|
||||
tui_format " • Dotfiles deployment"
|
||||
tui_format ""
|
||||
tui_bold "This installation will:"
|
||||
tui_list \
|
||||
"Hyprland compositor + tools" \
|
||||
"GPU drivers for: ${gpu_name}" \
|
||||
"Audio system (PipeWire)" \
|
||||
"Network services (NetworkManager)" \
|
||||
"Fonts and icon themes" \
|
||||
"Dotfiles deployment"
|
||||
printf '\n'
|
||||
|
||||
if ! tui_confirm "Proceed with full system setup?"; then
|
||||
log_info "Fresh installation aborted by user"
|
||||
tui_info "Fresh installation aborted by user"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! have paru && ! have yay; then
|
||||
log_info "No AUR helper found. Installing paru..."
|
||||
tui_info "No AUR helper found. Installing paru..."
|
||||
install_aur_helper
|
||||
fi
|
||||
|
||||
if ! have gum; then
|
||||
log_info "Installing gum for better TUI..."
|
||||
sudo_run pacman -S --needed --noconfirm gum 2>/dev/null || true
|
||||
tui_style
|
||||
if ((!OMERON_HAS_GUM)) && have pacman; then
|
||||
tui_install_gum 2>/dev/null || true
|
||||
fi
|
||||
|
||||
log_success "Preflight complete — ready for full installation"
|
||||
tui_success "Preflight complete — ready for full installation"
|
||||
else
|
||||
tui_format ""
|
||||
tui_format "#{bold}#{green}✓ Existing Hyprland system detected#{normal}"
|
||||
tui_format "Will check for missing packages and updates."
|
||||
tui_format ""
|
||||
tui_success "Existing Hyprland system detected"
|
||||
tui_info "Will check for missing packages and updates."
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -84,7 +81,7 @@ install_aur_helper() {
|
||||
local build_dir
|
||||
build_dir="$(mktemp -d)"
|
||||
|
||||
log_info "Building paru from AUR..."
|
||||
tui_info "Building paru from AUR..."
|
||||
sudo_run pacman -S --needed --noconfirm rustup 2>/dev/null || true
|
||||
|
||||
if have rustup; then
|
||||
@@ -93,10 +90,10 @@ install_aur_helper() {
|
||||
|
||||
if git clone https://aur.archlinux.org/paru.git "$build_dir/paru" 2>/dev/null; then
|
||||
(cd "$build_dir/paru" && makepkg -si --needed --noconfirm) 2>&1 | tail -5 || {
|
||||
log_warn "paru build failed, trying yay..."
|
||||
tui_warn "paru build failed, trying yay..."
|
||||
if git clone https://aur.archlinux.org/yay.git "$build_dir/yay" 2>/dev/null; then
|
||||
(cd "$build_dir/yay" && makepkg -si --needed --noconfirm) 2>&1 | tail -5 || {
|
||||
log_warn "yay build failed too. Install manually: paru -S paru-bin"
|
||||
tui_warn "yay build failed too. Install manually: paru -S paru-bin"
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ module_main() {
|
||||
local homelab_config_dir="$HOME/.config/homelab"
|
||||
local homelab_config_file="$homelab_config_dir/config.yaml"
|
||||
|
||||
tui_format "#{bold}Homelab Control Center Setup#{normal}"
|
||||
tui_format "This configures SSH access and connection details to your Unraid server."
|
||||
tui_format ""
|
||||
tui_bold "Homelab Control Center Setup"
|
||||
tui_info "This configures SSH access and connection details to your Unraid server."
|
||||
printf '\n'
|
||||
|
||||
local server_address server_username
|
||||
|
||||
@@ -59,13 +59,12 @@ CONFIG
|
||||
|
||||
log_success "Homelab configuration saved to $homelab_config_file"
|
||||
|
||||
tui_format ""
|
||||
tui_format "#{bold}Configuration Summary:#{normal}"
|
||||
tui_format " Server address: ${server_address}"
|
||||
tui_format " SSH username: ${server_username}"
|
||||
tui_format ""
|
||||
tui_format "You can edit the configuration anytime at:"
|
||||
tui_format " ${homelab_config_file}"
|
||||
printf '\n'
|
||||
tui_bold "Configuration Summary:"
|
||||
tui_info " Server address: ${server_address}"
|
||||
tui_info " SSH username: ${server_username}"
|
||||
printf '\n'
|
||||
tui_info "You can edit the configuration anytime at: ${homelab_config_file}"
|
||||
|
||||
if tui_confirm "Test SSH connection to ${server_username}@${server_address}?"; then
|
||||
log_info "Testing SSH connection..."
|
||||
|
||||
@@ -14,14 +14,13 @@ module_prereqs() {
|
||||
module_main() {
|
||||
log_section "Optional Software Selection"
|
||||
|
||||
tui_format "#{bold}Select software to install:#{normal}"
|
||||
tui_format "Use space to select, enter to confirm."
|
||||
tui_format ""
|
||||
tui_bold "Select optional software to install:"
|
||||
tui_info "Use space (gum) or enter indices (basic) to select."
|
||||
printf '\n'
|
||||
|
||||
local choices
|
||||
choices="$(
|
||||
gum choose --no-limit \
|
||||
--header "Select optional packages (space to toggle, enter to confirm)" \
|
||||
tui_multiselect "Software Selection" \
|
||||
"Obsidian" \
|
||||
"Neovim" \
|
||||
"Visual Studio Code" \
|
||||
@@ -31,7 +30,7 @@ module_main() {
|
||||
"VLC" \
|
||||
"PipeWire Tools" \
|
||||
"Docker" \
|
||||
"Blender" 2>&1
|
||||
"Blender"
|
||||
)"
|
||||
|
||||
if [[ -z "$choices" ]]; then
|
||||
|
||||
Reference in New Issue
Block a user