diff --git a/setup/tui-install.sh b/setup/tui-install.sh index a10e598..9329eae 100755 --- a/setup/tui-install.sh +++ b/setup/tui-install.sh @@ -126,28 +126,53 @@ ui_menu() { ui_checklist() { # Multi-select list. Args: title prompt tag1 desc1 state1 tag2 desc2 state2 … - # state is "on"/"off" for the initial selection. The list is redrawn each - # round; the user toggles items by entering numbers and/or ranges - # (e.g. "1 3 5-8"), then presses Enter to confirm. Entering 'q' aborts - # (returns 1). The space-separated selected tags are printed to stdout. + # state is "on"/"off" for the initial selection. Long lists are paginated to + # the terminal height so they never scroll off-screen: the screen is cleared + # and redrawn each round, with n/p moving between pages. The user toggles + # items by number and/or range (e.g. "1 3 5-8") — numbering is global, so any + # item can be toggled from any page — then presses Enter to confirm. Entering + # 'q' aborts (returns 1). The space-separated selected tags go to stdout. local title="$1" prompt="$2"; shift 2 local -a tags=() descs=() state=() while [[ $# -gt 0 ]]; do tags+=("$1"); descs+=("$2"); state+=("$3"); shift 3; done local n=${#tags[@]} i mark line tok lo hi j + # Reserve rows for the header, prompt and footer; the remainder hold items. + # tput may be absent on a bare TTY, so fall back to a safe 24-row assumption. + local rows per pg=0 pages start end + rows=$(tput lines 2>/dev/null || echo 24) + [[ "$rows" =~ ^[0-9]+$ ]] || rows=24 + per=$(( rows - 9 )); (( per < 1 )) && per=1 + pages=$(( (n + per - 1) / per )); (( pages < 1 )) && pages=1 while true; do + (( pg < 0 )) && pg=0 + (( pg >= pages )) && pg=$(( pages - 1 )) + start=$(( pg * per )); end=$(( start + per )); (( end > n )) && end=$n { + # \033[2J\033[H clears the screen and homes the cursor so each page + # paints in place rather than scrolling — a stable, scrollable view. + printf '\033[2J\033[H' ui_header "$title" - printf '%b\n\n' "$prompt" - for i in "${!tags[@]}"; do + printf '%b' "$prompt" + (( pages > 1 )) && printf ' %s(page %d/%d)%s' "$C_DIM" $((pg + 1)) "$pages" "$C_RESET" + printf '\n\n' + for (( i = start; i < end; i++ )); do if [[ "${state[$i]}" == on ]]; then mark="${C_ACCENT}[x]${C_RESET}"; else mark='[ ]'; fi printf ' %s %s%3d%s %s\n' "$mark" "$C_ACCENT" $((i + 1)) "$C_RESET" "${descs[$i]}" done - printf '\n%s Toggle items by number/range (e.g. 1 3 5-8); Enter confirms, q aborts.%s\n' "$C_DIM" "$C_RESET" + if (( pages > 1 )); then + printf '\n%s number/range toggles (1 3 5-8) · n/p next/prev page · Enter confirms · q aborts%s\n' "$C_DIM" "$C_RESET" + else + printf '\n%s Toggle items by number/range (e.g. 1 3 5-8); Enter confirms, q aborts.%s\n' "$C_DIM" "$C_RESET" + fi printf '%s > %s' "$C_ACCENT" "$C_RESET" } >&2 read -r line