Breathing-block status indicator (blue/green/red) #10
+46
-15
@@ -101,43 +101,74 @@ ui_quiet_run() {
|
|||||||
|
|
||||||
before=$(wc -l < "$log" 2>/dev/null || echo 0)
|
before=$(wc -l < "$log" 2>/dev/null || echo 0)
|
||||||
|
|
||||||
# Child writes to the log; we draw a LIVE line: spinner frame + the package
|
# ---- status BLOCK (TTY-safe: a background-coloured SPACE, never a glyph) --
|
||||||
# dnf is currently working on. gum spin was tried here first but rendered
|
# busy: blue block that breathes (bg-colour ramp) + current dnf action
|
||||||
# as a frozen block on the real VM ("looks like it hangs") — this variant
|
# ok : solid green block + package count
|
||||||
# animates independently of gum and shows real progress, not just motion.
|
# fail: solid RED block (spec change from the X, per YuKu)
|
||||||
|
# Colour-depth aware: 256 -> ramp, 16 -> blue/bright-blue, none -> ASCII.
|
||||||
|
local depth=0 nc
|
||||||
|
nc=$(tput colors 2>/dev/null || echo 0)
|
||||||
|
case "$nc" in ''|*[!0-9]*) nc=0 ;; esac
|
||||||
|
if (( nc >= 256 )); then depth=256
|
||||||
|
elif (( nc >= 8 )); then depth=16
|
||||||
|
fi
|
||||||
|
|
||||||
|
local -a bg=()
|
||||||
|
case "$depth" in
|
||||||
|
256) bg=( "48;5;17" "48;5;19" "48;5;21" "48;5;27" "48;5;33" "48;5;27" "48;5;21" "48;5;19" ) ;;
|
||||||
|
16) bg=( "44" "104" "44" "104" ) ;;
|
||||||
|
esac
|
||||||
|
local -a ch=( "|" "/" "-" "\\" )
|
||||||
|
|
||||||
"$@" >>"$log" 2>&1 &
|
"$@" >>"$log" 2>&1 &
|
||||||
local pid=$!
|
local pid=$!
|
||||||
# ASCII frames ONLY: braille/em-dash are multibyte UTF-8 and rendered as
|
|
||||||
# garbage letters when the session locale is not UTF-8 (VM-proven: the
|
|
||||||
# spinner appeared as changing random chars, not as a spinner).
|
|
||||||
local frames=( '|' '/' '-' '\\' )
|
|
||||||
local f=0 cur
|
local f=0 cur
|
||||||
while kill -0 "$pid" 2>/dev/null; do
|
while kill -0 "$pid" 2>/dev/null; do
|
||||||
cur=$(sed -n "$((before+1)),\$p" "$log" 2>/dev/null \
|
cur=$(sed -n "$((before+1)),\$p" "$log" 2>/dev/null \
|
||||||
| grep -E '^(Installing|Upgrading|Downgrading|Removing|Verifying|Preparing|Cleanup):' \
|
| grep -E '^(Installing|Upgrading|Downgrading|Removing|Verifying|Preparing|Cleanup):' \
|
||||||
| tail -n 1 || true)
|
| tail -n 1 || true)
|
||||||
if [[ -n "$cur" ]]; then
|
if (( depth >= 16 )); then
|
||||||
printf '\r %s %s: %s' "${frames[$f]}" "$msg" "$cur" >&2
|
if [[ -n "$cur" ]]; then
|
||||||
|
printf '\r\033[%sm \033[0m %s: %s' "${bg[$(( f % ${#bg[@]} ))]}" "$msg" "$cur" >&2
|
||||||
|
else
|
||||||
|
printf '\r\033[%sm \033[0m %s' "${bg[$(( f % ${#bg[@]} ))]}" "$msg" >&2
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
printf '\r %s %s' "${frames[$f]}" "$msg" >&2
|
if [[ -n "$cur" ]]; then
|
||||||
|
printf '\r %s %s: %s' "${ch[$(( f % ${#ch[@]} ))]}" "$msg" "$cur" >&2
|
||||||
|
else
|
||||||
|
printf '\r %s %s' "${ch[$(( f % ${#ch[@]} ))]}" "$msg" >&2
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
f=$(( (f + 1) % ${#frames[@]} ))
|
f=$(( f + 1 ))
|
||||||
sleep 0.15
|
sleep 0.15
|
||||||
done
|
done
|
||||||
|
|
||||||
local rc=0
|
local rc=0
|
||||||
wait "$pid" || rc=$?
|
wait "$pid" || rc=$?
|
||||||
# wipe the progress line before printing the result
|
|
||||||
printf '\r%*s\r' 100 '' >&2
|
printf '\r%*s\r' 100 '' >&2
|
||||||
|
|
||||||
if (( rc == 0 )); then
|
if (( rc == 0 )); then
|
||||||
n=$(sed -n "$((before+1)),\$p" "$log" 2>/dev/null | grep -c '^Installing:' || true)
|
n=$(sed -n "$((before+1)),\$p" "$log" 2>/dev/null | grep -c '^Installing:' || true)
|
||||||
local plural="s"
|
local plural="s"
|
||||||
(( n == 1 )) && plural=""
|
(( n == 1 )) && plural=""
|
||||||
ok "$msg (${n} package${plural})"
|
if (( depth == 256 )); then
|
||||||
|
printf '\033[48;5;34m \033[0m %s (%s package%s)\n' "$msg" "$n" "$plural" >&2
|
||||||
|
elif (( depth == 16 )); then
|
||||||
|
printf '\033[42m \033[0m %s (%s package%s)\n' "$msg" "$n" "$plural" >&2
|
||||||
|
else
|
||||||
|
ok "$msg (${n} package${plural})"
|
||||||
|
fi
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
color_red " Failed: $msg"
|
if (( depth == 256 )); then
|
||||||
|
printf '\033[48;5;196m \033[0m %s\n' "$msg" >&2
|
||||||
|
elif (( depth == 16 )); then
|
||||||
|
printf '\033[41m \033[0m %s\n' "$msg" >&2
|
||||||
|
else
|
||||||
|
color_red " Failed: $msg"
|
||||||
|
fi
|
||||||
|
color_red " (dnf reported a transaction failure)"
|
||||||
color_red " Last lines from $log:"
|
color_red " Last lines from $log:"
|
||||||
sed -n "$((before+1)),\$p" "$log" 2>/dev/null | tail -n 20 | sed 's/^/ /' >&2
|
sed -n "$((before+1)),\$p" "$log" 2>/dev/null | tail -n 20 | sed 's/^/ /' >&2
|
||||||
return "$rc"
|
return "$rc"
|
||||||
|
|||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ui-block.sh - TTY-safe status "block" indicator (no special fonts)
|
||||||
|
#
|
||||||
|
# busy : blue block that "breathes" (background colour pulses)
|
||||||
|
# ok : solid green block
|
||||||
|
# fail : solid red block (changed from red X per YuKu's spec)
|
||||||
|
#
|
||||||
|
# Why this works on a bare TTY: the "block" is a BACKGROUND-COLOURED SPACE,
|
||||||
|
# not a U+2588 glyph. No fonts, no UTF-8, no locale dependency.
|
||||||
|
#
|
||||||
|
# Draws on /dev/tty, never stdout: ui_choose/ui_confirm capture stdout with $(),
|
||||||
|
# so a status line on stdout would corrupt every captured answer.
|
||||||
|
|
||||||
|
_UB_FD=""
|
||||||
|
_UB_PID=""
|
||||||
|
_UB_MSG=""
|
||||||
|
|
||||||
|
_ub_tty() {
|
||||||
|
if [[ -z "$_UB_FD" ]]; then
|
||||||
|
# probe first: a bare `exec {fd}>/dev/tty` failure still prints its error
|
||||||
|
{ : >/dev/tty; } 2>/dev/null || { _UB_FD=""; return 1; }
|
||||||
|
exec {_UB_FD}>/dev/tty 2>/dev/null || { _UB_FD=""; return 1; }
|
||||||
|
fi
|
||||||
|
[[ -n "$_UB_FD" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
_ub_depth() {
|
||||||
|
local n
|
||||||
|
n=$(tput colors 2>/dev/null || echo 0)
|
||||||
|
case "$n" in ''|*[!0-9]*) n=0 ;; esac
|
||||||
|
if [[ "$n" -ge 256 ]]; then echo 256
|
||||||
|
elif [[ "$n" -ge 8 ]]; then echo 16
|
||||||
|
else echo 0; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# paint one background-coloured block (a SPACE) + message
|
||||||
|
_ub_paint() {
|
||||||
|
printf '\r\033[%sm \033[0m %s' "$1" "$2" >&"$_UB_FD"
|
||||||
|
}
|
||||||
|
|
||||||
|
_ub_busy_loop() {
|
||||||
|
local depth pal=() i
|
||||||
|
depth=$(_ub_depth)
|
||||||
|
case "$depth" in
|
||||||
|
256) pal=(17 18 19 20 21 27 33 27 21 20 19 18) ;; # blue breathe
|
||||||
|
16) pal=(44 104 44 104) ;; # blue / bright blue
|
||||||
|
*) pal=("|" "/" "-" "\\") ;; # last-resort spinner
|
||||||
|
esac
|
||||||
|
while :; do
|
||||||
|
for i in "${pal[@]}"; do
|
||||||
|
if [[ "$depth" -eq 256 ]]; then
|
||||||
|
_ub_paint "48;5;$i" "$_UB_MSG" # 256-colour cube background
|
||||||
|
elif [[ "$depth" -eq 16 ]]; then
|
||||||
|
_ub_paint "$i" "$_UB_MSG" # plain ANSI background (44/104)
|
||||||
|
else
|
||||||
|
printf '\r[%s] %s' "$i" "$_UB_MSG" >&"$_UB_FD"
|
||||||
|
fi
|
||||||
|
sleep 0.08 || return 0
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_ub_stop() {
|
||||||
|
if [[ -n "$_UB_PID" ]]; then
|
||||||
|
kill "$_UB_PID" 2>/dev/null
|
||||||
|
wait "$_UB_PID" 2>/dev/null
|
||||||
|
_UB_PID=""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ui_block_start() {
|
||||||
|
_UB_MSG="${1:-Working}"
|
||||||
|
_ub_tty || return 0 # no tty: silent no-op
|
||||||
|
_ub_stop
|
||||||
|
_ub_busy_loop &
|
||||||
|
_UB_PID=$!
|
||||||
|
}
|
||||||
|
|
||||||
|
ui_block_ok() {
|
||||||
|
local msg="${1:-Done}" d
|
||||||
|
_ub_tty || return 0
|
||||||
|
_ub_stop
|
||||||
|
d=$(_ub_depth)
|
||||||
|
case "$d" in
|
||||||
|
256) _ub_paint "48;5;34" "$msg" ;;
|
||||||
|
16) _ub_paint "42" "$msg" ;;
|
||||||
|
*) printf '\r[OK] %s' "$msg" >&"$_UB_FD" ;;
|
||||||
|
esac
|
||||||
|
printf '\033[0m\n' >&"$_UB_FD"
|
||||||
|
}
|
||||||
|
|
||||||
|
ui_block_fail() {
|
||||||
|
local msg="${1:-Failed}" d
|
||||||
|
_ub_tty || return 0
|
||||||
|
_ub_stop
|
||||||
|
d=$(_ub_depth)
|
||||||
|
case "$d" in
|
||||||
|
256) printf '\r\033[48;5;196m \033[0m %s' "$msg" >&"$_UB_FD" ;;
|
||||||
|
16) printf '\r\033[41m \033[0m %s' "$msg" >&"$_UB_FD" ;;
|
||||||
|
*) printf '\r[!] %s' "$msg" >&"$_UB_FD" ;;
|
||||||
|
esac
|
||||||
|
printf '\n' >&"$_UB_FD"
|
||||||
|
}
|
||||||
|
|
||||||
|
ui_block_close() {
|
||||||
|
_ub_stop
|
||||||
|
if [[ -n "$_UB_FD" ]]; then
|
||||||
|
exec {_UB_FD}>&- 2>/dev/null
|
||||||
|
_UB_FD=""
|
||||||
|
fi
|
||||||
|
return 0 # an EXIT trap's status becomes the script's exit code — never fail here
|
||||||
|
}
|
||||||
|
|
||||||
|
# ALWAYS install a cleanup trap in the sourcing script:
|
||||||
|
# trap 'ui_block_close' EXIT INT TERM
|
||||||
@@ -12,6 +12,10 @@ source "$SCRIPT_DIR/lib/common.sh"
|
|||||||
source "$SCRIPT_DIR/lib/session.sh"
|
source "$SCRIPT_DIR/lib/session.sh"
|
||||||
# shellcheck source=lib/ui.sh
|
# shellcheck source=lib/ui.sh
|
||||||
source "$SCRIPT_DIR/lib/ui.sh"
|
source "$SCRIPT_DIR/lib/ui.sh"
|
||||||
|
# shellcheck source=lib/ui-block.sh
|
||||||
|
source "$SCRIPT_DIR/lib/ui-block.sh"
|
||||||
|
# never leave a block pulse loop running if the script is interrupted
|
||||||
|
trap 'ui_block_close' EXIT INT TERM
|
||||||
|
|
||||||
# Full dnf output from quiet transactions lands here (shown on failure).
|
# Full dnf output from quiet transactions lands here (shown on failure).
|
||||||
export SETUP_DNF_LOG="${SETUP_DNF_LOG:-/tmp/fedora-setup-dnf-$(id -u).log}"
|
export SETUP_DNF_LOG="${SETUP_DNF_LOG:-/tmp/fedora-setup-dnf-$(id -u).log}"
|
||||||
|
|||||||
Reference in New Issue
Block a user