Compare commits

...

11 Commits

Author SHA1 Message Date
sam_oneal 2d5a4e9f4c updated waybar power menu list 2026-09-07 20:13:04 -06:00
sam_oneal 8d2aa49668 updated to latest submodule 2026-09-07 20:01:50 -06:00
sam_oneal f60b7e263b hyprland: SUPER+Right creates a new workspace past the end of a monitor
m+1/m-1 only cycle existing workspaces on the active monitor and wrap at
the edges, so SUPER+Right never created a workspace. Replace them with a
Lua helper: advancing past the last workspace on the monitor now creates
a fresh (empty) workspace there; going before the first wraps to the
monitor's last workspace.
2026-09-07 18:26:27 -06:00
sam_oneal b148fcc256 hyprland: add SUPER+arrow workspace cycling and overview binds
SUPER+Left/Right cycle existing workspaces on the active monitor
(m+1/m-1 with cycling enabled), SUPER+Up opens the hyprshell overview
scoped to the current monitor (create/manage workspaces there), and
SUPER+Down closes the overview again. Adds binds-help.sh cheat sheet
(SHIFT+SUPER+H) and overview-close.sh helper.
2026-09-07 18:14:27 -06:00
sam_oneal 09c8706f35 fixed issue with lid closing was not being detected 2026-09-07 15:45:44 -06:00
sam_oneal 9d788d42e1 changed how closing windows worked; updated how the cloudflare access service works 2026-08-31 19:54:42 -06:00
sam_oneal 086097f2a9 waybar: add weather module to bar
Add custom/weather module between battery and clock, polling
weather.sh every 10 minutes.
2026-08-18 08:03:33 -06:00
sam_oneal 1755d17558 hyprlock: disable fingerprint, add battery/weather/notifications
- Disable fingerprint auth (GDM holds the device; hyprlock can't
  claim it, blocking the auth thread).
- Fix date format escaping (single quotes).
- Add battery, weather, and latest mako notification labels.
- Add hyprlock wrapper (crash guard), battery script, notifications
  script, and weather script.
2026-08-18 08:02:50 -06:00
sam_oneal 9e310d2f58 hyprland: prepend PATH, gate idle inhibitors, allow lock restore
- Prepend ~/.local/bin to PATH so user scripts (hyprlock wrapper,
  weather.sh) resolve before system binaries.
- Gate dpms-off and suspend on lock screen running (condition_cmd)
  so idle inhibitors from apps work while unlocked but screen still
  blanks and machine suspends after lock.
- Enable allow_session_lock_restore so a crashed lock client can be
  replaced.
- Temporarily enable debug logging to diagnose re-lock issues.
2026-08-18 08:02:16 -06:00
sam_oneal 21dbb16e8b zsh: add pv-backed mv/cp wrappers with progress and dir creation
Override mv and cp to pipe large transfers through pv for progress.
Directories are streamed via tar+pv; single files go through pv
directly. Parent directories are created (mkdir -p) when the
destination path doesn't exist yet.
2026-08-18 08:01:05 -06:00
sam_oneal 5cfa02ea6b make: exclude local-only packages and use --restow for idempotency
Filter out resources and wluma from PACKAGES since they are
local-only and should not be stowed. Use stow --restow so make link
can be run repeatedly without conflicts from pre-existing symlinks.
2026-08-18 08:00:40 -06:00
22 changed files with 773 additions and 75 deletions
+3 -3
View File
@@ -1,6 +1,6 @@
HOME_TARGET ?= $(HOME) HOME_TARGET ?= $(HOME)
SYSTEM_DIRS := etc usr var opt boot SYSTEM_DIRS := etc usr var opt boot
PACKAGES := $(patsubst %/,%,$(wildcard */)) PACKAGES := $(filter-out resources wluma,$(patsubst %/,%,$(wildcard */)))
.PHONY: check-stow pull-updates unlink link .PHONY: check-stow pull-updates unlink link
@@ -77,10 +77,10 @@ link: check-stow
done; \ done; \
if [ "$$is_sys" -eq 1 ]; then \ if [ "$$is_sys" -eq 1 ]; then \
echo " [System] Stowing $$pkg/$$sub to $$sub"; \ echo " [System] Stowing $$pkg/$$sub to $$sub"; \
sudo stow -d "$$pkg" -t "/$$sub" "$$sub"; \ sudo stow --restow -d "$$pkg" -t "/$$sub" "$$sub"; \
else \ else \
echo " [USER] Stowing $$pkg/$$sub to $(HOME_TARGET)/$$sub"; \ echo " [USER] Stowing $$pkg/$$sub to $(HOME_TARGET)/$$sub"; \
stow -d "$$pkg" -t "$(HOME_TARGET)/$$sub" "$$sub"; \ stow --restow -d "$$pkg" -t "$(HOME_TARGET)/$$sub" "$$sub"; \
fi; \ fi; \
done; \ done; \
done done
+85 -38
View File
@@ -1,71 +1,118 @@
-- See https://wiki.hypr.land/Configuring/Basics/Binds/ for more -- See https://wiki.hypr.land/Configuring/Basics/Binds/ for more
-- Sound through pactl -- Sound through pactl
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("pactl set-sink-volume @DEFAULT_SINK@ +10%")) hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("pactl set-sink-volume @DEFAULT_SINK@ +10%"), { desc = "Raise volume" })
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("pactl set-sink-volume @DEFAULT_SINK@ -10%")) hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("pactl set-sink-volume @DEFAULT_SINK@ -10%"), { desc = "Lower volume" })
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("pactl set-sink-mute @DEFAULT_SINK@ toggle")) hl.bind("XF86AudioMute", hl.dsp.exec_cmd("pactl set-sink-mute @DEFAULT_SINK@ toggle"), { desc = "Mute audio" })
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause")) hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { desc = "Play/pause media" })
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous")) hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { desc = "Previous track" })
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next")) hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { desc = "Next track" })
-- Brightness through brightnessctl -- Brightness through brightnessctl
hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl set +5%")) hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl set +5%"), { desc = "Increase brightness" })
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl set 5%-")) hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl set 5%-"), { desc = "Decrease brightness" })
-- Main modifier binds -- Main modifier binds
hl.bind(mainMod .. " + T", hl.dsp.exec_cmd(terminal)) hl.bind(mainMod .. " + T", hl.dsp.exec_cmd(terminal), { desc = "Open terminal" })
hl.bind(mainMod .. " + B", hl.dsp.exec_cmd(browser)) hl.bind(mainMod .. " + B", hl.dsp.exec_cmd(browser), { desc = "Open browser" })
hl.bind(mainMod .. " + F", hl.dsp.exec_cmd(fileManager)) hl.bind(mainMod .. " + F", hl.dsp.exec_cmd(fileManager), { desc = "Open file manager" })
hl.bind(mainMod .. " + C", hl.dsp.window.kill()) hl.bind(mainMod .. " + C", hl.dsp.window.close(), { desc = "Close active window" })
hl.bind(mainMod .. " + M", hl.dsp.exit()) hl.bind(mainMod .. " + Q", hl.dsp.exec_cmd("hyprctl dispatch closewindow \"class:$(hyprctl activewindow -j | jq -r .class)\""), { desc = "Close active window (by class)" })
hl.bind(mainMod .. " + V", hl.dsp.window.float({ action = "toggle" })) hl.bind(mainMod .. " + M", hl.dsp.exit(), { desc = "Exit Hyprland" })
hl.bind(mainMod .. " + space", hl.dsp.exec_cmd(menu)) hl.bind(mainMod .. " + V", hl.dsp.window.float({ action = "toggle" }), { desc = "Toggle floating" })
hl.bind(SUPER_SHIFT .. " + L", hl.dsp.exec_cmd("hyprlock")) hl.bind(mainMod .. " + space", hl.dsp.exec_cmd(menu), { desc = "Open app menu" })
hl.bind(SUPER_SHIFT .. " + R", hl.dsp.exec_cmd("hyprctl reload")) hl.bind(SUPER_SHIFT .. " + L", hl.dsp.exec_cmd("hyprlock"), { desc = "Lock screen" })
hl.bind(SUPER_SHIFT .. " + R", hl.dsp.exec_cmd("hyprctl reload"), { desc = "Reload config" })
-- Monique: GUI monitor arrangement/display settings -- Monique: GUI monitor arrangement/display settings
hl.bind(SUPER_SHIFT .. " + M", hl.dsp.exec_cmd("monique")) hl.bind(SUPER_SHIFT .. " + M", hl.dsp.exec_cmd("monique"), { desc = "Open monique (monitor settings)" })
hl.bind(SUPER_SHIFT .. " + D", hl.dsp.exec_cmd("hyprctl keyword monitor eDP-1, disable")) hl.bind(SUPER_SHIFT .. " + D", hl.dsp.exec_cmd("hyprctl keyword monitor eDP-1, disable"), { desc = "Disable eDP-1 (clamshell)" })
hl.bind(SUPER_SHIFT .. " + F", hl.dsp.exec_cmd("hyprctl keyword monitor eDP-1, enable")) hl.bind(SUPER_SHIFT .. " + F", hl.dsp.exec_cmd("hyprctl keyword monitor eDP-1, enable"), { desc = "Enable eDP-1" })
-- Screenshots -- Screenshots
hl.bind("Print", hl.dsp.exec_cmd("grim -c -o \"$(hyprctl activeworkspace -j | jq -r '.monitor')\" $(xdg-user-dir PICTURES)/Screenshots/$(date +'%s_grim.png')")) hl.bind("Print", hl.dsp.exec_cmd("grim -c -o \"$(hyprctl activeworkspace -j | jq -r '.monitor')\" $(xdg-user-dir PICTURES)/Screenshots/$(date +'%s_grim.png')"), { desc = "Screenshot active monitor" })
hl.bind("SHIFT + Print", hl.dsp.exec_cmd("grim -g \"$(slurp)\" $(xdg-user-dir PICTURES)/Screenshots/$(date +'%s_grim.png')")) hl.bind("SHIFT + Print", hl.dsp.exec_cmd("grim -g \"$(slurp)\" $(xdg-user-dir PICTURES)/Screenshots/$(date +'%s_grim.png')"), { desc = "Screenshot region" })
-- Dwindle layout -- Dwindle layout
hl.bind(mainMod .. " + P", hl.dsp.window.pseudo()) -- dwindle hl.bind(mainMod .. " + P", hl.dsp.window.pseudo(), { desc = "Toggle pseudo tiling" }) -- dwindle
hl.bind(mainMod .. " + E", hl.dsp.layout("togglesplit")) -- dwindle hl.bind(mainMod .. " + E", hl.dsp.layout("togglesplit"), { desc = "Toggle split" }) -- dwindle
hl.bind(mainMod .. " + CTRL + F", hl.dsp.window.fullscreen({ action = "toggle" })) -- dwindle hl.bind(mainMod .. " + CTRL + F", hl.dsp.window.fullscreen({ action = "toggle" }), { desc = "Toggle fullscreen" }) -- dwindle
hl.bind(mainMod .. " + W", hl.dsp.group.toggle()) -- dwindle hl.bind(mainMod .. " + W", hl.dsp.group.toggle(), { desc = "Toggle window group" }) -- dwindle
-- Move focus with mainMod + vim keys -- Move focus with mainMod + vim keys
hl.bind(mainMod .. " + H", hl.dsp.focus({ direction = "l" })) hl.bind(mainMod .. " + H", hl.dsp.focus({ direction = "l" }), { desc = "Focus left" })
hl.bind(mainMod .. " + L", hl.dsp.focus({ direction = "r" })) hl.bind(mainMod .. " + L", hl.dsp.focus({ direction = "r" }), { desc = "Focus right" })
hl.bind(mainMod .. " + K", hl.dsp.focus({ direction = "u" })) hl.bind(mainMod .. " + K", hl.dsp.focus({ direction = "u" }), { desc = "Focus up" })
hl.bind(mainMod .. " + J", hl.dsp.focus({ direction = "d" })) hl.bind(mainMod .. " + J", hl.dsp.focus({ direction = "d" }), { desc = "Focus down" })
-- Cycle workspaces on the active monitor with mainMod + arrows. Advancing past
-- the last workspace on the monitor creates a fresh (empty) workspace there;
-- going before the first wraps around to the last one on the monitor.
local function step_workspace(dir)
local mon = hl.get_active_monitor()
if not mon then return end
local current = mon.active_workspace.id
local mon_ids, all_max = {}, 0
for _, ws in ipairs(hl.get_workspaces()) do
if ws.id > all_max then all_max = ws.id end
if ws.monitor and ws.monitor.id == mon.id then
table.insert(mon_ids, ws.id)
end
end
table.sort(mon_ids)
local target
if dir > 0 then
for _, id in ipairs(mon_ids) do
if id > current then target = id break end
end
-- No next workspace on this monitor: create one right here.
if not target then target = all_max + 1 end
else
for i = #mon_ids, 1, -1 do
if mon_ids[i] < current then target = mon_ids[i] break end
end
-- No previous workspace on this monitor: wrap to the last one.
if not target and #mon_ids > 0 then target = mon_ids[#mon_ids] end
end
if target then hl.dispatch(hl.dsp.focus({ workspace = target })) end
end
hl.bind(mainMod .. " + Left", function() step_workspace(-1) end, { desc = "Previous workspace (active monitor)" })
hl.bind(mainMod .. " + Right", function() step_workspace(1) end, { desc = "Next workspace (active monitor); creates one past the end" })
-- Switch workspaces with mainMod + [0-9] -- Switch workspaces with mainMod + [0-9]
for i = 1, 10 do for i = 1, 10 do
local key = i % 10 -- 10 maps to key 0 local key = i % 10 -- 10 maps to key 0
hl.bind(mainMod .. " + " .. key, hl.dsp.focus({ workspace = i })) hl.bind(mainMod .. " + " .. key, hl.dsp.focus({ workspace = i }), { desc = "Go to workspace " .. i })
end end
-- Move active window to a workspace with mainMod + SHIFT + [0-9] -- Move active window to a workspace with mainMod + SHIFT + [0-9]
for i = 1, 10 do for i = 1, 10 do
local key = i % 10 -- 10 maps to key 0 local key = i % 10 -- 10 maps to key 0
hl.bind(mainMod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i })) hl.bind(mainMod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i }), { desc = "Move window to workspace " .. i })
end end
-- Example special workspace (scratchpad) -- Example special workspace (scratchpad)
hl.bind(mainMod .. " + S", hl.dsp.workspace.toggle_special("magic")) hl.bind(mainMod .. " + S", hl.dsp.workspace.toggle_special("magic"), { desc = "Toggle scratchpad" })
hl.bind(mainMod .. " + SHIFT + S", hl.dsp.window.move({ workspace = "special:magic" })) hl.bind(mainMod .. " + SHIFT + S", hl.dsp.window.move({ workspace = "special:magic" }), { desc = "Move window to scratchpad" })
-- Scroll through existing workspaces with mainMod + scroll -- Scroll through existing workspaces with mainMod + scroll
hl.bind(mainMod .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" })) hl.bind(mainMod .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" }), { desc = "Next workspace (scroll)" })
hl.bind(mainMod .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" })) hl.bind(mainMod .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" }), { desc = "Previous workspace (scroll)" })
-- Keybind cheat sheet
hl.bind(SUPER_SHIFT .. " + H", hl.dsp.exec_cmd("binds-help.sh"), { desc = "Keybind cheat sheet" })
-- Workspace overview (macOS Mission Control style). SUPER + Up is registered by
-- the hyprshell daemon itself ("Open Overview with Super + Up"); SUPER + Down
-- closes the overview again without switching.
hl.bind(mainMod .. " + Down", hl.dsp.exec_cmd("overview-close.sh"), { desc = "Close workspace overview" })
-- Move/resize windows with mainMod + LMB/RMB and dragging -- Move/resize windows with mainMod + LMB/RMB and dragging
hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true }) hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true, desc = "Drag window" })
hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true }) hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true, desc = "Resize window" })
-- Laptop lid is handled by hypr-lid-power.service (delayed lock/suspend when -- Laptop lid is handled by hypr-lid-power.service (delayed lock/suspend when
-- closed undocked; display/clamshell handling is done by moniqued) instead of -- closed undocked; display/clamshell handling is done by moniqued) instead of
+4
View File
@@ -2,3 +2,7 @@
hl.env("XCURSOR_SIZE", "24") hl.env("XCURSOR_SIZE", "24")
hl.env("QT_QPA_PLATFORMTHEME", "qt5ct") -- change to qt6ct if you have that 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") hl.env("SUDO_ASKPASS", os.getenv("HOME") .. "/.local/bin/sudo-askpass.sh")
-- Prepend ~/.local/bin so `hyprlock` (and other user scripts) resolve to the
-- crash-guard wrapper instead of /usr/bin/hyprlock. Hyprland's exec/keybind
-- PATH is otherwise the system PATH and misses user-local binaries.
hl.env("PATH", os.getenv("HOME") .. "/.local/bin:" .. (os.getenv("PATH") or "/usr/bin:/bin"))
+12
View File
@@ -23,14 +23,26 @@ listener {
on-timeout = loginctl lock-session # lock screen when timeout has passed on-timeout = loginctl lock-session # lock screen when timeout has passed
} }
# The dpms-off and suspend listeners ignore idle inhibitors (Steam, browsers,
# etc. hold them) so the screen still blanks and the machine still suspends
# after the screen is locked -- otherwise a held inhibitor would keep the
# locked screen on forever. `condition_cmd` gates them on the lock screen
# actually running, so inhibitors keep working as intended while unlocked
# (e.g. a video keeps the screen awake).
listener { listener {
timeout = 330 # 5.5min timeout = 330 # 5.5min
ignore_inhibit = true # fire even while apps hold idle inhibitors
condition_cmd = pidof hyprlock > /dev/null # ...but only once the session is locked
condition_retry = 30 # if not locked yet, re-check every 30s while idle
on-timeout = hyprctl dispatch dpms off # screen off when timeout has passed on-timeout = hyprctl dispatch dpms off # screen off when timeout has passed
on-resume = hyprctl dispatch dpms on && brightnessctl -r # screen on when activity is detected after timeout has fired. on-resume = hyprctl dispatch dpms on && brightnessctl -r # screen on when activity is detected after timeout has fired.
} }
listener { listener {
timeout = 1800 # 30min timeout = 1800 # 30min
ignore_inhibit = true # fire even while apps hold idle inhibitors
condition_cmd = pidof hyprlock > /dev/null # ...but only while the session is locked
condition_retry = 60 # if not locked yet, re-check every 60s while idle
on-timeout = systemctl suspend # suspend pc on-timeout = systemctl suspend # suspend pc
} }
+14
View File
@@ -39,6 +39,14 @@ hl.config({
}, },
}) })
hl.config({
binds = {
-- Let SUPER+scroll workspace cycling wrap around instead of stopping at
-- the edges. (SUPER+Left/Right wrapping is handled directly in binds.lua.)
allow_workspace_cycles = true,
},
})
hl.config({ hl.config({
decoration = { decoration = {
rounding = 4, rounding = 4,
@@ -94,6 +102,9 @@ hl.config({
mouse_move_focuses_monitor = false, mouse_move_focuses_monitor = false,
-- Skip drawing the Hyprland logo in the corner of empty workspaces. -- Skip drawing the Hyprland logo in the corner of empty workspaces.
disable_hyprland_logo = true, disable_hyprland_logo = true,
-- Allow a new hyprlock to take over the lock after a crash; without this a
-- dead lock client leaves the session permanently locked (no lockscreen).
allow_session_lock_restore = true,
}, },
}) })
@@ -103,5 +114,8 @@ hl.config({
hl.config({ hl.config({
debug = { debug = {
vfr = true, vfr = true,
-- Temporarily on to diagnose the mystery re-lock (disable_logs=true
-- default hides dispatch exec lines from hyprland.log).
disable_logs = false,
}, },
}) })
+11 -8
View File
@@ -1,8 +1,11 @@
-- Managed by monique (run `monique` or SUPER+SHIFT+M to arrange). The built-in -- Generated by Monique - https://github.com/ToRvaLDz/monique
-- panel is managed at runtime by moniqued's clamshell mode: enabled here, then
-- disabled while the lid is closed with external displays attached. hl.monitor({
-- See https://wiki.hypr.land/Configuring/Basics/Monitors/ for more output = "desc:BOE 0x0BC9",
hl.monitor({ output = "desc:BOE 0x0BC9", mode = "preferred", position = "0x0", scale = 1.0 }) mode = "2560x1600@165",
hl.monitor({ output = "desc:Viewteck Co. Ltd. GNV34DB", mode = "3440x1440@50.0", position = "3795x1785", scale = 1.0 }) position = "0x0",
hl.monitor({ output = "desc:Dell Inc. DELL U2515HX GMDK46C4053L", mode = "2560x1440@59.95", position = "5488x345", scale = 1.0 }) scale = 1,
hl.monitor({ output = "desc:Dell Inc. DELL U2518D 0WG2J9C5B3WL", mode = "2560x1440@59.95", position = "2928x345", scale = 1.0 }) cm = "srgb",
sdr_min_luminance = 0.2,
sdr_max_luminance = 80,
})
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Searchable cheat sheet of Hyprland keybindings, shown in wofi.
#
# The descriptions shown here come from the `desc` option passed to hl.bind()
# in ~/.config/hypr/binds.lua, read back at runtime via `hyprctl binds -j`,
# so the cheat sheet always matches the live config.
set -u
mods_from_mask() {
local mask="$1" out=""
(( mask & 1 )) && out="${out:+$out + }SHIFT"
(( mask & 4 )) && out="${out:+$out + }CTRL"
(( mask & 8 )) && out="${out:+$out + }ALT"
(( mask & 64 )) && out="${out:+$out + }SUPER"
(( mask & 128 )) && out="${out:+$out + }MOD5"
printf "%s" "$out"
}
hyprctl binds -j |
jq -rc '.[] | select(.has_description) | select(.description != "") | [.modmask, .key, .description] | @tsv' |
while IFS=$'\t' read -r mask key desc; do
if (( mask )); then
mods="$(mods_from_mask "$mask")"
printf '%s + %s\t%s\n' "$mods" "$key" "$desc"
else
printf '%s\t%s\n' "$key" "$desc"
fi
done |
sort -k1,1 |
column -t -s $'\t' |
wofi --dmenu --prompt "Hyprland keybinds" >/dev/null
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
# macOS-style SUPER+Down: close the hyprshell workspace overview if it is open,
# returning to the current workspace without switching.
#
# hyprshell's overview is a gtk4-layer-shell surface whose namespace is
# "hyprshell_overview", and the OpenOverview IPC message toggles the overview
# closed when it is already open (see OverviewRootInput::OpenOverview).
set -u
if ! hyprctl layers -j | jq -e '
.. | objects | .namespace? // empty | select(. == "hyprshell_overview")
' >/dev/null; then
exit 0
fi
hyprshell socat '"OpenOverview"'
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# Print the current weather as a short icon+temp string, e.g. "🌦️ +83°F".
# Caches the wttr.in response for CACHE_MAX_AGE seconds so the lock screen and
# waybar don't hit the network on every refresh. Override the location with
# WEATHER_CITY or edit CITY below.
set -u
CITY="${WEATHER_CITY:-Denver}"
CACHE_DIR="${XDG_RUNTIME_DIR:-/tmp}"
CACHE="${CACHE_DIR}/wttr.cache"
CACHE_MAX_AGE=1800 # 30 minutes
URL="https://wttr.in/${CITY}?format=%c+%t"
cache_age() {
[ -f "${CACHE}" ] || return 1
now=$(date +%s)
mtime=$(stat -c %Y "${CACHE}")
[ "$(( now - mtime ))" -lt "${CACHE_MAX_AGE}" ]
}
if cache_age; then
cat "${CACHE}"
exit 0
fi
TMP="${CACHE}.tmp.$$"
if curl -fsS --max-time 10 "${URL}" > "${TMP}" 2>/dev/null; then
mv "${TMP}" "${CACHE}"
cat "${CACHE}"
else
rm -f "${TMP}" || true
# Offline: fall back to whatever we cached last, even if stale.
[ -f "${CACHE}" ] && cat "${CACHE}"
fi
+42 -3
View File
@@ -7,14 +7,17 @@ $font = JetBrainsMono Nerd Font
# General # General
general { general {
disable_loading_bar = false
hide_cursor = false hide_cursor = false
} }
# Authentication # Authentication
# Fingerprint is disabled: GDM's login worker (pam/gdm-fingerprint) holds the
# device for the whole session, so hyprlock can never claim it -- the failed
# claim blocked the auth thread and made unlocks appear stuck. Fingerprint is
# still available at the GDM login screen; hyprlock is password-only.
auth { auth {
fingerprint { fingerprint {
enabled = true enabled = false
ready_message = Scan fingerprint to unlock ready_message = Scan fingerprint to unlock
present_message = Scanning... present_message = Scanning...
retry_delay = 250 # in milliseconds retry_delay = 250 # in milliseconds
@@ -45,7 +48,7 @@ label {
# Date # Date
label { label {
monitor = monitor =
text = cmd[update:43200000] echo "$(date + "%A, %d %B %Y")" text = cmd[update:43200000] echo "$(date +'%A, %d %B %Y')"
color = $text color = $text
font_size = 25 font_size = 25
font_family = $font font_family = $font
@@ -54,6 +57,30 @@ label {
valign = top valign = top
} }
# Battery
label {
monitor =
text = cmd[update:60000] $HOME/.local/bin/hyprlock-battery.sh
color = $subtext1
font_size = 20
font_family = $font
position = -30, -190
halign = right
valign = top
}
# Weather
label {
monitor =
text = cmd[update:600000] $HOME/.local/bin/weather.sh
color = $subtext1
font_size = 20
font_family = $font
position = -30, -215
halign = right
valign = top
}
# User Avatar # User Avatar
image { image {
monitor = monitor =
@@ -66,6 +93,18 @@ image {
valign = center valign = center
} }
# Notifications (latest mako notification; empty when none)
label {
monitor =
text = cmd[update:5000] $HOME/.local/bin/hyprlock-notifications.sh
color = $subtext0
font_size = 16
font_family = $font
position = 0, 60
halign = center
valign = bottom
}
# Input Field # Input Field
input-field { input-field {
monitor = monitor =
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Guard wrapper for hyprlock (0.9.x): upstream has crashes on DPMS-off / monitor
# power transitions while the lock screen is running (hyprlock#991, #953, #695).
# If the real hyprlock dies, this restarts it so the session doesn't end up in a
# "lockscreen app died" state. Requires misc:allow_session_lock_restore = true
# in Hyprland config for the replacement lock to be accepted.
set -u
REAL_HYPRLOCK=/usr/bin/hyprlock
MAX_RESTARTS=20
restart=0
while true; do
"$REAL_HYPRLOCK" "$@"
code=$?
# 0: exited cleanly (e.g. unlocked) -- done.
# 1: auth failed, retry -- let the loop back off and try again.
if [ "$code" -eq 0 ]; then
exit 0
fi
restart=$((restart + 1))
if [ "$restart" -ge "$MAX_RESTARTS" ]; then
exit "$code"
fi
# Brief pause so a crash loop doesn't burn CPU (fade back in quickly).
sleep 0.5
done
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# Battery status for the hyprlock screen, e.g. " 18%".
# Uses the first battery found under /sys/class/power_supply (BAT0/BAT1, ...);
# prints nothing on desktops without a battery.
set -u
bat=""
for b in /sys/class/power_supply/BAT*; do
if [ -r "${b}/capacity" ]; then
bat="${b}"
break
fi
done
[ -z "${bat}" ] && exit 0
cap="$(cat "${bat}/capacity" 2>/dev/null || echo 0)"
status="$(cat "${bat}/status" 2>/dev/null || echo Unknown)"
case "${status}" in
Charging|Full)
icon=""
;;
*)
if [ "${cap}" -ge 80 ]; then
icon=""
elif [ "${cap}" -ge 60 ]; then
icon=""
elif [ "${cap}" -ge 40 ]; then
icon=""
elif [ "${cap}" -ge 20 ]; then
icon=""
else
icon=""
fi
;;
esac
printf '%s %s%%' "${icon}" "${cap}"
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Latest mako notification for the hyprlock screen, Pango-escaped, e.g.
# "Battery test: Battery at 18%". Prints nothing when there are no
# notifications, so the label renders empty.
set -u
makoctl list -j 2>/dev/null | jq -r '
if length == 0 then
empty
else
.[-1] as $n |
(if $n.app_name != null and $n.app_name != "" then $n.app_name + ": " else "" end) +
($n.summary // "") +
(if $n.body != null and $n.body != "" then " — " + $n.body[0:80] else "" end)
end' 2>/dev/null | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g'
+10 -8
View File
@@ -1,6 +1,6 @@
// Edit with `hyprshell config edit` // Edit with `hyprshell config edit`
( (
version: 3, version: 4,
windows: ( windows: (
scale: 8.5, scale: 8.5,
items_per_row: 5, items_per_row: 5,
@@ -43,7 +43,6 @@
actions: ( actions: (
actions: [ actions: [
lock_screen, lock_screen,
hibernate,
logout, logout,
reboot, reboot,
shutdown, shutdown,
@@ -69,11 +68,13 @@
), ),
), ),
), ),
key: "Tab", key: "Up",
modifier: "alt", top_offset: 430,
filter_by: [], modifier: "super",
hide_filtered: false, filter_by: [
exclude_special_workspaces: "", current_monitor,
],
exclude_workspaces: "special:.*",
), ),
switch: ( switch: (
modifier: "alt", modifier: "alt",
@@ -82,7 +83,8 @@
current_monitor, current_monitor,
], ],
switch_workspaces: false, switch_workspaces: false,
exclude_special_workspaces: "", exclude_workspaces: "",
kill_key: 'q',
), ),
switch_2: None, switch_2: None,
), ),
+279
View File
@@ -0,0 +1,279 @@
{
"name": "desk",
"monitors": [
{
"name": "eDP-2",
"description": "BOE 0x0BC9",
"make": "BOE",
"model": "0x0BC9",
"serial": "",
"width": 2560,
"height": 1600,
"refresh_rate": 165.0,
"resolution_mode": "explicit",
"available_modes": [
"2560x1600@165.00Hz",
"2560x1600@60.00Hz",
"1920x1200@165.00Hz",
"1920x1080@165.00Hz",
"1600x1200@165.00Hz",
"1680x1050@165.00Hz",
"1280x1024@165.00Hz",
"1440x900@165.00Hz",
"1280x800@165.00Hz",
"1280x720@165.00Hz",
"1024x768@165.00Hz",
"800x600@165.00Hz",
"640x480@165.00Hz"
],
"x": 0,
"y": 0,
"position_mode": "explicit",
"scale": 1,
"scale_mode": "explicit",
"transform": 0,
"mirror_of": "",
"bitdepth": 8,
"vrr": 0,
"color_management": "srgb",
"icc_profile": "",
"sdr_brightness": 1,
"sdr_saturation": 1,
"sdr_eotf": 0,
"supports_hdr": 0,
"supports_wide_color": 0,
"sdr_min_luminance": 0.2,
"sdr_max_luminance": 80,
"min_luminance": 0.0,
"max_luminance": 0.0,
"max_avg_luminance": 0.0,
"reserved_top": 0,
"reserved_bottom": 0,
"reserved_left": 0,
"reserved_right": 0,
"enabled": true,
"focus_at_startup": false
},
{
"name": "DP-7",
"description": "Dell Inc. DELL U2515HX GMDK46C4053L",
"make": "Dell Inc.",
"model": "DELL U2515HX",
"serial": "GMDK46C4053L",
"width": 2560,
"height": 1440,
"refresh_rate": 59.951,
"resolution_mode": "explicit",
"available_modes": [
"2560x1440@59.95Hz",
"2048x1152@60.00Hz",
"1920x1200@59.88Hz",
"1920x1080@60.00Hz",
"1920x1080@60.00Hz",
"1920x1080@59.94Hz",
"1920x1080@50.00Hz",
"1920x1080@30.00Hz",
"1920x1080@29.97Hz",
"1920x1080@25.00Hz",
"1920x1080@24.00Hz",
"1920x1080@23.98Hz",
"1600x1200@60.00Hz",
"1680x1050@59.95Hz",
"1280x1024@75.03Hz",
"1280x1024@60.02Hz",
"1200x960@59.99Hz",
"1152x864@75.00Hz",
"1280x720@60.00Hz",
"1280x720@60.00Hz",
"1280x720@59.94Hz",
"1280x720@50.00Hz",
"1024x768@75.03Hz",
"1024x768@60.00Hz",
"800x600@75.00Hz",
"800x600@60.32Hz",
"720x576@50.00Hz",
"720x480@60.00Hz",
"720x480@60.00Hz",
"720x480@59.94Hz",
"720x480@59.94Hz",
"720x480@59.94Hz",
"640x480@75.00Hz",
"640x480@60.00Hz",
"640x480@59.94Hz",
"640x480@59.94Hz",
"720x400@70.08Hz"
],
"x": 5488,
"y": 345,
"position_mode": "explicit",
"scale": 1,
"scale_mode": "explicit",
"transform": 0,
"mirror_of": "",
"bitdepth": 8,
"vrr": 0,
"color_management": "srgb",
"icc_profile": "",
"sdr_brightness": 1,
"sdr_saturation": 1,
"sdr_eotf": 0,
"supports_hdr": 0,
"supports_wide_color": 0,
"sdr_min_luminance": 0.2,
"sdr_max_luminance": 80,
"min_luminance": 0.0,
"max_luminance": 0.0,
"max_avg_luminance": 0.0,
"reserved_top": 0,
"reserved_bottom": 0,
"reserved_left": 0,
"reserved_right": 0,
"enabled": true,
"focus_at_startup": false
},
{
"name": "DP-10",
"description": "Dell Inc. DELL U2518D 0WG2J9C5B3WL",
"make": "Dell Inc.",
"model": "DELL U2518D",
"serial": "0WG2J9C5B3WL",
"width": 2560,
"height": 1440,
"refresh_rate": 59.951,
"resolution_mode": "explicit",
"available_modes": [
"2560x1440@59.95Hz",
"2048x1080@60.00Hz",
"2048x1080@24.00Hz",
"1920x1080@60.00Hz",
"1920x1080@60.00Hz",
"1920x1080@59.94Hz",
"1920x1080@50.00Hz",
"1600x1200@60.00Hz",
"1280x1024@75.03Hz",
"1280x1024@60.02Hz",
"1152x864@75.00Hz",
"1280x720@60.00Hz",
"1280x720@60.00Hz",
"1280x720@59.94Hz",
"1280x720@50.00Hz",
"1024x768@75.03Hz",
"1024x768@60.00Hz",
"800x600@75.00Hz",
"800x600@60.32Hz",
"720x576@50.00Hz",
"720x576@50.00Hz",
"720x480@60.00Hz",
"720x480@60.00Hz",
"720x480@59.94Hz",
"720x480@59.94Hz",
"640x480@75.00Hz",
"640x480@60.00Hz",
"640x480@59.94Hz",
"640x480@59.94Hz",
"720x400@70.08Hz"
],
"x": 2928,
"y": 345,
"position_mode": "explicit",
"scale": 1,
"scale_mode": "explicit",
"transform": 0,
"mirror_of": "",
"bitdepth": 8,
"vrr": 0,
"color_management": "srgb",
"icc_profile": "",
"sdr_brightness": 1,
"sdr_saturation": 1,
"sdr_eotf": 0,
"supports_hdr": 0,
"supports_wide_color": 0,
"sdr_min_luminance": 0.2,
"sdr_max_luminance": 80,
"min_luminance": 0.0,
"max_luminance": 0.0,
"max_avg_luminance": 0.0,
"reserved_top": 0,
"reserved_bottom": 0,
"reserved_left": 0,
"reserved_right": 0,
"enabled": true,
"focus_at_startup": false
},
{
"name": "DP-4",
"description": "Viewteck Co. Ltd. GNV34DB",
"make": "Viewteck Co., Ltd.",
"model": "GNV34DB",
"serial": "",
"width": 3440,
"height": 1440,
"refresh_rate": 50.0,
"resolution_mode": "explicit",
"available_modes": [
"3440x1440@30.00Hz",
"3440x1440@50.00Hz",
"3440x1440@50.00Hz",
"2560x1080@60.00Hz",
"2560x1080@59.94Hz",
"1720x1440@60.00Hz",
"1920x1080@60.00Hz",
"1920x1080@60.00Hz",
"1920x1080@59.94Hz",
"1920x1080@50.00Hz",
"1680x1050@59.88Hz",
"1600x900@60.00Hz",
"1280x1024@75.03Hz",
"1280x1024@60.02Hz",
"1440x900@59.90Hz",
"1280x720@60.00Hz",
"1280x720@60.00Hz",
"1280x720@59.94Hz",
"1280x720@50.00Hz",
"1024x768@75.03Hz",
"1024x768@60.00Hz",
"800x600@75.00Hz",
"800x600@60.32Hz",
"720x480@60.00Hz",
"720x480@60.00Hz",
"720x480@59.94Hz",
"720x480@59.94Hz",
"640x480@75.00Hz",
"640x480@60.00Hz",
"640x480@59.94Hz",
"640x480@59.94Hz",
"720x400@70.08Hz"
],
"x": 3795,
"y": 1785,
"position_mode": "explicit",
"scale": 1,
"scale_mode": "explicit",
"transform": 0,
"mirror_of": "",
"bitdepth": 8,
"vrr": 0,
"color_management": "srgb",
"icc_profile": "",
"sdr_brightness": 1,
"sdr_saturation": 1,
"sdr_eotf": 0,
"supports_hdr": 0,
"supports_wide_color": 0,
"sdr_min_luminance": 0.2,
"sdr_max_luminance": 80,
"min_luminance": 0.0,
"max_luminance": 0.0,
"max_avg_luminance": 0.0,
"reserved_top": 0,
"reserved_bottom": 0,
"reserved_left": 0,
"reserved_right": 0,
"enabled": true,
"focus_at_startup": false
}
],
"workspace_rules": [],
"last_applied_time": 1788832961.6364207
}
@@ -0,0 +1,60 @@
{
"name": "laptop",
"monitors": [
{
"name": "eDP-2",
"description": "BOE 0x0BC9",
"make": "BOE",
"model": "0x0BC9",
"serial": "",
"width": 2560,
"height": 1600,
"refresh_rate": 165.0,
"resolution_mode": "explicit",
"available_modes": [
"2560x1600@165.00Hz",
"2560x1600@60.00Hz",
"1920x1200@165.00Hz",
"1920x1080@165.00Hz",
"1600x1200@165.00Hz",
"1680x1050@165.00Hz",
"1280x1024@165.00Hz",
"1440x900@165.00Hz",
"1280x800@165.00Hz",
"1280x720@165.00Hz",
"1024x768@165.00Hz",
"800x600@165.00Hz",
"640x480@165.00Hz"
],
"x": 0,
"y": 0,
"position_mode": "explicit",
"scale": 1,
"scale_mode": "explicit",
"transform": 0,
"mirror_of": "",
"bitdepth": 8,
"vrr": 0,
"color_management": "srgb",
"icc_profile": "",
"sdr_brightness": 1,
"sdr_saturation": 1,
"sdr_eotf": 0,
"supports_hdr": 0,
"supports_wide_color": 0,
"sdr_min_luminance": 0.2,
"sdr_max_luminance": 80,
"min_luminance": 0.0,
"max_luminance": 0.0,
"max_avg_luminance": 0.0,
"reserved_top": 0,
"reserved_bottom": 0,
"reserved_left": 0,
"reserved_right": 0,
"enabled": true,
"focus_at_startup": false
}
],
"workspace_rules": [],
"last_applied_time": 1788832965.6726744
}
+2 -1
View File
@@ -1,5 +1,6 @@
{ {
"clamshell_mode": true, "clamshell_mode": true,
"update_sddm": true, "update_sddm": true,
"migrate_workspaces": true "migrate_workspaces": true,
"active_profile": "laptop"
} }
@@ -19,8 +19,8 @@ StartLimitBurst=5
[Service] [Service]
PassEnvironment=DISPLAY WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS XDG_RUNTIME_DIR PassEnvironment=DISPLAY WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS XDG_RUNTIME_DIR
Environment="OP_BIOMETRIC_UNLOCK_ENABLED=true" Environment="OP_BIOMETRIC_UNLOCK_ENABLED=true"
Environment="CF_ACCESS_CLIENT_ID=op://Homelab/K8s API Service Token/Service Token/client-id" Environment="TUNNEL_SERVICE_TOKEN_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" Environment="TUNNEL_SERVICE_TOKEN_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 ExecStart=/usr/bin/op run -- /usr/bin/cloudflared access tcp --hostname k8s.samoneal.io --url localhost:6443
Restart=on-failure Restart=on-failure
RestartSec=60 RestartSec=60
+11 -4
View File
@@ -2,7 +2,7 @@
"position": "top", "position": "top",
"modules-left": ["hyprland/workspaces", "mpris"], "modules-left": ["hyprland/workspaces", "mpris"],
"modules-center": ["hyprland/window"], "modules-center": ["hyprland/window"],
"modules-right": ["cpu", "memory", "tray", "bluetooth", "network", "pulseaudio", "battery", "clock", "power-profiles-daemon", "custom/power"], "modules-right": ["cpu", "memory", "tray", "bluetooth", "network", "pulseaudio", "battery", "custom/weather", "clock", "power-profiles-daemon", "custom/power"],
"clock": { "clock": {
"format": "<span foreground='#f5c2e7'> </span>{:%a %d %H:%M}", "format": "<span foreground='#f5c2e7'> </span>{:%a %d %H:%M}",
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>" "tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>"
@@ -28,6 +28,12 @@
"memory": { "memory": {
"on-click": "~/.local/bin/btop.sh" "on-click": "~/.local/bin/btop.sh"
}, },
"custom/weather": {
"format": "{}",
"exec": "~/.local/bin/weather.sh",
"interval": 600,
"tooltip": false
},
"tray": { "tray": {
"icon-size": 16, "icon-size": 16,
"icon-theme": "Papirus-Dark", "icon-theme": "Papirus-Dark",
@@ -83,10 +89,11 @@
"menu": "on-click", "menu": "on-click",
"menu-file": "~/.config/waybar/power_menu.xml", "menu-file": "~/.config/waybar/power_menu.xml",
"menu-actions": { "menu-actions": {
"shutdown": "shutdown",
"reboot": "reboot",
"suspend": "systemctl suspend", "suspend": "systemctl suspend",
"hibernate": "systemctl hibernate", "lock": "hyprlock",
"logout": "hyprctl dispatch exit",
"reboot": "reboot",
"shutdown": "shutdown",
}, },
"escape": true "escape": true
}, },
+11 -6
View File
@@ -7,21 +7,26 @@
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="hibernate"> <object class="GtkMenuItem" id="lock">
<property name="label">Hibernate</property> <property name="label">Lock</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="shutdown"> <object class="GtkMenuItem" id="logout">
<property name="label">Shutdown</property> <property name="label">Logout</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="reboot">
<property name="label">Restart</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkSeparatorMenuItem" id="delimiter1" /> <object class="GtkSeparatorMenuItem" id="delimiter1" />
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="reboot"> <object class="GtkMenuItem" id="shutdown">
<property name="label">Reboot</property> <property name="label">Shutdown</property>
</object> </object>
</child> </child>
</object> </object>
+59
View File
@@ -139,3 +139,62 @@ alias pip=pip3
. "/home/sammieo/.deno/env" . "/home/sammieo/.deno/env"
export LC_ALL="en_US.UTF-8" export LC_ALL="en_US.UTF-8"
# Make sure no alias hides the real mv / cp
unalias mv 2>/dev/null
unalias cp 2>/dev/null
# Custom 'move' command that incorporates the pv command
mv() {
if (( $# > 2 )); then
command mv "$@"
return
fi
local src=$1 dest=$2
if [ -d "$src" ]; then
mkdir -p "$dest"
# Total size in bytes (du -sb)
local total=$(du -sb "$src" | awk '{print $1}')
# Pipe through pv and extract at destination
tar -cf - -C "$(dirname "$src")" "$(basename "$src")" |
pv -s "$total" |
tar -xf - -C "$dest"
# Remove the original directory
rm -rf "$src"
else
mkdir -p "$(dirname "$dest")"
command mv "$@"
fi
}
# Custom 'cp' command that incorporates the pv command
cp() {
if (( $# > 2 )); then
command cp "$@"
return
fi
local src=$1 dst=$2
if [ -d "$src" ]; then
mkdir -p "$dst"
# ---- 1️⃣ Total size of the directory ---------------------------------
local total=$(du -sb "$src" | awk '{print $1}')
# ---- 2️⃣ Stream the dir through pv into the destination ---------------
tar -cf - -C "$(dirname "$src")" "$(basename "$src")" |
pv -s "$total" |
tar -xf - -C "$dst"
else
# ---- 3️⃣ Single file -------------------------------------------------
mkdir -p "$(dirname "$dst")"
local fsize=$(stat -c%s "$src" 2>/dev/null || wc -c <"$src")
pv -s "$fsize" < "$src" > "$dst"
fi
}