diff --git a/hyprland/.config/hypr/env.lua b/hyprland/.config/hypr/env.lua
index d107e6d..ca4f03d 100644
--- a/hyprland/.config/hypr/env.lua
+++ b/hyprland/.config/hypr/env.lua
@@ -1,3 +1,4 @@
-- See https://wiki.hypr.land/Configuring/Advanced-and-Cool/Environment-variables/
hl.env("XCURSOR_SIZE", "24")
hl.env("QT_QPA_PLATFORMTHEME", "qt5ct") -- change to qt6ct if you have that
+hl.env("SUDO_ASKPASS", os.getenv("HOME") .. "/.local/bin/sudo-askpass.sh")
diff --git a/hyprland/.config/hypr/exec.lua b/hyprland/.config/hypr/exec.lua
index 23f6028..92927ca 100644
--- a/hyprland/.config/hypr/exec.lua
+++ b/hyprland/.config/hypr/exec.lua
@@ -1,11 +1,14 @@
-- Autostart. See https://wiki.hypr.land/Configuring/Basics/Autostart/
hl.on("hyprland.start", function()
hl.exec_cmd("hyprctl setcursor catppuccin-mocha-dark-cursors 28 &")
- hl.exec_cmd("systemctl --user start hyprpolkitagent &")
+ hl.exec_cmd("systemctl --user start plasma-polkit-agent &")
hl.exec_cmd("waybar & mako & hyprpaper & hypridle & wluma & hyprshell run &")
- hl.exec_cmd("systemctl --user start hypr-lid.service &")
- hl.exec_cmd("blueman-applet &")
hl.exec_cmd("1password --silent &")
- hl.exec_cmd("op signin &")
+ hl.exec_cmd("systemctl --user start hyprland-session.target &")
+ hl.exec_cmd("blueman-applet &")
hl.exec_cmd("sunsetr &")
end)
+
+hl.on("hyprland.shutdown", function()
+ os.execute("systemctl --user stop hyprland-session.target && sleep 0.1")
+end)
diff --git a/hyprland/.config/hypr/hyprland.lua b/hyprland/.config/hypr/hyprland.lua
index 5b43983..62cb745 100644
--- a/hyprland/.config/hypr/hyprland.lua
+++ b/hyprland/.config/hypr/hyprland.lua
@@ -67,7 +67,9 @@ hl.config({
})
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
-hl.animation({ leaf = "borderangle", enabled = true, speed = 50, bezier = "linear", style = "loop" })
+-- borderangle disabled: the old "loop" style animated continuously, forcing
+-- constant redraws and spiking idle CPU usage even with VFR enabled.
+hl.animation({ leaf = "borderangle", enabled = false, speed = 50, bezier = "linear", style = "loop" })
hl.animation({ leaf = "workspaces", enabled = true, speed = 0.5, bezier = "default" })
hl.animation({ leaf = "windows", enabled = false })
hl.animation({ leaf = "fade", enabled = false })
@@ -83,9 +85,23 @@ hl.config({
-- new_is_master = true
-- }
+-- CPU/power efficiency. See https://wiki.hypr.land/Configuring/Variables/#misc
hl.config({
misc = {
force_default_wallpaper = 0, -- Set to 0 or 1 to disable the anime mascot wallpapers
middle_click_paste = false,
+ -- Don't wake/re-render just because the cursor crossed a monitor edge.
+ mouse_move_focuses_monitor = false,
+ -- Skip drawing the Hyprland logo in the corner of empty workspaces.
+ disable_hyprland_logo = true,
+ },
+})
+
+-- Variable framerate: only redraw when the scene actually changes instead of
+-- running at a fixed rate. This is the single biggest idle-CPU saver; it is
+-- true by default, but set explicitly (note it lives under `debug:`).
+hl.config({
+ debug = {
+ vfr = true,
},
})
diff --git a/hyprland/.config/systemd/user/hyprland-session.target b/hyprland/.config/systemd/user/hyprland-session.target
new file mode 100644
index 0000000..be479fb
--- /dev/null
+++ b/hyprland/.config/systemd/user/hyprland-session.target
@@ -0,0 +1,6 @@
+[Unit]
+Description=Hyprland session
+BindsTo=graphical-session.target
+Wants=graphical-session-pre.target
+After=graphical-session-pre.target
+PropagatesStopTo=graphical-session.target
diff --git a/hyprland/.local/bin/btop.sh b/hyprland/.local/bin/btop.sh
new file mode 100755
index 0000000..59379f4
--- /dev/null
+++ b/hyprland/.local/bin/btop.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+# Open btop fullscreen in a ghostty window; focus it if it is already open.
+# Intended as the on-click action for the waybar cpu/memory modules.
+set -uo pipefail
+
+if hyprctl clients -j 2>/dev/null | jq -e 'any(.[]; ((.title // "") + " " + (.class // "")) | ascii_downcase | contains("btop"))' >/dev/null 2>&1; then
+ hyprctl dispatch 'hl.dsp.focus({ window = "title:btop" })' 2>/dev/null || true
+ exit 0
+fi
+
+ghostty -e btop &
+sleep 0.5
+hyprctl dispatch 'hl.dsp.window.fullscreen({ action = "set" })' 2>/dev/null || true
diff --git a/hyprland/.local/bin/sudo-askpass.sh b/hyprland/.local/bin/sudo-askpass.sh
new file mode 100755
index 0000000..8185a96
--- /dev/null
+++ b/hyprland/.local/bin/sudo-askpass.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+# sudo-askpass: GUI password prompt for sudo via $SUDO_ASKPASS.
+#
+# sudo calls the program pointed to by $SUDO_ASKPASS when it cannot prompt on a
+# terminal (or with `sudo -A`) and reads the password from its stdout. zenity's
+# --password dialog does exactly that. If the dialog is cancelled or times out
+# zenity exits non-zero and sudo aborts.
+
+set -uo pipefail
+
+exec zenity --title="sudo authentication" \
+ --password \
+ --text="Enter password for $(id -un):" \
+ --timeout=120 2>/dev/null
diff --git a/mako/.config/mako/config b/mako/.config/mako/config
index 75ec128..5d06d52 100644
--- a/mako/.config/mako/config
+++ b/mako/.config/mako/config
@@ -1,5 +1,35 @@
+# https://github.com/emersion/mako/wiki/Configuration
+
max-history=10
# Style
default-timeout=5000
layer=overlay
+anchor=top-right
+border-radius=10
+font="MesloLGS Nerd Font Mono Bold 12"
+icons=1
+max-icon-size=48
+
+# Catppuccin Mocha palette (matches waybar mocha.css)
+background-color=#1e1e2e
+text-color=#cdd6f4
+border-color=#cba6f7
+progress-color=#a6e3a1
+
+# Grouping
+group-by=app-name
+sort=-time
+
+[grouped]
+format=(%g) %s\n%b
+
+[urgency=low]
+border-color=#94e2d5
+default-timeout=8000
+
+[urgency=normal]
+
+[urgency=critical]
+border-color=#f38ba8
+default-timeout=0
diff --git a/systemd/.config/systemd/user/cloudflared-k8s.service b/systemd/.config/systemd/user/cloudflared-k8s.service
index 0d1931b..c9f1184 100644
--- a/systemd/.config/systemd/user/cloudflared-k8s.service
+++ b/systemd/.config/systemd/user/cloudflared-k8s.service
@@ -2,22 +2,29 @@
# systemctl --user daemon-reload
# systemctl --user enable --now cloudflared-k8s.service
# systemctl --user status cloudflared-k8s.service
+#
+# The Cloudflare Access service token for k8s-api lives in the "Homelab"
+# 1Password vault (item "K8s API Service Token", section "Service Token") and
+# is injected at runtime via `op run` -- the secret is never written to disk.
[Unit]
Description=Cloudflare Tunnel to K8s API
-After=network-online.target graphical-session.target
-BindsTo=graphical-session.target
+After=network-online.target graphical-session.target op-cli-signin.service
Wants=network-online.target systemd-networkd-wait-online.service
+BindsTo=graphical-session.target
StartLimitIntervalSec=500
StartLimitBurst=5
[Service]
PassEnvironment=DISPLAY WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS XDG_RUNTIME_DIR
-ExecStart=uwsm app -- /usr/bin/cloudflared access tcp --hostname k8s.samoneal.io --url localhost:6443
+Environment="OP_BIOMETRIC_UNLOCK_ENABLED=true"
+Environment="CF_ACCESS_CLIENT_ID=op://Homelab/K8s API Service Token/Service Token/client-id"
+Environment="CF_ACCESS_CLIENT_SECRET=op://Homelab/K8s API Service Token/Service Token/client-secret"
+ExecStart=/usr/bin/op run -- /usr/bin/cloudflared access tcp --hostname k8s.samoneal.io --url localhost:6443
Restart=on-failure
RestartSec=60
[Install]
-WantedBy=default.target
+WantedBy=graphical-session.target
diff --git a/systemd/.config/systemd/user/gdrive-rclone.service b/systemd/.config/systemd/user/gdrive-rclone.service
index 0fc38b7..062de2e 100644
--- a/systemd/.config/systemd/user/gdrive-rclone.service
+++ b/systemd/.config/systemd/user/gdrive-rclone.service
@@ -1,8 +1,8 @@
[Unit]
Description=Connect to GDrive via RClone
After=network-online.target graphical-session.target
+Wants=network-online.target
BindsTo=graphical-session.target
-Wants=network-online.target graphical-session.target
[Service]
PassEnvironment=DISPLAY WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS XDG_RUNTIME_DIR
@@ -11,4 +11,4 @@ Restart=on-failure
RestartSec=60
[Install]
-WantedBy=default.target
+WantedBy=graphical-session.target
diff --git a/systemd/.config/systemd/user/mnt-nas-credentials.service b/systemd/.config/systemd/user/mnt-nas-credentials.service
index 45a99ed..ce16755 100644
--- a/systemd/.config/systemd/user/mnt-nas-credentials.service
+++ b/systemd/.config/systemd/user/mnt-nas-credentials.service
@@ -1,17 +1,18 @@
[Unit]
Description=Fetch NAS credentials from 1Password
-After=op-cli-signin.service
+After=graphical-session.target op-cli-signin.service
+BindsTo=graphical-session.target
[Service]
Type=oneshot
-Environment="DISPLAY=:0"
-Environment="WAYLAND_DISPLAY=wayland-0"
+PassEnvironment=DISPLAY WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS XDG_RUNTIME_DIR
Environment="OP_BIOMETRIC_UNLOCK_ENABLED=true"
-ExecStart=sudo -u sammieo -E /home/sammieo/.local/bin/fetch-nas-credentials.sh
-ExecStartPost=sudo -u sammieo /usr/bin/systemctl start nas-mounts.target
+Environment="SUDO_ASKPASS=/home/sammieo/.local/bin/sudo-askpass.sh"
+ExecStart=sudo -A -u sammieo -E /home/sammieo/.local/bin/fetch-nas-credentials.sh
+ExecStartPost=sudo -A -u sammieo /usr/bin/systemctl start nas-mounts.target
RemainAfterExit=yes
StandardOutput=journal
StandardError=journal
[Install]
-WantedBy=default.target
+WantedBy=graphical-session.target
diff --git a/systemd/.config/systemd/user/op-cli-signin.service b/systemd/.config/systemd/user/op-cli-signin.service
index 95e6948..61c1d66 100644
--- a/systemd/.config/systemd/user/op-cli-signin.service
+++ b/systemd/.config/systemd/user/op-cli-signin.service
@@ -1,13 +1,19 @@
[Unit]
Description=1Password CLI SignIn
After=graphical-session.target
+BindsTo=graphical-session.target
+
+StartLimitIntervalSec=300
+StartLimitBurst=10
[Service]
Type=oneshot
Environment="DISPLAY=:0"
Environment="WAYLAND_DISPLAY=wayland-0"
Environment="OP_BIOMETRIC_UNLOCK_ENABLED=true"
-ExecStart=/bin/sh op signin
+ExecStart=/usr/bin/op signin
+Restart=on-failure
+RestartSec=5
[Install]
-WantedBy=default.target
+WantedBy=graphical-session.target
diff --git a/waybar/.config/waybar/config.jsonc b/waybar/.config/waybar/config.jsonc
index 885ff20..ff2a4de 100644
--- a/waybar/.config/waybar/config.jsonc
+++ b/waybar/.config/waybar/config.jsonc
@@ -2,7 +2,7 @@
"position": "top",
"modules-left": ["hyprland/workspaces", "mpris"],
"modules-center": ["hyprland/window"],
- "modules-right": ["cpu", "memory", "bluetooth", "network", "pulseaudio", "battery", "clock", "power-profiles-daemon", "custom/power"],
+ "modules-right": ["cpu", "memory", "tray", "bluetooth", "network", "pulseaudio", "battery", "clock", "power-profiles-daemon", "custom/power"],
"clock": {
"format": " {:%a %d %H:%M}",
"tooltip-format": "{:%Y %B}\n{calendar}"
@@ -22,6 +22,24 @@
"format-icons": ["", "", "", "", ""],
"tooltip-format": "{time}"
},
+ "cpu": {
+ "on-click": "~/.local/bin/btop.sh"
+ },
+ "memory": {
+ "on-click": "~/.local/bin/btop.sh"
+ },
+ "tray": {
+ "icon-size": 16,
+ "icon-theme": "Papirus-Dark",
+ "spacing": 8,
+ "tooltip": true,
+ "menu": {
+ "enabled": true,
+ "arrow": false,
+ "separator": true,
+ "size": 16
+ }
+ },
"network": {
"format-wifi": " {essid}",
"format-ethernet": " Disconnected",
diff --git a/waybar/.config/waybar/style.css b/waybar/.config/waybar/style.css
index 3b8b9ce..c3d9c2d 100644
--- a/waybar/.config/waybar/style.css
+++ b/waybar/.config/waybar/style.css
@@ -122,6 +122,20 @@ button:hover {
border-bottom: 2px solid @mauve;
}
+#tray {
+ color: @lavender;
+}
+
+#tray > .passive {
+ opacity: 0.7;
+}
+
+#tray > .needs-attention {
+ background-color: rgba(0, 0, 0, 0.3);
+ color: @red;
+ border-bottom: 2px solid @red;
+}
+
#custom-power {
color: #ec9974;
}