202 lines
5.9 KiB
Bash
Executable File
202 lines
5.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# timer-pick — TUI time picker
|
|
# Install: ~/.config/scripts/timer-pick (companion: timer-run in same dir)
|
|
|
|
SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
set -uo pipefail
|
|
|
|
ESC=$'\e'; CSI="${ESC}["
|
|
cup() { printf "${CSI}%d;%dH" "$1" "$2"; }
|
|
cls() { printf "${CSI}2J${CSI}H"; }
|
|
hide() { printf "${CSI}?25l"; }
|
|
show() { printf "${CSI}?25h"; }
|
|
|
|
b="${CSI}1m" ; d="${CSI}2m" ; r="${CSI}0m"
|
|
cy="${CSI}96m"; gn="${CSI}92m"
|
|
yl="${CSI}93m"; wh="${CSI}97m"; gy="${CSI}90m"
|
|
rv="${CSI}7m"
|
|
|
|
FIELDS=(HH MM SS)
|
|
MAX=(99 59 59)
|
|
VALUES=(0 0 0)
|
|
ACTIVE=1
|
|
LABEL=""
|
|
|
|
TW=34; IW=32; CW=10
|
|
BR=2; BC=2
|
|
|
|
hl() { printf '─%.0s' $(seq 1 "$1"); }
|
|
|
|
redraw() {
|
|
local row=$BR col=$BC
|
|
|
|
cup $row $col; printf "${d}╭$(hl $IW)╮${r}"
|
|
|
|
(( row++ )); cup $row $col
|
|
printf "${d}│${r}${b}${cy} ⏱ Set Timer%-*s${r}${d}│${r}" $(( IW - 14 )) ""
|
|
|
|
(( row++ )); cup $row $col
|
|
printf "${d}├$(hl $CW)┬$(hl $CW)┬$(hl $CW)┤${r}"
|
|
|
|
# labels
|
|
(( row++ )); cup $row $col; printf "${d}│${r}"
|
|
for i in 0 1 2; do
|
|
local lpad=$(( (CW - 2) / 2 )) rpad=$(( CW - 2 - (CW-2)/2 ))
|
|
(( i == ACTIVE )) \
|
|
&& printf "%*s${yl}${b}${FIELDS[$i]}${r}%*s" $lpad "" $rpad "" \
|
|
|| printf "%*s${gy}${FIELDS[$i]}${r}%*s" $lpad "" $rpad ""
|
|
(( i < 2 )) && printf "${d}│${r}"
|
|
done; printf "${d}│${r}"
|
|
|
|
# up arrows
|
|
(( row++ )); cup $row $col; printf "${d}│${r}"
|
|
for i in 0 1 2; do
|
|
local lpad=$(( (CW-1) / 2 )) rpad=$(( CW - 1 - (CW-1)/2 ))
|
|
(( i == ACTIVE )) \
|
|
&& printf "%*s${yl}${b}▲${r}%*s" $lpad "" $rpad "" \
|
|
|| printf "%*s${gy}▲${r}%*s" $lpad "" $rpad ""
|
|
(( i < 2 )) && printf "${d}│${r}"
|
|
done; printf "${d}│${r}"
|
|
|
|
# values
|
|
(( row++ )); cup $row $col; printf "${d}│${r}"
|
|
for i in 0 1 2; do
|
|
local vstr; printf -v vstr '%02d' "${VALUES[$i]}"
|
|
local lpad=$(( (CW-4) / 2 )) rpad=$(( CW - 4 - (CW-4)/2 ))
|
|
(( i == ACTIVE )) \
|
|
&& printf "%*s${rv}${wh}${b} %s ${r}%*s" $lpad "" "$vstr" $rpad "" \
|
|
|| printf "%*s${d} %s ${r}%*s" $lpad "" "$vstr" $rpad ""
|
|
(( i < 2 )) && printf "${d}│${r}"
|
|
done; printf "${d}│${r}"
|
|
|
|
# down arrows
|
|
(( row++ )); cup $row $col; printf "${d}│${r}"
|
|
for i in 0 1 2; do
|
|
local lpad=$(( (CW-1) / 2 )) rpad=$(( CW - 1 - (CW-1)/2 ))
|
|
(( i == ACTIVE )) \
|
|
&& printf "%*s${yl}${b}▼${r}%*s" $lpad "" $rpad "" \
|
|
|| printf "%*s${gy}▼${r}%*s" $lpad "" $rpad ""
|
|
(( i < 2 )) && printf "${d}│${r}"
|
|
done; printf "${d}│${r}"
|
|
|
|
(( row++ )); cup $row $col; printf "${d}├$(hl $IW)┤${r}"
|
|
|
|
(( row++ )); cup $row $col
|
|
local ld="${LABEL:-(optional)}"
|
|
printf "${d}│${r} ${gy}label:${r} ${cy}%-*s${r}${d}│${r}" $(( IW - 8 )) "$ld"
|
|
|
|
(( row++ )); cup $row $col; printf "${d}├$(hl $IW)┤${r}"
|
|
|
|
(( row++ )); cup $row $col
|
|
printf "${d}│${r} ${gy}h/l${r} field ${gy}j/k${r} value ${gy}[N]j/k${r} ${d}│${r}"
|
|
(( row++ )); cup $row $col
|
|
printf "${d}│${r} ${gy}i${r} label ${gy}0/G${r} min/max ${gn}↵${r} start ${d}│${r}"
|
|
|
|
(( row++ )); cup $row $col; printf "${d}╰$(hl $IW)╯${r}"
|
|
|
|
cup $(( row + 1 )) 1
|
|
}
|
|
|
|
read_label() {
|
|
show
|
|
local lrow=$(( BR + 9 )) lcol=$(( BC + 9 ))
|
|
cup $lrow $lcol; printf "${CSI}K"
|
|
local buf="" ch
|
|
while IFS= read -rsn1 ch; do
|
|
case "$ch" in
|
|
$'\n'|$'\r'|$'\e') break ;;
|
|
$'\x7f'|$'\b')
|
|
[[ -n $buf ]] && buf="${buf%?}"
|
|
cup $lrow $lcol; printf "${CSI}K%s" "$buf" ;;
|
|
*) [[ ${#buf} -lt $(( IW - 10 )) ]] && buf+="$ch" && printf "%s" "$ch" ;;
|
|
esac
|
|
done
|
|
LABEL="$buf"; hide
|
|
}
|
|
|
|
cleanup() {
|
|
# only reset tty if we actually changed it
|
|
[[ "${TTY_SAVED:-0}" == "1" ]] && stty sane 2>/dev/null
|
|
show
|
|
cls
|
|
tput cup 0 0 2>/dev/null
|
|
}
|
|
|
|
main() {
|
|
# Wait for kitty to fully attach the pty — critical when launched via Hyprland
|
|
# keybind where the window and pty aren't synchronised at script start.
|
|
local tries=0
|
|
until [[ -t 0 && -t 1 ]]; do
|
|
sleep 0.05
|
|
(( tries++ ))
|
|
(( tries > 40 )) && exit 1 # give up after 2s
|
|
done
|
|
|
|
hide; cls
|
|
stty -echo -icanon min 1 time 0
|
|
TTY_SAVED=1
|
|
trap cleanup EXIT INT TERM
|
|
|
|
redraw
|
|
|
|
local ch seq count_buf=""
|
|
|
|
while true; do
|
|
IFS= read -rsn1 ch || continue
|
|
if [[ $ch == $'\e' ]]; then
|
|
IFS= read -rsn2 -t 0.05 seq || seq=""
|
|
ch="${ch}${seq}"
|
|
fi
|
|
|
|
if [[ $ch =~ ^[1-9]$ ]]; then count_buf+="$ch"; redraw; continue; fi
|
|
local N=1
|
|
[[ -n $count_buf && $count_buf =~ ^[0-9]+$ ]] && N=$count_buf
|
|
|
|
case "$ch" in
|
|
$'\t'|$'\e[C'|l) ACTIVE=$(( (ACTIVE + 1) % 3 )); count_buf="" ;;
|
|
$'\e[Z'|$'\e[D'|h) ACTIVE=$(( (ACTIVE + 2) % 3 )); count_buf="" ;;
|
|
'^') ACTIVE=0; count_buf="" ;;
|
|
'$') ACTIVE=2; count_buf="" ;;
|
|
|
|
$'\e[A'|k)
|
|
local mx=${MAX[$ACTIVE]} v=${VALUES[$ACTIVE]}
|
|
local nv=$(( v + N )); (( nv > mx )) && nv=$mx
|
|
VALUES[$ACTIVE]=$nv; count_buf="" ;;
|
|
|
|
$'\e[B'|j)
|
|
local v=${VALUES[$ACTIVE]}
|
|
local nv=$(( v - N )); (( nv < 0 )) && nv=0
|
|
VALUES[$ACTIVE]=$nv; count_buf="" ;;
|
|
|
|
0)
|
|
if [[ -n $count_buf ]]; then count_buf+="0"; redraw; continue; fi
|
|
VALUES[$ACTIVE]=0; count_buf="" ;;
|
|
G) VALUES[$ACTIVE]=${MAX[$ACTIVE]}; count_buf="" ;;
|
|
|
|
i) count_buf=""; read_label ;;
|
|
|
|
''|$'\n')
|
|
count_buf=""
|
|
local total=$(( VALUES[0]*3600 + VALUES[1]*60 + VALUES[2] ))
|
|
if (( total == 0 )); then redraw; continue; fi
|
|
TTY_SAVED=0
|
|
stty sane 2>/dev/null; show; cls; tput cup 0 0 2>/dev/null
|
|
# SIG_IGN for SIGHUP is inherited through exec, eliminating the race
|
|
# between fork() and setsid() creating its own session. Without this,
|
|
# kitty closing the pty on exit sends SIGHUP to the foreground process
|
|
# group (which includes the child in non-interactive bash) before setsid
|
|
# has a chance to move it to a new session.
|
|
( trap '' SIGHUP; exec setsid bash "$SCRIPTS_DIR/timer-run" "$total" "$LABEL" </dev/null >/dev/null 2>&1 ) &
|
|
exit 0 ;;
|
|
|
|
q|Q) exit 0 ;;
|
|
*) count_buf="" ;;
|
|
esac
|
|
|
|
redraw
|
|
done
|
|
}
|
|
|
|
main
|