Files
dotfiles/config/shell/.config/zsh/conf.d/10-done.zsh
T
2026-07-30 22:31:36 +02:00

113 lines
3.1 KiB
Bash

# Long command done notification for Niri (Wayland)
# Sends notify-send when a command takes longer than $uy_done_min_cmd_duration
# seconds and the terminal window is not focused.
(( $+commands[notify-send] )) && [[ -n "$NIRI_SOCKET" ]] || return
zmodload zsh/datetime
: ${uy_done_min_cmd_duration:=10}
: ${uy_done_exclude:='^(nvim|helix|hx|vim|vi|nano|less|more|man|ssh|top|htop|btop|sudoedit|yazi)$'}
# Returns the id in $REPLY, empty if there is none or niri did not answer.
if zmodload zsh/net/socket 2>/dev/null; then
uy_done_get_focused_window_id() {
setopt local_options extended_glob
local fd line
local -a match mbegin mend
REPLY=
zsocket "$NIRI_SOCKET" 2>/dev/null || return
fd=$REPLY
REPLY=
if print -u $fd -r -- '"FocusedWindow"' 2>/dev/null; then
read -t 1 -u $fd -r line 2>/dev/null
fi
exec {fd}>&-
# With no focused window the payload is null, so the id is simply absent.
[[ $line == (#b)*'"FocusedWindow":'*'"id":'(<->)* ]] && REPLY=$match[1]
}
elif (( $+commands[jq] )); then
uy_done_get_focused_window_id() {
REPLY=$(niri msg --json focused-window 2>/dev/null | jq -r '.id // empty')
}
else
return
fi
uy_done_cmd_name() {
local -a words
words=(${(z)1})
while (( $#words )); do
case $words[1] in
*=*) ;;
-*) ;;
sudo|doas|env|command|builtin|exec|nohup|nice|ionice|time|stdbuf|setsid) ;;
*) REPLY=${words[1]:t}; return ;;
esac
shift words
done
# Nothing but wrappers — fall back to the first word.
REPLY=${1%% *}
}
uy_done_preexec() {
local REPLY
uy_done_cmd="$1"
uy_done_start=$EPOCHSECONDS
uy_done_get_focused_window_id
uy_done_window_id=$REPLY
}
uy_done_precmd() {
local exit_status=$?
[[ -n "$uy_done_start" ]] || return
local elapsed=$(( EPOCHSECONDS - uy_done_start ))
unset uy_done_start
(( elapsed >= uy_done_min_cmd_duration )) || return
# skip excluded commands
local REPLY
uy_done_cmd_name "$uy_done_cmd"
[[ "$REPLY" =~ $uy_done_exclude ]] && return
# skip if window is still focused
local current_id
uy_done_get_focused_window_id
current_id=$REPLY
[[ -n "$uy_done_window_id" && "$uy_done_window_id" = "$current_id" ]] && return
# humanize duration
local title duration="" urgency=low
local minutes=$(( elapsed / 60 )) seconds=$(( elapsed % 60 ))
local hours=$(( elapsed / 3600 ))
(( hours > 0 )) && duration+="${hours}h "
(( minutes > 0 )) && duration+="$(( minutes % 60 ))m "
duration+="${seconds}s"
local wd="${PWD/#$HOME/~}"
if (( exit_status == 0 )); then
title="Done in $duration"
else
title="Failed ($exit_status) after $duration"
urgency=critical
fi
notify-send \
--hint=int:transient:1 \
--urgency="$urgency" \
--icon=utilities-terminal \
--app-name=zsh \
"$title" "$wd/ $uy_done_cmd" & disown
}
autoload -Uz add-zsh-hook
add-zsh-hook preexec uy_done_preexec
add-zsh-hook precmd uy_done_precmd