Dotfiles/desktopenvs/hyprlua/scripts/timer-pick

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