summaryrefslogtreecommitdiff
path: root/polybar/scripts
diff options
context:
space:
mode:
authorDavid Moc <personal@cdatgoose.org>2026-05-24 11:48:07 +0200
committerDavid Moc <personal@cdatgoose.org>2026-05-24 11:48:07 +0200
commit5fb19f10389b70ee2389755106092a9ef6c64598 (patch)
tree969eb0466de581cb14aa09f751a95138620fc720 /polybar/scripts
Go
Diffstat (limited to 'polybar/scripts')
-rwxr-xr-xpolybar/scripts/awake-status.sh17
-rwxr-xr-xpolybar/scripts/awake-toggle.sh122
-rwxr-xr-xpolybar/scripts/bar-mode-status.sh18
-rwxr-xr-xpolybar/scripts/bar-mode-toggle.sh48
-rwxr-xr-xpolybar/scripts/clipboard-menu.sh45
-rwxr-xr-xpolybar/scripts/display-brightness.sh39
-rwxr-xr-xpolybar/scripts/display-status.sh17
-rwxr-xr-xpolybar/scripts/display-toggle.sh40
-rwxr-xr-xpolybar/scripts/dunst-status.sh26
-rwxr-xr-xpolybar/scripts/kb-status.sh26
-rwxr-xr-xpolybar/scripts/kb-switch.sh36
-rwxr-xr-xpolybar/scripts/kb-switch.sh~17
-rwxr-xr-xpolybar/scripts/mic-status.sh21
-rwxr-xr-xpolybar/scripts/mic-toggle.sh13
-rwxr-xr-xpolybar/scripts/music-status.sh38
-rwxr-xr-xpolybar/scripts/net-speed.sh64
-rwxr-xr-xpolybar/scripts/polkit-agent.sh29
-rwxr-xr-xpolybar/scripts/power-menu.sh33
-rwxr-xr-xpolybar/scripts/scratchpad-status.sh18
-rwxr-xr-xpolybar/scripts/screenshot-menu.sh73
-rwxr-xr-xpolybar/scripts/temp-status.sh36
-rwxr-xr-xpolybar/scripts/updates-action.sh16
-rwxr-xr-xpolybar/scripts/updates-status.sh17
-rwxr-xr-xpolybar/scripts/wg-pc-status.sh32
-rwxr-xr-xpolybar/scripts/wg-pc-toggle.sh82
25 files changed, 923 insertions, 0 deletions
diff --git a/polybar/scripts/awake-status.sh b/polybar/scripts/awake-status.sh
new file mode 100755
index 0000000..98948f2
--- /dev/null
+++ b/polybar/scripts/awake-status.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+state_dir="${XDG_RUNTIME_DIR:-/tmp}"
+if [ ! -w "$state_dir" ]; then
+ state_dir="${XDG_CACHE_HOME:-$HOME/.cache}"
+fi
+if [ ! -w "$state_dir" ]; then
+ state_dir="/tmp"
+fi
+state_file="$state_dir/polybar-awake.state"
+
+if [ -f "$state_file" ]; then
+ echo "%{F#98971a}on%{F-}"
+else
+ echo "%{F#928374}off%{F-}"
+fi
diff --git a/polybar/scripts/awake-toggle.sh b/polybar/scripts/awake-toggle.sh
new file mode 100755
index 0000000..608bbc2
--- /dev/null
+++ b/polybar/scripts/awake-toggle.sh
@@ -0,0 +1,122 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+state_dir="${XDG_RUNTIME_DIR:-/tmp}"
+if [ ! -w "$state_dir" ]; then
+ state_dir="${XDG_CACHE_HOME:-$HOME/.cache}"
+ mkdir -p "$state_dir"
+fi
+if [ ! -w "$state_dir" ]; then
+ state_dir="/tmp"
+fi
+state_file="$state_dir/polybar-awake.state"
+action="${1:-toggle}"
+
+export DISPLAY="${DISPLAY:-:0}"
+if [ -z "${XAUTHORITY:-}" ] && [ -r "$HOME/.Xauthority" ]; then
+ export XAUTHORITY="$HOME/.Xauthority"
+fi
+
+notify() {
+ if command -v dunstify >/dev/null 2>&1; then
+ dunstify -a polybar -u low "Force awake" "$1"
+ fi
+}
+
+save_current_state() {
+ local query
+ query="$(xset q)"
+
+ {
+ printf 'blanking=%s\n' "$(printf '%s\n' "$query" | awk '/prefer blanking:/ { print $3; exit }')"
+ printf 'exposures=%s\n' "$(printf '%s\n' "$query" | awk '/allow exposures:/ { print $6; exit }')"
+ printf 'timeout=%s\n' "$(printf '%s\n' "$query" | awk '/timeout:/ { print $2; exit }')"
+ printf 'cycle=%s\n' "$(printf '%s\n' "$query" | awk '/timeout:/ { print $4; exit }')"
+ printf 'standby=%s\n' "$(printf '%s\n' "$query" | awk '/Standby:/ { print $2; exit }')"
+ printf 'suspend=%s\n' "$(printf '%s\n' "$query" | awk '/Standby:/ { print $4; exit }')"
+ printf 'off=%s\n' "$(printf '%s\n' "$query" | awk '/Standby:/ { print $6; exit }')"
+ printf 'dpms=%s\n' "$(printf '%s\n' "$query" | awk '/DPMS is/ { print $3; exit }')"
+ } >"$state_file"
+}
+
+enable_awake() {
+ if [ ! -f "$state_file" ]; then
+ save_current_state
+ fi
+
+ xset s noblank
+ xset s off
+ xset -dpms
+ notify "enabled"
+}
+
+restore_value() {
+ local name="$1"
+ local fallback="$2"
+ local value
+
+ value="$(awk -F= -v key="$name" '$1 == key { print $2; exit }' "$state_file" 2>/dev/null || true)"
+ printf '%s' "${value:-$fallback}"
+}
+
+disable_awake() {
+ if [ -f "$state_file" ]; then
+ local blanking exposures timeout cycle standby suspend off dpms
+ blanking="$(restore_value blanking yes)"
+ exposures="$(restore_value exposures yes)"
+ timeout="$(restore_value timeout 600)"
+ cycle="$(restore_value cycle 600)"
+ standby="$(restore_value standby 600)"
+ suspend="$(restore_value suspend 600)"
+ off="$(restore_value off 600)"
+ dpms="$(restore_value dpms Enabled)"
+
+ if [ "$blanking" = "yes" ]; then
+ xset s blank
+ else
+ xset s noblank
+ fi
+
+ if [ "$exposures" = "yes" ]; then
+ xset s expose
+ else
+ xset s noexpose
+ fi
+
+ xset s "$timeout" "$cycle"
+ xset dpms "$standby" "$suspend" "$off"
+
+ if [ "$dpms" = "Enabled" ]; then
+ xset +dpms
+ else
+ xset -dpms
+ fi
+
+ rm -f "$state_file"
+ else
+ xset s on
+ xset +dpms
+ fi
+
+ notify "disabled"
+}
+
+case "$action" in
+ on)
+ enable_awake
+ ;;
+ off)
+ disable_awake
+ ;;
+ toggle)
+ if [ -f "$state_file" ]; then
+ disable_awake
+ else
+ enable_awake
+ fi
+ ;;
+ *)
+ notify "unknown action: $action"
+ exit 2
+ ;;
+esac
diff --git a/polybar/scripts/bar-mode-status.sh b/polybar/scripts/bar-mode-status.sh
new file mode 100755
index 0000000..7e6d787
--- /dev/null
+++ b/polybar/scripts/bar-mode-status.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+state_dir="${XDG_RUNTIME_DIR:-/tmp}"
+if [ ! -w "$state_dir" ]; then
+ state_dir="${XDG_CACHE_HOME:-$HOME/.cache}"
+fi
+if [ ! -w "$state_dir" ]; then
+ state_dir="/tmp"
+fi
+
+mode_file="$state_dir/polybar-mode.state"
+
+if [ -r "$mode_file" ] && [ "$(cat "$mode_file")" = "full" ]; then
+ echo "%{F#d79921}less%{F-}"
+else
+ echo "%{F#98971a}more%{F-}"
+fi
diff --git a/polybar/scripts/bar-mode-toggle.sh b/polybar/scripts/bar-mode-toggle.sh
new file mode 100755
index 0000000..d943df1
--- /dev/null
+++ b/polybar/scripts/bar-mode-toggle.sh
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+requested="${1:-toggle}"
+state_dir="${XDG_RUNTIME_DIR:-/tmp}"
+
+if [ ! -w "$state_dir" ]; then
+ state_dir="${XDG_CACHE_HOME:-$HOME/.cache}"
+ mkdir -p "$state_dir" 2>/dev/null || true
+fi
+
+if [ ! -w "$state_dir" ]; then
+ state_dir="/tmp"
+fi
+
+mode_file="$state_dir/polybar-mode.state"
+
+current="compact"
+if [ -r "$mode_file" ]; then
+ current="$(cat "$mode_file")"
+fi
+
+case "$requested" in
+ full|expand|expanded)
+ next="full"
+ ;;
+ compact|minimize|minimized)
+ next="compact"
+ ;;
+ toggle)
+ if [ "$current" = "full" ]; then
+ next="compact"
+ else
+ next="full"
+ fi
+ ;;
+ *)
+ exit 2
+ ;;
+esac
+
+printf '%s\n' "$next" >"$mode_file"
+
+if command -v dunstify >/dev/null 2>&1; then
+ dunstify -a polybar -u low "Polybar" "$next mode"
+fi
+
+/home/aag/.config/polybar/launch.sh >/tmp/polybar-mode-toggle.log 2>&1 &
diff --git a/polybar/scripts/clipboard-menu.sh b/polybar/scripts/clipboard-menu.sh
new file mode 100755
index 0000000..10fe648
--- /dev/null
+++ b/polybar/scripts/clipboard-menu.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+state_dir="${XDG_CACHE_HOME:-$HOME/.cache}"
+mkdir -p "$state_dir" 2>/dev/null || true
+if [ ! -w "$state_dir" ]; then
+ state_dir="/tmp"
+fi
+history_file="$state_dir/polybar-clipboard-history"
+
+export DISPLAY="${DISPLAY:-:0}"
+if [ -z "${XAUTHORITY:-}" ] && [ -r "$HOME/.Xauthority" ]; then
+ export XAUTHORITY="$HOME/.Xauthority"
+fi
+
+current=""
+if command -v xclip >/dev/null 2>&1; then
+ current="$(xclip -selection clipboard -o 2>/dev/null || true)"
+elif command -v xsel >/dev/null 2>&1; then
+ current="$(xsel --clipboard --output 2>/dev/null || true)"
+fi
+
+if [ -n "$current" ]; then
+ one_line="$(printf '%s' "$current" | tr '\n' ' ' | cut -c 1-180)"
+ if ! grep -Fxq "$one_line" "$history_file" 2>/dev/null; then
+ tmp="${history_file}.tmp"
+ { printf '%s\n' "$one_line"; cat "$history_file" 2>/dev/null; } | awk 'NF && !seen[$0]++' | head -n 50 >"$tmp"
+ mv "$tmp" "$history_file"
+ fi
+fi
+
+choice="$(
+ dmenu -i -p clipboard \
+ -fn "FiraCode Nerd Font-14" \
+ -nb "#282828" -nf "#ebdbb2" \
+ -sb "#d79921" -sf "#282828" <"$history_file"
+)"
+
+[ -n "$choice" ] || exit 0
+
+if command -v xclip >/dev/null 2>&1; then
+ printf '%s' "$choice" | xclip -selection clipboard
+elif command -v xsel >/dev/null 2>&1; then
+ printf '%s' "$choice" | xsel --clipboard --input
+fi
diff --git a/polybar/scripts/display-brightness.sh b/polybar/scripts/display-brightness.sh
new file mode 100755
index 0000000..ea3d04c
--- /dev/null
+++ b/polybar/scripts/display-brightness.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+direction="${1:-up}"
+state_dir="${XDG_RUNTIME_DIR:-/tmp}"
+if [ ! -w "$state_dir" ]; then
+ state_dir="${XDG_CACHE_HOME:-$HOME/.cache}"
+ mkdir -p "$state_dir"
+fi
+if [ ! -w "$state_dir" ]; then
+ state_dir="/tmp"
+fi
+state_file="$state_dir/polybar-display-brightness.state"
+
+export DISPLAY="${DISPLAY:-:0}"
+if [ -z "${XAUTHORITY:-}" ] && [ -r "$HOME/.Xauthority" ]; then
+ export XAUTHORITY="$HOME/.Xauthority"
+fi
+
+output="$(
+ xrandr --query 2>/dev/null |
+ awk '/ connected primary/ { print $1; exit } / connected/ && !out { out = $1 } END { if (out) print out }'
+)"
+
+[ -n "$output" ] || exit 0
+
+brightness="$(cat "$state_file" 2>/dev/null || printf '1.0')"
+
+case "$direction" in
+ up)
+ brightness="$(awk -v v="$brightness" 'BEGIN { v += 0.05; if (v > 1.00) v = 1.00; printf "%.2f", v }')"
+ ;;
+ down)
+ brightness="$(awk -v v="$brightness" 'BEGIN { v -= 0.05; if (v < 0.30) v = 0.30; printf "%.2f", v }')"
+ ;;
+esac
+
+printf '%s\n' "$brightness" >"$state_file"
+xrandr --output "$output" --brightness "$brightness"
diff --git a/polybar/scripts/display-status.sh b/polybar/scripts/display-status.sh
new file mode 100755
index 0000000..620e3da
--- /dev/null
+++ b/polybar/scripts/display-status.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+state_dir="${XDG_RUNTIME_DIR:-/tmp}"
+if [ ! -w "$state_dir" ]; then
+ state_dir="${XDG_CACHE_HOME:-$HOME/.cache}"
+fi
+if [ ! -w "$state_dir" ]; then
+ state_dir="/tmp"
+fi
+state_file="$state_dir/polybar-display-night.state"
+
+if [ -f "$state_file" ]; then
+ echo "%{F#d65d0e}night%{F-}"
+else
+ echo "day"
+fi
diff --git a/polybar/scripts/display-toggle.sh b/polybar/scripts/display-toggle.sh
new file mode 100755
index 0000000..df810e8
--- /dev/null
+++ b/polybar/scripts/display-toggle.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+state_dir="${XDG_RUNTIME_DIR:-/tmp}"
+if [ ! -w "$state_dir" ]; then
+ state_dir="${XDG_CACHE_HOME:-$HOME/.cache}"
+ mkdir -p "$state_dir"
+fi
+if [ ! -w "$state_dir" ]; then
+ state_dir="/tmp"
+fi
+state_file="$state_dir/polybar-display-night.state"
+
+export DISPLAY="${DISPLAY:-:0}"
+if [ -z "${XAUTHORITY:-}" ] && [ -r "$HOME/.Xauthority" ]; then
+ export XAUTHORITY="$HOME/.Xauthority"
+fi
+
+output="$(
+ xrandr --query 2>/dev/null |
+ awk '/ connected primary/ { print $1; exit } / connected/ && !out { out = $1 } END { if (out) print out }'
+)"
+
+[ -n "$output" ] || exit 0
+
+notify() {
+ if command -v dunstify >/dev/null 2>&1; then
+ dunstify -a polybar -u low "Display" "$1"
+ fi
+}
+
+if [ -f "$state_file" ]; then
+ xrandr --output "$output" --gamma 1:1:1
+ rm -f "$state_file"
+ notify "day mode"
+else
+ xrandr --output "$output" --gamma 1:0.88:0.72
+ : >"$state_file"
+ notify "night mode"
+fi
diff --git a/polybar/scripts/dunst-status.sh b/polybar/scripts/dunst-status.sh
new file mode 100755
index 0000000..da23ddd
--- /dev/null
+++ b/polybar/scripts/dunst-status.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+if ! command -v dunstctl >/dev/null 2>&1; then
+ echo "n/a"
+ exit 0
+fi
+
+paused="$(dunstctl is-paused 2>/dev/null || echo unavailable)"
+waiting="$(dunstctl count waiting 2>/dev/null || echo 0)"
+
+case "$paused" in
+ true)
+ if [ "$waiting" -gt 0 ] 2>/dev/null; then
+ echo "dnd $waiting"
+ else
+ echo "dnd"
+ fi
+ ;;
+ false)
+ echo "on"
+ ;;
+ *)
+ echo "off"
+ ;;
+esac
diff --git a/polybar/scripts/kb-status.sh b/polybar/scripts/kb-status.sh
new file mode 100755
index 0000000..551e587
--- /dev/null
+++ b/polybar/scripts/kb-status.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+export DISPLAY="${DISPLAY:-:0}"
+if [ -z "${XAUTHORITY:-}" ] && [ -r "$HOME/.Xauthority" ]; then
+ export XAUTHORITY="$HOME/.Xauthority"
+fi
+
+query="$(setxkbmap -query 2>/dev/null || true)"
+layout="$(printf '%s\n' "$query" | awk '/^layout:/ { print $2; exit }')"
+variant="$(printf '%s\n' "$query" | awk '/^variant:/ { print $2; exit }')"
+
+case "${layout:-unknown}:${variant:-}" in
+ us:dvorak)
+ echo "us dv"
+ ;;
+ cz:*|cz:)
+ echo "cz"
+ ;;
+ *:)
+ echo "${layout:-unknown}"
+ ;;
+ *)
+ echo "${layout:-unknown} ${variant}"
+ ;;
+esac
diff --git a/polybar/scripts/kb-switch.sh b/polybar/scripts/kb-switch.sh
new file mode 100755
index 0000000..af231f0
--- /dev/null
+++ b/polybar/scripts/kb-switch.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+DMENU="${DMENU:-/usr/bin/dmenu}"
+SETXKBMAP="${SETXKBMAP:-/usr/bin/setxkbmap}"
+LOG_FILE="${XDG_RUNTIME_DIR:-/tmp}/polybar-kb-switch.log"
+
+exec 2>>"$LOG_FILE"
+
+export DISPLAY="${DISPLAY:-:0}"
+if [ -z "${XAUTHORITY:-}" ] && [ -r "$HOME/.Xauthority" ]; then
+ export XAUTHORITY="$HOME/.Xauthority"
+fi
+
+choice="$(
+ printf '%s\n' "us" "us dvorak" "cz" |
+ "$DMENU" -i -p "Keyboard" \
+ -b \
+ -fn "FiraCode Nerd Font-12" \
+ -nb "#282828" \
+ -nf "#ebdbb2" \
+ -sb "#d79921" \
+ -sf "#282828"
+)" || exit 0
+
+case "$choice" in
+ "us")
+ "$SETXKBMAP" us
+ ;;
+ "us dvorak")
+ "$SETXKBMAP" us -variant dvorak
+ ;;
+ "cz")
+ "$SETXKBMAP" cz
+ ;;
+esac
diff --git a/polybar/scripts/kb-switch.sh~ b/polybar/scripts/kb-switch.sh~
new file mode 100755
index 0000000..69ff036
--- /dev/null
+++ b/polybar/scripts/kb-switch.sh~
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+options="us\nus dvorak\ncs"
+
+choice=$(echo -e "$options" | dmenu -i -p "Keyboard layout")
+
+case "$choice" in
+ "us")
+ setxkbmap us
+ ;;
+ "us dvorak")
+ setxkbmap us -variant dvorak
+ ;;
+ "cs")
+ setxkbmap cz
+ ;;
+esac
diff --git a/polybar/scripts/mic-status.sh b/polybar/scripts/mic-status.sh
new file mode 100755
index 0000000..21c44ce
--- /dev/null
+++ b/polybar/scripts/mic-status.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+if ! command -v pactl >/dev/null 2>&1; then
+ echo "n/a"
+ exit 0
+fi
+
+mute="$(pactl get-source-mute @DEFAULT_SOURCE@ 2>/dev/null | awk '{ print $2 }' || true)"
+
+case "$mute" in
+ yes)
+ echo "%{F#cc241d}muted%{F-}"
+ ;;
+ no)
+ echo "%{F#98971a}on%{F-}"
+ ;;
+ *)
+ echo "n/a"
+ ;;
+esac
diff --git a/polybar/scripts/mic-toggle.sh b/polybar/scripts/mic-toggle.sh
new file mode 100755
index 0000000..de93cf6
--- /dev/null
+++ b/polybar/scripts/mic-toggle.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+if ! command -v pactl >/dev/null 2>&1; then
+ exit 0
+fi
+
+pactl set-source-mute @DEFAULT_SOURCE@ toggle
+
+if command -v dunstify >/dev/null 2>&1; then
+ status="$(pactl get-source-mute @DEFAULT_SOURCE@ 2>/dev/null | awk '{ print $2 }' || true)"
+ dunstify -a polybar -u low "Microphone" "${status:-toggled}"
+fi
diff --git a/polybar/scripts/music-status.sh b/polybar/scripts/music-status.sh
new file mode 100755
index 0000000..229e058
--- /dev/null
+++ b/polybar/scripts/music-status.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+if ! command -v playerctl >/dev/null 2>&1; then
+ echo "n/a"
+ exit 0
+fi
+
+status="$(playerctl status 2>/dev/null || true)"
+if [ -z "$status" ]; then
+ echo "off"
+ exit 0
+fi
+
+artist="$(playerctl metadata artist 2>/dev/null || true)"
+title="$(playerctl metadata title 2>/dev/null || true)"
+
+if [ -n "$artist" ] && [ -n "$title" ]; then
+ text="$artist - $title"
+elif [ -n "$title" ]; then
+ text="$title"
+else
+ text="$status"
+fi
+
+case "$status" in
+ Playing)
+ prefix="%{F#98971a}>%{F-}"
+ ;;
+ Paused)
+ prefix="%{F#d79921}=%{F-}"
+ ;;
+ *)
+ prefix="-"
+ ;;
+esac
+
+printf '%s %s\n' "$prefix" "$(printf '%s' "$text" | cut -c 1-32)"
diff --git a/polybar/scripts/net-speed.sh b/polybar/scripts/net-speed.sh
new file mode 100755
index 0000000..ead10fd
--- /dev/null
+++ b/polybar/scripts/net-speed.sh
@@ -0,0 +1,64 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+iface="${POLYBAR_NETWORK:-eth0}"
+state_dir="${XDG_RUNTIME_DIR:-/tmp}"
+if [ ! -w "$state_dir" ]; then
+ state_dir="${XDG_CACHE_HOME:-$HOME/.cache}"
+ mkdir -p "$state_dir"
+fi
+if [ ! -w "$state_dir" ]; then
+ state_dir="/tmp"
+fi
+state_file="$state_dir/polybar-net-${iface}.state"
+rx_file="/sys/class/net/$iface/statistics/rx_bytes"
+tx_file="/sys/class/net/$iface/statistics/tx_bytes"
+
+human() {
+ local bytes="$1"
+
+ if [ "$bytes" -ge 1048576 ]; then
+ awk -v v="$bytes" 'BEGIN { printf "%.1fM", v / 1048576 }'
+ elif [ "$bytes" -ge 1024 ]; then
+ awk -v v="$bytes" 'BEGIN { printf "%.0fK", v / 1024 }'
+ else
+ printf '%sB' "$bytes"
+ fi
+}
+
+if [ ! -r "$rx_file" ] || [ ! -r "$tx_file" ]; then
+ echo "n/a"
+ exit 0
+fi
+
+now="$(date +%s)"
+rx="$(cat "$rx_file")"
+tx="$(cat "$tx_file")"
+
+if [ -r "$state_file" ]; then
+ read -r old_now old_rx old_tx <"$state_file" || true
+else
+ old_now="$now"
+ old_rx="$rx"
+ old_tx="$tx"
+fi
+
+printf '%s %s %s\n' "$now" "$rx" "$tx" >"$state_file"
+
+delta=$((now - old_now))
+if [ "$delta" -le 0 ]; then
+ delta=1
+fi
+
+down=$(((rx - old_rx) / delta))
+up=$(((tx - old_tx) / delta))
+
+if [ "$down" -lt 0 ]; then
+ down=0
+fi
+
+if [ "$up" -lt 0 ]; then
+ up=0
+fi
+
+printf '%s/%s\n' "$(human "$down")" "$(human "$up")"
diff --git a/polybar/scripts/polkit-agent.sh b/polybar/scripts/polkit-agent.sh
new file mode 100755
index 0000000..ed86a5f
--- /dev/null
+++ b/polybar/scripts/polkit-agent.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+export DISPLAY="${DISPLAY:-:0}"
+if [ -z "${XAUTHORITY:-}" ] && [ -r "$HOME/.Xauthority" ]; then
+ export XAUTHORITY="$HOME/.Xauthority"
+fi
+
+if pgrep -u "$UID" -f 'polkit-gnome-authentication-agent-1|polkit-kde-authentication-agent-1|lxqt-policykit-agent|polkit-mate-authentication-agent-1|xfce-polkit' >/dev/null 2>&1; then
+ exit 0
+fi
+
+agents=(
+ /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
+ /usr/lib/polkit-kde-authentication-agent-1
+ /usr/lib/lxqt-policykit/lxqt-policykit-agent
+ /usr/bin/lxqt-policykit-agent
+ /usr/lib/mate-polkit/polkit-mate-authentication-agent-1
+ /usr/lib/xfce-polkit/xfce-polkit
+)
+
+for agent in "${agents[@]}"; do
+ if [ -x "$agent" ]; then
+ exec "$agent"
+ fi
+done
+
+echo "No supported polkit authentication agent found" >&2
+exit 1
diff --git a/polybar/scripts/power-menu.sh b/polybar/scripts/power-menu.sh
new file mode 100755
index 0000000..ab59352
--- /dev/null
+++ b/polybar/scripts/power-menu.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+export DISPLAY="${DISPLAY:-:0}"
+if [ -z "${XAUTHORITY:-}" ] && [ -r "$HOME/.Xauthority" ]; then
+ export XAUTHORITY="$HOME/.Xauthority"
+fi
+
+choice="$(
+ printf '%s\n' lock logout suspend reboot shutdown |
+ dmenu -i -p power \
+ -fn "FiraCode Nerd Font-14" \
+ -nb "#282828" -nf "#ebdbb2" \
+ -sb "#d79921" -sf "#282828"
+)"
+
+case "$choice" in
+ lock)
+ i3lock
+ ;;
+ logout)
+ i3-msg exit
+ ;;
+ suspend)
+ loginctl suspend
+ ;;
+ reboot)
+ loginctl reboot
+ ;;
+ shutdown)
+ loginctl poweroff
+ ;;
+esac
diff --git a/polybar/scripts/scratchpad-status.sh b/polybar/scripts/scratchpad-status.sh
new file mode 100755
index 0000000..426d3b2
--- /dev/null
+++ b/polybar/scripts/scratchpad-status.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+if ! command -v i3-msg >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then
+ echo "n/a"
+ exit 0
+fi
+
+count="$(
+ i3-msg -t get_tree 2>/dev/null |
+ jq '[.. | objects | select(.name == "__i3_scratch") | .floating_nodes[]?] | length' 2>/dev/null || printf '0'
+)"
+
+if [ "$count" -gt 0 ] 2>/dev/null; then
+ echo "%{F#d79921}$count%{F-}"
+else
+ echo "0"
+fi
diff --git a/polybar/scripts/screenshot-menu.sh b/polybar/scripts/screenshot-menu.sh
new file mode 100755
index 0000000..b3064a9
--- /dev/null
+++ b/polybar/scripts/screenshot-menu.sh
@@ -0,0 +1,73 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+dir="${XDG_PICTURES_DIR:-$HOME/Pictures}/Screenshots"
+mkdir -p "$dir" 2>/dev/null || true
+if [ ! -w "$dir" ]; then
+ dir="/tmp"
+fi
+
+export DISPLAY="${DISPLAY:-:0}"
+if [ -z "${XAUTHORITY:-}" ] && [ -r "$HOME/.Xauthority" ]; then
+ export XAUTHORITY="$HOME/.Xauthority"
+fi
+
+notify() {
+ if command -v dunstify >/dev/null 2>&1; then
+ dunstify -a polybar -u low "Screenshot" "$1"
+ fi
+}
+
+menu() {
+ printf '%s\n' full selection window |
+ dmenu -i -p screenshot \
+ -fn "FiraCode Nerd Font-14" \
+ -nb "#282828" -nf "#ebdbb2" \
+ -sb "#d79921" -sf "#282828"
+}
+
+file="$dir/shot-$(date +%Y%m%d-%H%M%S).png"
+choice="$(menu)"
+
+case "$choice" in
+ full)
+ if command -v scrot >/dev/null 2>&1; then
+ scrot "$file"
+ elif command -v import >/dev/null 2>&1; then
+ import -window root "$file"
+ else
+ notify "No screenshot tool found"
+ exit 1
+ fi
+ ;;
+ selection)
+ if command -v flameshot >/dev/null 2>&1; then
+ flameshot gui -p "$dir"
+ exit 0
+ elif command -v maim >/dev/null 2>&1; then
+ maim -s "$file"
+ elif command -v scrot >/dev/null 2>&1; then
+ scrot -s "$file"
+ elif command -v import >/dev/null 2>&1; then
+ import "$file"
+ else
+ notify "No selection screenshot tool found"
+ exit 1
+ fi
+ ;;
+ window)
+ if command -v scrot >/dev/null 2>&1; then
+ scrot -u "$file"
+ elif command -v import >/dev/null 2>&1; then
+ import "$file"
+ else
+ notify "No window screenshot tool found"
+ exit 1
+ fi
+ ;;
+ *)
+ exit 0
+ ;;
+esac
+
+notify "$file"
diff --git a/polybar/scripts/temp-status.sh b/polybar/scripts/temp-status.sh
new file mode 100755
index 0000000..d1f7665
--- /dev/null
+++ b/polybar/scripts/temp-status.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+temp=""
+
+if command -v sensors >/dev/null 2>&1; then
+ temp="$(
+ sensors 2>/dev/null |
+ awk '
+ /Package id 0:/ { gsub(/[^0-9.-]/, "", $4); print int($4); exit }
+ /Tctl:/ { gsub(/[^0-9.-]/, "", $2); print int($2); exit }
+ /temp1:/ && $2 ~ /^\+/ { gsub(/[^0-9.-]/, "", $2); print int($2); exit }
+ '
+ )"
+fi
+
+if [ -z "$temp" ]; then
+ for zone in /sys/class/thermal/thermal_zone*/temp; do
+ [ -r "$zone" ] || continue
+ value="$(cat "$zone")"
+ if [ "$value" -gt 0 ] 2>/dev/null; then
+ temp=$((value / 1000))
+ break
+ fi
+ done
+fi
+
+if [ -z "$temp" ]; then
+ echo "n/a"
+elif [ "$temp" -ge 80 ]; then
+ echo "%{F#cc241d}${temp}C%{F-}"
+elif [ "$temp" -ge 65 ]; then
+ echo "%{F#d79921}${temp}C%{F-}"
+else
+ echo "${temp}C"
+fi
diff --git a/polybar/scripts/updates-action.sh b/polybar/scripts/updates-action.sh
new file mode 100755
index 0000000..a22a747
--- /dev/null
+++ b/polybar/scripts/updates-action.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+cmd='sudo pacman -Syu; echo; read -r -p "Press Enter to close..."'
+
+if command -v kitty >/dev/null 2>&1; then
+ exec kitty --title "System update" /usr/bin/env bash -lc "$cmd"
+elif command -v foot >/dev/null 2>&1; then
+ exec foot --title "System update" /usr/bin/env bash -lc "$cmd"
+elif command -v xterm >/dev/null 2>&1; then
+ exec xterm -T "System update" -e /usr/bin/env bash -lc "$cmd"
+fi
+
+if command -v dunstify >/dev/null 2>&1; then
+ dunstify -a polybar -u normal "Updates" "No terminal found"
+fi
diff --git a/polybar/scripts/updates-status.sh b/polybar/scripts/updates-status.sh
new file mode 100755
index 0000000..75568ad
--- /dev/null
+++ b/polybar/scripts/updates-status.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+if command -v checkupdates >/dev/null 2>&1; then
+ count="$( (checkupdates 2>/dev/null || true) | wc -l)"
+elif command -v pacman >/dev/null 2>&1; then
+ count="$( (pacman -Qu 2>/dev/null || true) | wc -l)"
+else
+ echo "n/a"
+ exit 0
+fi
+
+if [ "$count" -gt 0 ]; then
+ echo "%{F#d79921}$count%{F-}"
+else
+ echo "0"
+fi
diff --git a/polybar/scripts/wg-pc-status.sh b/polybar/scripts/wg-pc-status.sh
new file mode 100755
index 0000000..b708695
--- /dev/null
+++ b/polybar/scripts/wg-pc-status.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+iface="${WG_QUICK_PROFILE:-PC}"
+
+age_label() {
+ local latest now age
+
+ latest="$(wg show "$iface" latest-handshakes 2>/dev/null | awk 'NF { if ($2 > newest) newest = $2 } END { print newest + 0 }')"
+ if [ -z "$latest" ] || [ "$latest" -le 0 ] 2>/dev/null; then
+ return 0
+ fi
+
+ now="$(date +%s)"
+ age=$((now - latest))
+
+ if [ "$age" -lt 60 ]; then
+ printf ' %ss' "$age"
+ elif [ "$age" -lt 3600 ]; then
+ printf ' %sm' "$((age / 60))"
+ elif [ "$age" -lt 86400 ]; then
+ printf ' %sh' "$((age / 3600))"
+ else
+ printf ' %sd' "$((age / 86400))"
+ fi
+}
+
+if [ -d "/sys/class/net/$iface" ]; then
+ echo "%{F#98971a}up$(age_label)%{F-}"
+else
+ echo "%{F#cc241d}down%{F-}"
+fi
diff --git a/polybar/scripts/wg-pc-toggle.sh b/polybar/scripts/wg-pc-toggle.sh
new file mode 100755
index 0000000..5cd102f
--- /dev/null
+++ b/polybar/scripts/wg-pc-toggle.sh
@@ -0,0 +1,82 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+profile="${WG_QUICK_PROFILE:-PC}"
+iface="$profile"
+requested="${1:-toggle}"
+
+export DISPLAY="${DISPLAY:-:0}"
+if [ -z "${XAUTHORITY:-}" ] && [ -r "$HOME/.Xauthority" ]; then
+ export XAUTHORITY="$HOME/.Xauthority"
+fi
+
+case "$profile" in
+ *[!A-Za-z0-9_.@-]*|"")
+ echo "Invalid WireGuard profile: $profile" >&2
+ exit 2
+ ;;
+esac
+
+log_file="${XDG_RUNTIME_DIR:-/tmp}/polybar-wg-$profile.log"
+
+notify() {
+ if command -v dunstify >/dev/null 2>&1; then
+ dunstify -a polybar -u low "WireGuard" "$1"
+ fi
+}
+
+polkit_agent_running() {
+ pgrep -u "$UID" -f 'polkit-gnome-authentication-agent-1|polkit-kde-authentication-agent-1|lxqt-policykit-agent|polkit-mate-authentication-agent-1|xfce-polkit' >/dev/null 2>&1
+}
+
+ensure_polkit_agent() {
+ if polkit_agent_running; then
+ return 0
+ fi
+
+ if [ -x /home/aag/.config/polybar/scripts/polkit-agent.sh ]; then
+ /usr/bin/setsid -f /home/aag/.config/polybar/scripts/polkit-agent.sh >>"$log_file" 2>&1
+ sleep 0.5
+ fi
+
+ polkit_agent_running
+}
+
+case "$requested" in
+ up|down)
+ action="$requested"
+ ;;
+ toggle)
+ if [ -d "/sys/class/net/$iface" ]; then
+ action="down"
+ else
+ action="up"
+ fi
+ ;;
+ *)
+ notify "Unknown action: $requested"
+ exit 2
+ ;;
+esac
+
+if [ "$(id -u)" -eq 0 ]; then
+ auth=()
+elif command -v pkexec >/dev/null 2>&1; then
+ if ! ensure_polkit_agent; then
+ notify "No polkit agent running"
+ echo "No polkit authentication agent is running." >"$log_file"
+ echo "Install/start polkit-gnome, then restart i3 or run ~/.config/polybar/scripts/polkit-agent.sh." >>"$log_file"
+ exit 1
+ fi
+ auth=(pkexec --disable-internal-agent)
+else
+ notify "pkexec not found"
+ exit 1
+fi
+
+if "${auth[@]}" /usr/bin/wg-quick "$action" "$profile" >"$log_file" 2>&1; then
+ notify "$profile $action complete"
+else
+ notify "$profile $action failed: $log_file"
+ exit 1
+fi