Hyprland scripts¶
Standalone Hyprland window-manager utilities. Each is a single bash script
wrapped with writeShellApplication and its dependencies.
fast¶
Toggle animations and corner rounding for a snappier feel. If animations are on, it disables them (and rounding); if off, it reloads Hyprland (restoring your config defaults).
state=$(hyprctl getoption animations:enabled -j | jq .int)
if [ "$state" -eq 0 ]; then
hyprctl reload
else
hyprctl keyword animations:enabled 0
hyprctl keyword decoration:rounding 0
fi
| Arguments | none |
| Runtime deps | jq, hyprctl |
| Package | fast |
focus¶
Directional focus that is aware of floating vs. tiling windows.
# Special: cycle next/prev (no direction)
focus cyclenext # cycle forward
focus cyclenext prev # cycle backward
# Directional (l/r/u/d)
focus l
focus r
focus u
focus d
Behavior¶
- Floating window active: uses
cyclenext(withprevfor left/up), then raises the newly focused window viaalterzorder top. - Tiling window active: uses
movefocus <dir>, then raises withalterzorder top.
if [[ "$1" =~ ^(l|r|u|d)$ ]]; then
floating=$(hyprctl activewindow -j | jq '.floating')
if [ "$floating" = true ]; then
arg=none
[[ "$1" =~ ^(l|u)$ ]] && arg='prev'
hyprctl dispatch cyclenext "$arg"
# ... raises if still floating
else
hyprctl dispatch movefocus "$1"
# ... raises
fi
fi
| Arguments | l | r | u | d | cyclenext [ prev ] |
| Runtime deps | jq, hyprctl |
| Package | focus |
move¶
Directional window move. For floating windows, moves by a pixel delta
(controlled by HYPR_MOVE_VAL); for tiling windows, uses movewindow.
_curr_win_floating=$(hyprctl activewindow -j | jq -r '.floating')
val=${HYPR_MOVE_VAL:-50}
getVal() {
case "$1" in
"r") echo "$val 0" ;;
"d") echo "0 $val" ;;
"l") echo "-$val 0" ;;
"u") echo "0 -$val" ;;
esac
}
if [[ $_curr_win_floating == "true" ]]; then
hyprctl dispatch moveactive $(getVal "$1")
else
hyprctl dispatch movewindow "$1"
fi
| Arguments | l | r | u | d |
| Env vars | HYPR_MOVE_VAL (default 50) - pixel step for floating moves |
| Runtime deps | jq, hyprctl |
| Package | move |
fullscreen¶
Cycles through Hyprland's three fullscreen modes by incrementing modulo 3:
| Step | Mode | Value |
|---|---|---|
| 0 | tiled (no fullscreen) | 0 |
| 1 | maximized | 1 |
| 2 | fullscreen | 2 |
fullscreenmode="$(hyprctl activewindow -j | jq '.fullscreenClient')"
result=$(((fullscreenmode + 1) % 3))
hyprctl dispatch fullscreen "$result"
| Arguments | none |
| Runtime deps | jq, hyprctl |
| Package | fullscreen |
zoom¶
Cursor zoom control - increment, decrement, or reset the
cursor:zoom_factor.
zoom() {
local zoom_curr
zoom_curr=$(current_zoom_size)
if [ "$1" == "0" ]; then
hyprctl keyword cursor:zoom_factor "1.0"
else
zoom_value="$(echo "$zoom_curr" + "$1" | bc)"
hyprctl keyword cursor:zoom_factor "$zoom_value"
fi
}
| Arguments | in | out | reset |
| Step | ±0.1 per call |
| Runtime deps | jq, bc, hyprctl |
| Package | zoom |
nix run github:semi710/utils#zoom -- in
nix run github:semi710/utils#zoom -- out
nix run github:semi710/utils#zoom -- reset
toggle-group¶
Toggle window grouping. If the active window is not grouped, it enables
grouping and notifies. If it is already grouped, it enters the Group
submap (for intra-group navigation).
isGroup="$(hyprctl activewindow -j | jq '.grouped | length')"
if [ "$isGroup" -eq "0" ]; then
hyprctl dispatch togglegroup
notify-send "Group enabled"
else
hyprctl dispatch submap "Group"
notify-send "Entered Submap"
fi
| Arguments | none |
| Runtime deps | jq, libnotify, hyprctl |
| Package | toggle-group |
Group submap
You need a Group submap defined in your Hyprland config for the second
branch to do anything useful (e.g. h/j/k/l to navigate within the group).
lid-down¶
Handle laptop lid close. If an external monitor is connected, it disables the
internal eDP-1 (clamshell mode). If only the internal display is present,
it locks the session.
mon_count=$(hyprctl monitors -j | jq '. | length')
if [ "$mon_count" -gt 1 ]; then
hyprctl keyword monitor "eDP-1, disable"
else
loginctl lock-session
fi
| Arguments | none |
| Runtime deps | jq, hyprctl, loginctl |
| Package | lid-down |
Typically bound to a lid switch event:
quick-term¶
A Yakuake-style drop-down terminal. If a quick-term window already exists:
- Focused → kill it (toggle off)
- Not focused → focus it
If it doesn't exist, spawn one using foot or kitty (selectable via
$TERMINAL), attached to a persistent tmux session named quick-term.
TERMINAL="${TERMINAL:-foot}"
if [ "$_pid" != "" ]; then
curr_focused="$(hyprctl activewindow -j | jq -r '.class')"
if [ "$curr_focused" = "quick-term" ]; then
kill -9 "$_pid"
else
hyprctl dispatch focuswindow pid:"$_pid"
fi
else
case "$TERMINAL" in
"foot")
foot -a "quick-term" sh -c "tmux new-session -A -s 'quick-term'" & ;;
"kitty")
kitty --class "quick-term" sh -c "tmux new-session -A -s 'quick-term'" & ;;
*)
echo "Unknown terminal: $TERMINAL"; exit 1 ;;
esac
fi
| Arguments | none |
| Env vars | TERMINAL (default foot, supports kitty) |
| Runtime deps | foot, jq, tmux, hyprctl |
| Package | quick-term |
Tmux session persistence
The terminal attaches to (or creates) a tmux session named quick-term
via tmux new-session -A, so the terminal state persists across
toggles. TMUX_TMPDIR is set to XDG_RUNTIME_DIR to avoid socket path
issues.
monitor¶
Auto-assign workspaces to monitors for a two-monitor setup. Workspaces 1–9 go to the primary monitor (id 1), workspace 10 goes to the secondary (id 0). Then focuses workspace 1 on the primary.
mon_count=$(hyprctl monitors -j | jq ". | length")
[ "$mon_count" -eq 1 ] && exit 0
workspaces_on_primary=(1 2 3 4 5 6 7 8 9)
workspaces_on_secondary=(10)
primary=$(hyprctl monitors -j | jq ".[] | select(.id == 1) | .name" | tr -d '"')
secondary=$(hyprctl monitors -j | jq ".[] | select(.id == 0) | .name" | tr -d '"')
for i in "${workspaces_on_primary[@]}"; do
hyprctl dispatch moveworkspacetomonitor "$i" "$primary"
done
for i in "${workspaces_on_secondary[@]}"; do
hyprctl dispatch moveworkspacetomonitor "$i" "$secondary"
done
hyprctl dispatch workspace 1
hyprctl dispatch focusmonitor "$primary"
| Arguments | none |
| Runtime deps | jq, hyprctl |
| Package | monitor |
Two-monitor only
The script has a TODO for multi-monitor support - currently hard-coded to exactly 2 monitors (id 0 = secondary, id 1 = primary). With 3+ monitors only two will be configured.