Files
Panama/config/dot/quickshell/scripts/panama-pick
T
Gabriel Brown 4e978bf3b7 Teach the launcher what an operating system knows
The OS-parity batch from the vicinae plan, tasks 1 through 7. The audit
came back better than the plan guessed: the calculator already links
libqalculate, the built-in file index answers in under 100ms across all
of home, quicklinks and snippets ship as built-in stores -- so zero new
packages, and `vicinae dmenu` replaces the planned compiled extension
outright. What was missing gets built: a power menu (lock, suspend, log
out through uwsm, restart, power off), reminders as transient systemd
timers with a pick-to-cancel list, a color picker over hyprpicker, and
dmenu pick-lists for window switching, force quit, kill process, SSH
hosts, and recent files -- all through one panama-pick helper. The
launcher commands contract exercises the reminder parsing and every
pick-list against stubs, including killing its own sacrificial sleep.
2026-08-21 19:32:11 -04:00

104 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# List-driven launcher commands, through `vicinae dmenu`.
#
# panama-pick window fuzzy-switch to an open window
# panama-pick quit-window pick a window; SIGTERM it, SIGKILL on repeat
# panama-pick process pick a process by CPU; SIGTERM it
# panama-pick ssh pick a Host from ~/.ssh/config; open a session
# panama-pick recent pick a recently used file; open it
#
# One helper rather than five scripts because every subcommand is the same
# sentence: build a list, let dmenu pick a line, act on the index. dmenu is
# vicinae's own list view, so these read as launcher commands without a
# compiled extension behind them.
set -uo pipefail
# Prints the picked index for the lines on stdin, or nothing on Escape.
menu() {
vicinae dmenu --navigation-title "$1" --section-title "$2" \
--format index --no-section 2>/dev/null
}
# Selected line (1-indexed by dmenu's 0-indexed output) from a saved list.
line_at() {
sed -n "$(( $1 + 1 ))p"
}
case "${1:-}" in
window|quit-window)
clients="$(hyprctl clients -j | jq -r '
[.[] | select(.mapped and .workspace.id > 0)] | sort_by(.workspace.id)[]
| [.address, .pid, "\(.title) (\(.class), workspace \(.workspace.id))"]
| @tsv')"
[[ -n "$clients" ]] || exit 0
title="Switch to window"; [[ "$1" == quit-window ]] && title="Force quit window"
idx="$(cut -f3 <<<"$clients" | menu "$title" 'Windows ({count})')" || exit 0
[[ -n "$idx" ]] || exit 0
picked="$(line_at "$idx" <<<"$clients")"
address="$(cut -f1 <<<"$picked")"
pid="$(cut -f2 <<<"$picked")"
if [[ "$1" == window ]]; then
exec hyprctl dispatch focuswindow "address:$address"
fi
# TERM first; the window still being mapped a moment later means the app
# ignored it, which is what force quit exists for.
kill "$pid" 2>/dev/null || true
sleep 1
if hyprctl clients -j | jq -e --arg a "$address" '.[] | select(.address == $a)' >/dev/null 2>&1; then
kill -9 "$pid" 2>/dev/null || true
fi
;;
process)
procs="$(ps -eo pid=,pcpu=,comm= --sort=-pcpu | awk -v self=$$ '$1 != self { printf "%s\t%5.1f%% %s\n", $1, $2, $3 }' | head -60)"
[[ -n "$procs" ]] || exit 0
idx="$(cut -f2 <<<"$procs" | menu "Kill process" 'By CPU ({count})')" || exit 0
[[ -n "$idx" ]] || exit 0
pid="$(line_at "$idx" <<<"$procs" | cut -f1)"
kill "$pid" 2>/dev/null \
&& notify-send "Sent SIGTERM to $pid" 2>/dev/null || true
;;
ssh)
config="$HOME/.ssh/config"
hosts="$( [[ -r "$config" ]] && awk '/^[Hh]ost / { for (i = 2; i <= NF; i++) if ($i !~ /[*?]/) print $i }' "$config" | sort -u )"
if [[ -z "$hosts" ]]; then
notify-send "SSH Hosts" "No hosts in ~/.ssh/config" 2>/dev/null || true
exit 0
fi
idx="$(menu "Open SSH session" 'Hosts ({count})' <<<"$hosts")" || exit 0
[[ -n "$idx" ]] || exit 0
host="$(line_at "$idx" <<<"$hosts")"
# kitty, matching hypr/keybinds.lua -- the terminal is written down in a
# few places and this one keeps the same value until one owner exists.
exec kitty --detach ssh "$host"
;;
recent)
xbel="$HOME/.local/share/recently-used.xbel"
entries="$( [[ -r "$xbel" ]] && python3 - "$xbel" <<'PY'
import sys, urllib.parse, xml.etree.ElementTree as ET
try:
root = ET.parse(sys.argv[1]).getroot()
except ET.ParseError:
sys.exit(0)
marks = [(b.get("modified") or "", b.get("href") or "") for b in root.iter("bookmark")]
for _, href in sorted(marks, reverse=True)[:40]:
if href.startswith("file://"):
path = urllib.parse.unquote(href[7:])
print(f"{path}")
PY
)"
if [[ -z "$entries" ]]; then
notify-send "Recent Files" "Nothing recorded yet" 2>/dev/null || true
exit 0
fi
idx="$(sed "s|^$HOME|~|" <<<"$entries" | menu "Open recent file" 'Recent ({count})')" || exit 0
[[ -n "$idx" ]] || exit 0
exec xdg-open "$(line_at "$idx" <<<"$entries")"
;;
*)
echo 'usage: panama-pick window|quit-window|process|ssh|recent' >&2
exit 2
;;
esac