Files
Pathfinder 0630acf020 Console pulse: one-third speed (150ms frames), 256-ramp untouched
YuKu: 'slow the flicker a bit (half or one-third speed)'. The 16-colour
blue<->grey pulse now ticks at 150ms (600ms full cycle, calm); the
256-colour terminal breathe keeps its 50ms deep ramp. Per-depth tick
variable; same in the vendored helper.

Rate proven by pty trace: 8 frames in a 1.2s transaction = 150ms
exactly. (Frame-counting via grep -c undercounts — \r overwrites
collapse frames onto one line; count matches, not lines.)
2026-10-27 22:48:50 +01:00

119 lines
3.3 KiB
Bash

#!/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
# bare Linux VT: cap regardless of what tput reports
case "${TERM:-}" in linux*|console*) echo 16 ;; esac
}
# 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 tick=0.05
depth=$(_ub_depth)
[[ "$depth" -eq 16 ]] && tick=0.15 # console pulse: slow, not flicker
case "$depth" in
256) pal=(17 19 21 27 33 39 45 39 33 27 21 19) ;; # deep breathe: dark navy to bright blue
16) pal=(44 44 100 100) ;; # blue <-> dark grey, constant layout
*) 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" # blue <-> dark grey pulse
else
printf '\r[%s] %s' "$i" "$_UB_MSG" >&"$_UB_FD"
fi
sleep "$tick" || 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