SoDa7 .MOD - SCHW

⬇️ Download the config and sound files HERE!

This guide shows you how to recreate our custom Omarchy Linux without overwriting upstream Omarchy scripts. The key idea is: wrap + patch at runtime, and keep your custom logic in your own files.

Im going to explain what to create and where to put it — and inside this blog post you’ll see code blocks for each script/config. You can copy/paste them, tweak a few variables (NAS IP, mount points, VPN service name, etc.), and you’re done.

What you’ll end up with:

Update-proof Omarchy menu patcher (adds a Custom submenu at runtime)

VPN control (PIA + PiVPN) + optional Waybar modules

NAS smart mount (LAN vs remote)

Safe Customization Status screen (won’t hang even if NFS is dead)

Screensaver mode toggles (stock vs long hypridle)

Windows startup/shutdown sound themes + sound-on-power actions

GParted fix for Wayland (pkexec env)

Amiga fonts + Alacritty font selection

Smart Fastfetch MOTD (logo only when it fits)

A few QoL Hyprland binds (cheatsheet launcher + emoji picker)

Optional PIA autoconnect systemd user service

Before you start: dependencies + assumptions

You’re on Omarchy / Hyprland / Wayland.

Recommended installs (names may vary by distro repo, but Arch/Omarchy users know the drill):

jq (required for the “smart MOTD” that checks workspace windows)

fastfetch

mpv (or ffplay / pw-play / paplay for audio playback)

gparted + polkit (for pkexec)

waybar (optional, only if you want the status modules)

emote (optional emoji picker)

Important: Every file below that is a script must be executable. You can chmod +x each script as you create it, but I’ll also give you one “do it all at once” command near the end.

VPN assumptions:

PIA CLI exists at /opt/piavpn/bin/piactl (or you’ll change the path)

PiVPN uses an OpenVPN client systemd unit (example: openvpn-client@pivpn)

NAS assumptions:

You mount NFS exports into /mnt/…

You know your NAS IP and export layout

📕 Part 1 — The “Update-Proof” Omarchy Menu Wrapper (the core concept)

Why this matters

Omarchy updates can overwrite upstream scripts. If you directly edit Omarchy’s menu, your changes can get nuked.

So instead, we do:

Don’t change Omarchy’s keybind (SUPER+ALT+SPACE still runs omarchy-menu)

Create a wrapper that “wins” in PATH

Wrapper runs a patcher script that:

reads upstream menu

patches a temp copy in /tmp

executes patched menu

falls back to stock if patch fails

Step 1: Add PATH precedence (so your wrapper wins)

Open/create:

~/.config/uwsm/env

Add (or ensure) PATH is ordered like this:

export OMARCHY_PATH=$HOME/.local/share/omarchy export PATH=$HOME/.local/bin:$OMARCHY_PATH/bin:$PATH

In this post, I’ll show the full file:

~/.config/uwsm/env:

# ~/.config/uwsm/env
#
# This file is sourced by UWSM (your session/environment manager) and sets
# environment variables used by Hyprland/Wayland apps.
#
# What this does (for your Omarchy customizations):
# - Ensures Omarchy’s binaries are available in PATH for menu helpers like:
#     omarchy-menu, omarchy-launch-*, omarchy-cmd-*
# - Ensures ~/.local/bin is also in PATH (where your wrapper scripts live)
#
# Other settings:
# - Sets cursor theme + size
# - Sources UWSM defaults (terminal/editor)
# - Optionally activates `mise` if Omarchy’s present-command wrapper says it exists

# Changes require a relaunch of Hyprland to take effect.

# Ensure Omarchy bins are in the path
export OMARCHY_PATH=$HOME/.local/share/omarchy
export PATH=$HOME/.local/bin:$OMARCHY_PATH/bin:$PATH
export XCURSOR_THEME=Bibata-Modern-Ice
export XCURSOR_SIZE=24

# Set default terminal and editor
source ~/.config/uwsm/default

# Activate mise if present on the system
omarchy-cmd-present mise && eval "$(mise activate bash)"

✅ After changing this file: relaunch Hyprland/UWSM so PATH updates apply.

Step 2: Create the omarchy-menu wrapper

Create:

~/.local/bin/omarchy-menu

This file should simply exec your patcher:

~/.local/bin/omarchy-menu:

#!/usr/bin/env bash
# ~/.local/bin/omarchy-menu
#
# What this does:
# - This is a tiny wrapper that ensures when you run `omarchy-menu`,
#   it launches *your* customized Omarchy menu (the patched one),
#   instead of the stock/upstream menu.
#
# Why it matters:
# - Your Waybar config (and likely keybinds) call `omarchy-menu`.
# - Without this wrapper in PATH, users would keep launching the stock menu.

exec "$HOME/.config/omarchy/bin/omarchy-menu-nas.sh" "$@"

Make it executable:

chmod +x ~/.local/bin/omarchy-menu

Step 3: Create the patcher script that injects your submenu

Create:

~/.config/omarchy/bin/omarchy-menu-nas.sh

This is the heart of the setup — it patches upstream omarchy-menu at runtime.

~/.config/omarchy/bin/omarchy-menu-nas.sh:

#!/usr/bin/env bash
# omarchy-menu-nas.sh
#
# What this does (high level):
# - Reads the upstream Omarchy menu script (the one shipped by Omarchy)
# - Patches it *in memory* to:
#     1) Add a new "Custom" entry to the main menu
#     2) Add routing so selecting Custom opens a new custom submenu
#     3) Inject show_custom_menu() (NAS mounts, VPN toggles, cheatsheets, etc.)
#     4) Wrap shutdown/reboot calls so your shutdown-sound wrapper runs first
# - Writes the patched result to a temp file and executes it
#
# Why this approach is nice:
# - You do NOT modify Omarchy’s upstream script permanently
# - If Omarchy updates the menu, you can re-run this and re-patch on the fly
#
# SECURITY / PUBLISH NOTES:
# - This script contains *no passwords, tokens, or IP addresses*.
# - It DOES reveal some local paths (PIA path, Nautilus, your $HOME layout).
#   That’s generally fine to publish. If you want it more generic, you *can*
#   mention in your blog that those paths may vary per system.

set -euo pipefail

# Upstream Omarchy menu script we will patch.
UPSTREAM="$HOME/.local/share/omarchy/bin/omarchy-menu"

# Temporary file where we write the patched copy of the upstream menu.
PATCHED="$(mktemp /tmp/omarchy-menu.patched.XXXXXX)"

# Desktop notification helper (silent fallback if notify-send isn't installed).
notify() {
  command -v notify-send >/dev/null 2>&1 && notify-send "Omarchy Custom Menu" "$1" || true
}

# Hard fail if upstream menu cannot be read.
if [[ ! -r "$UPSTREAM" ]]; then
  notify "Can't read upstream menu: $UPSTREAM"
  exit 1
fi

# Patch the upstream menu using an embedded Python script.
# Arguments passed to Python:
#   $1 = upstream menu path
#   $2 = output path (PATCHED)
python3 - <<'PY' "$UPSTREAM" "$PATCHED"
import sys, re

src_path, out_path = sys.argv[1], sys.argv[2]
s = open(src_path, "r", encoding="utf-8").read()

# The label inserted into the Omarchy main menu.
# Note: requires Nerd Font support for the icon glyph to render nicely.
CUSTOM_LABEL = "󰠱  Custom"

# Wrapper script used to play shutdown sound before power actions.
ACTION = '"$HOME/.config/omarchy/bin/action-with-shutdown-sound.sh"'

# Menu action that lets you pick startup/shutdown sounds.
SOUND_PICKER = '"$HOME/.config/omarchy/bin/change-startup-shutdown-sounds.sh"'

# -----------------------------
# 1) Add Custom to main menu list (just above System)
# -----------------------------
# This regex finds the options text passed to the upstream "menu" call inside
# show_main_menu(). We then splice in CUSTOM_LABEL before "System".
main_menu_pat = r'(show_main_menu\(\)\s*\{\s*\n\s*go_to_menu "\$\(\s*menu "Go" "\s*)([^"]+)("\s*\)\s*"\s*\n\s*\}\s*)'
m = re.search(main_menu_pat, s, re.S)

# If upstream changed and the regex can't find what we expect:
# - write the original unmodified script out
# - exit cleanly (your wrapper will still run, but without custom menu)
if not m:
  open(out_path, "w", encoding="utf-8").write(s)
  sys.exit(0)

options = m.group(2)

# Only insert if Custom is not already present.
if ("Custom" not in options) and (CUSTOM_LABEL not in options):
  items = options.split("\\n")

  # Defensive cleanup: remove any existing Custom lines to avoid duplicates.
  items = [x for x in items if ("Custom" not in x and x != CUSTOM_LABEL)]

  # Find the first "System" entry and insert Custom just above it.
  sys_idx = None
  for i, it in enumerate(items):
    if "System" in it:
      sys_idx = i
      break

  # If "System" wasn't found, append Custom at the end.
  if sys_idx is None:
    items.append(CUSTOM_LABEL)
  else:
    items.insert(sys_idx, CUSTOM_LABEL)

  options = "\\n".join(items)

# Splice the updated options back into the upstream script text.
s = s[:m.start(2)] + options + s[m.end(2):]

# -----------------------------
# 2) Route in go_to_menu()
# -----------------------------
# Upstream routes menu selections like:
#   *trigger*) show_trigger_menu ;;
# We add:
#   *custom*) show_custom_menu ;;
if "show_custom_menu" not in s:
  if "*trigger*) show_trigger_menu ;;" in s:
    s = s.replace(
      "*trigger*) show_trigger_menu ;;",
      "*trigger*) show_trigger_menu ;;\n  *custom*) show_custom_menu ;;"
    )

# -----------------------------
# 3) Inject show_custom_menu()
# -----------------------------
# We locate "show_trigger_menu() {" and insert our custom menu function right
# after that function ends (after the next "}\n\n").
if "show_custom_menu()" not in s:
  inject_after = "show_trigger_menu() {"
  idx = s.find(inject_after)
  if idx != -1:
    insert_point = s.find("}\n\n", idx)
    if insert_point != -1:
      # This is the actual custom menu definition inserted into upstream.
      # It depends on upstream helper functions:
      # - menu
      # - present_terminal
      # - show_main_menu
      custom_fn = r'''

show_custom_menu() {
  case $(menu "Custom" "🗄  Mount NAS (smart)\n🧹  Unmount NAS\n📁  Open /mnt (choose mount)\n──────── VPN ────────\n🛡  PIA Connect\n🚫  PIA Disconnect\n🔒  PiVPN Connect\n🔓  PiVPN Disconnect\n────── Cheatsheets ──────\n📄  Show Binds (TXT)\n🖼  Show Binds (PDF)\n────── Screensaver ──────\n󰖨  Screensaver Stock Mode\n󰖨  Screensaver Long Mode\n────── Sounds ──────\n🎵  Change Startup/Shutdown Sounds\n──────── Status ────────\nℹ  Customization Status") in

    *"Mount NAS"*)
      present_terminal "$HOME/.config/omarchy/bin/nas-mount-smart.sh"
      ;;

    *"Unmount NAS"*)
      present_terminal "$HOME/.config/omarchy/bin/nas-unmount-all.sh"
      ;;

    *"Open /mnt"*)
      # Open file browser to /mnt (where mounts typically live).
      setsid nautilus /mnt >/dev/null 2>&1 &
      disown
      ;;

    *"PIA Connect"*)
      # Connect PIA VPN via piactl and show state, then wait for a keypress.
      present_terminal "bash -lc '/opt/piavpn/bin/piactl connect || true; /opt/piavpn/bin/piactl get connectionstate || true; echo; read -n 1 -r -s -p \"Press any key…\"'"
      ;;

    *"PIA Disconnect"*)
      present_terminal "bash -lc '/opt/piavpn/bin/piactl disconnect || true; /opt/piavpn/bin/piactl get connectionstate || true; echo; read -n 1 -r -s -p \"Press any key…\"'"
      ;;

    *"PiVPN Connect"*)
      # Your own local scripts (outside Omarchy).
      present_terminal "$HOME/.local/bin/pivpn-connect.sh"
      ;;

    *"PiVPN Disconnect"*)
      present_terminal "$HOME/.local/bin/pivpn-disconnect.sh"
      ;;

    *"Show Binds (TXT)"*)
      present_terminal "$HOME/.config/omarchy/bin/show-cheatsheet.sh --all"
      ;;

    *"Show Binds (PDF)"*)
      # PDF version runs in background (no terminal needed).
      bash -lc "$HOME/.config/omarchy/bin/show-cheatsheet-pdf.sh --all" >/dev/null 2>&1 &
      ;;

    *"Screensaver Stock Mode"*)
      present_terminal "$HOME/.config/omarchy/bin/screensaver-stock-mode.sh"
      ;;

    *"Screensaver Long Mode"*)
      present_terminal "$HOME/.config/omarchy/bin/screensaver-long-mode.sh"
      ;;

    *"Change Startup/Shutdown Sounds"*)
      # Launch sound picker script in a terminal.
      present_terminal ''' + SOUND_PICKER + r'''
      ;;

    *"Customization Status"*)
      present_terminal "$HOME/.config/omarchy/bin/vpn-nas-status.sh"
      ;;

    *"────"*|*"────────"*)
      # If user selects a divider line, just re-open the custom menu.
      show_custom_menu
      ;;

    *)
      # Anything else returns to the main menu.
      show_main_menu
      ;;
  esac
}
'''
      # Insert our function into upstream script text.
      s = s[:insert_point+3] + custom_fn + s[insert_point+3:]

# -----------------------------
# 4) Wrap Omarchy System menu shutdown/reboot tokens
# -----------------------------
# Omarchy uses tokens like "omarchy-cmd-shutdown" (not always literal commands).
# We replace those tokens so they go through your ACTION wrapper first.
def wrap_token(token: str, replacement: str):
  global s
  # Negative lookbehind tries to avoid double-wrapping.
  pat = rf'(?<!{re.escape("action-with-shutdown-sound.sh")} )\b{re.escape(token)}\b'
  s = re.sub(pat, replacement, s)

wrap_token("omarchy-cmd-shutdown", f"{ACTION} shutdown")
wrap_token("omarchy-cmd-reboot",   f"{ACTION} reboot")

# -----------------------------
# 5) Safety net: wrap any systemctl poweroff/reboot/halt
# -----------------------------
# If upstream ever directly calls systemctl poweroff/reboot/halt,
# we wrap it too: ACTION -- systemctl ...
def wrap_cmd(pattern, repl):
  global s
  s = re.sub(pattern, repl, s, flags=re.M)

wrap_cmd(
  rf'(^|[;&\(\)\n]\s*)(?!{re.escape(ACTION)}\s+--\s+)((?:/usr/bin/)?systemctl\b[^\n;&\)]*\b(?:poweroff|reboot|halt)\b[^\n;&\)]*)',
  rf'\1{ACTION} -- \2'
)

# Marker to confirm patch success (used by the bash wrapper below).
if "OMARCHY_CUSTOM_PATCH_MARKER" not in s and "show_custom_menu()" in s:
  s += "\n# OMARCHY_CUSTOM_PATCH_MARKER\n"

# Write patched script out.
open(out_path, "w", encoding="utf-8").write(s)
PY

# Ensure patched menu script is executable and then run it.
chmod +x "$PATCHED"

# Notify user whether our marker exists (basic success check).
if grep -q "OMARCHY_CUSTOM_PATCH_MARKER" "$PATCHED"; then
  notify "Custom menu patch applied ✅"
else
  notify "Patch didn’t apply (upstream changed?) — running stock menu."
fi

# Execute patched menu, passing through any args
exec bash "$PATCHED" "$@"

Make it executable:

chmod +x ~/.config/omarchy/bin/omarchy-menu-nas.sh

How to test:

Hit SUPER + ALT + SPACE

Confirm you see your Custom submenu

If upstream changes later and patching fails: it should fall back safely and notify you.

📗Part 2 — VPN Setup (PIA + PiVPN) — do this before NAS

Why here?

Because your NAS smart mount may rely on PiVPN when you’re remote, and your dotfiles backup workflow may temporarily disconnect PIA.

Step 1: PiVPN connect/disconnect scripts

Create:

~/.local/bin/pivpn-connect.sh

~/.local/bin/pivpn-disconnect.sh

Paste them:

~/.local/bin/pivpn-connect.sh:

#!/usr/bin/env bash
# pivpn-connect.sh
#
# What this does:
# - Connects your PiVPN OpenVPN client via systemd:
#     openvpn-client@pivpn
# - Coordinates with PIA VPN (if installed) to avoid conflicts:
#     - If PIA is Connected/Connecting, it disconnects PIA first
#     - It writes a small state flag so the disconnect script can later restore PIA
#
# Key behavior:
# - Writes /tmp/pivpn-pia-was-connected when it *actually* had to disconnect PIA
# - If PiVPN is already active, it exits cleanly

set -euo pipefail

# OpenVPN systemd unit for PiVPN
SERVICE="openvpn-client@pivpn"

# PIA CLI path (used to disconnect PIA before bringing PiVPN up)
PIACTL="/opt/piavpn/bin/piactl"

# State flag file used to remember whether PIA was connected before PiVPN connect
STATE_FILE="/tmp/pivpn-pia-was-connected"

log() {
  # Only print if stdout is a terminal
  if [[ -t 1 ]]; then
    echo "$@"
  fi
}

log "[PiVPN] Connecting…"

# 1) Handle PIA state
# If PIA is connected (or connecting), disconnect it first and remember that fact.
if [[ -x "$PIACTL" ]]; then
  state="$("$PIACTL" get connectionstate 2>/dev/null || true)"
  if [[ "$state" == "Connected" || "$state" == "Connecting" ]]; then
    log "[PiVPN] PIA is $state – disconnecting it first…"
    echo "1" > "$STATE_FILE"          # <-- real file write, no log()
    "$PIACTL" disconnect || true
    sleep 2
  else
    rm -f "$STATE_FILE"
  fi
else
  # If piactl isn't installed/executable, ensure we don't leave stale state behind
  rm -f "$STATE_FILE"
fi

# 2) If PiVPN already active, bail
if systemctl is-active --quiet "$SERVICE"; then
  log "[PiVPN] $SERVICE is already active."
  exit 0
fi

# 3) Start via systemd (polkit will handle auth if needed)
log "[PiVPN] Starting $SERVICE via systemd…"
if ! systemctl start "$SERVICE"; then
  log "[PiVPN] ERROR: failed to start $SERVICE."
  exit 1
fi

log "[PiVPN] Started."

~/.local/bin/pivpn-disconnect.sh:

#!/usr/bin/env bash
# pivpn-disconnect.sh
#
# What this does:
# - Disconnects your PiVPN OpenVPN client via systemd:
#     openvpn-client@pivpn
# - Optionally restores PIA VPN if it was connected before you enabled PiVPN:
#     - pivpn-connect.sh writes /tmp/pivpn-pia-was-connected when it had to
#       disconnect PIA to bring PiVPN up
#     - If that state file exists here, this script reconnects PIA after
#       stopping PiVPN, then removes the state file

set -euo pipefail

# OpenVPN systemd unit for PiVPN
SERVICE="openvpn-client@pivpn"

# PIA CLI path (used to restore PIA after PiVPN is stopped)
PIACTL="/opt/piavpn/bin/piactl"

# State flag file created by pivpn-connect.sh if it disconnected PIA
STATE_FILE="/tmp/pivpn-pia-was-connected"

log() {
  # Only print if stdout is a terminal
  if [[ -t 1 ]]; then
    echo "$@"
  fi
}

log "[PiVPN] Disconnecting…"

# 1) Stop PiVPN via systemd (polkit will handle auth if needed)
if systemctl is-active --quiet "$SERVICE"; then
  if ! systemctl stop "$SERVICE"; then
    log "[PiVPN] ERROR: failed to stop $SERVICE."
    exit 1
  fi
else
  log "[PiVPN] $SERVICE is not active."
fi

log "[PiVPN] Disconnected."

# 2) If PIA was previously connected, reconnect it now
if [[ -x "$PIACTL" && -f "$STATE_FILE" ]]; then
  log "[PiVPN] PIA was connected before PiVPN. Reconnecting PIA…"
  "$PIACTL" connect || true
  rm -f "$STATE_FILE"
fi

Make executable:

chmod +x ~/.local/bin/pivpn-connect.sh ~/.local/bin/pivpn-disconnect.sh

Step 2: Optional PIA autoconnect user service

Create:

~/.config/systemd/user/pia-autoconnect.service

~/.config/systemd/user/pia-autoconnect.service:

[Unit]
Description=PIA headless autoconnect
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/opt/piavpn/bin/piactl background enable
ExecStart=/opt/piavpn/bin/piactl set protocol wireguard
ExecStart=/opt/piavpn/bin/piactl set region us-seattle
ExecStart=/opt/piavpn/bin/piactl set requestportforward false
ExecStart=/opt/piavpn/bin/piactl connect
RemainAfterExit=yes

[Install]
WantedBy=default.target

Enable:

systemctl –user daemon-reload systemctl –user enable –now pia-autoconnect.service systemctl –user status pia-autoconnect.service

📘Part 3 — NAS Smart Mount (LAN vs Remote)

Create:

~/.config/omarchy/bin/nas-mount-smart.sh

~/.config/omarchy/bin/nas-mount-smart.sh:

#!/usr/bin/env bash
# nas-mount-smart.sh
#
# What this does:
# - Attempts to mount multiple NFS shares from a NAS to local /mnt mountpoints
# - Tries the "local/LAN path" first
# - If mounts fail, it falls back to a PiVPN connection and retries

set -euo pipefail

echo "=== nas-mount-smart: $(date) ==="

# PRIVATE: NAS IP on your LAN (obfuscated for publishing)
NAS_IP="NAS_IP_HERE"

# PRIVATE: NAS export base path (obfuscated for publishing)
EXPORT_BASE="/mnt/NAS_EXPORT_BASE"

# Mounts to create
# PRIVATE: share names + mountpoints obfuscated for publishing
MOUNTS=(
  "share_1:/mnt/mount_point_1"
  "share_2:/mnt/mount_point_2"
  "share_3:/mnt/mount_point_3"
  "share_4:/mnt/mount_point_4"
)

# Scripts you already have
PIVPN_CONNECT="$HOME/.local/bin/pivpn-connect.sh"

# ---- helpers -------------------------------------------------------------

log(){ echo "[nas] $*"; }

have(){ command -v "$1" >/dev/null 2>&1; }

is_mounted() {
  # SAFE mount check (won’t hang on dead NFS)
  local target="$1"
  awk -v t="$target" '$5==t {found=1} END{exit(found?0:1)}' /proc/self/mountinfo
}

nas_port_open() {
  # Don’t rely only on ping; test NFS port quickly.
  # 2049 is NFSv4. This doesn’t “touch” the mountpoints.
  if have nc; then
    nc -z -w1 "$NAS_IP" 2049 >/dev/null 2>&1
  else
    timeout 1 bash -lc "cat < /dev/null > /dev/tcp/$NAS_IP/2049" >/dev/null 2>&1
  fi
}

nas_reachable() {
  # Use ping OR NFS port. Ping might be blocked; port check is better.
  ping -c 1 -W 1 "$NAS_IP" >/dev/null 2>&1 || nas_port_open
}

warm_sudo() {
  log "Warming sudo (may prompt once)..."
  sudo -v
}

mount_one() {
  local share="$1" target="$2"
  mkdir -p "$target"

  if is_mounted "$target"; then
    log "Already mounted: $target"
    return 0
  fi

  local src="${NAS_IP}:${EXPORT_BASE}/${share}"
  log "Mounting: $src -> $target"

  # IMPORTANT: timeout so we never hang forever if routing is broken
  # - Use a longer timeout if your network is slow.
  if timeout 8 sudo mount "$src" "$target"; then
    return 0
  else
    log "Mount failed (or timed out) for $target"
    return 1
  fi
}

mount_all() {
  local ok=0 total=0
  for item in "${MOUNTS[@]}"; do
    total=$((total+1))
    share="${item%%:*}"
    target="${item##*:}"
    if mount_one "$share" "$target"; then
      ok=$((ok+1))
    fi
  done
  log "Mounted $ok / $total mount(s)"
  [[ "$ok" -eq "$total" ]]
}

wait_for_nas() {
  local tries=20
  local i
  for i in $(seq 1 "$tries"); do
    if nas_reachable; then
      log "NAS is reachable ✅"
      return 0
    fi
    log "Waiting for NAS ($NAS_IP) to become reachable... ($i/$tries)"
    sleep 1
  done
  return 1
}

# ---- main ---------------------------------------------------------------

# 1) Try LOCAL mount path FIRST — no VPN changes
if nas_reachable; then
  log "NAS reachable (local path) -> attempting mount..."
else
  log "NAS not reachable right now -> still attempting a quick mount with timeouts..."
fi

warm_sudo

if mount_all; then
  notify-send "NAS" "Mounted ✅"
  exit 0
fi

# 2) If local mount failed, fall back to PiVPN path
log "Local mount failed -> falling back to PiVPN path..."
log "Connecting PiVPN..."
"$PIVPN_CONNECT" || true

log "Waiting for openvpn-client@pivpn to be active..."
# give systemd a moment even if connect script returns quickly
for _ in $(seq 1 15); do
  systemctl is-active --quiet openvpn-client@pivpn && break
  sleep 1
done

if ! systemctl is-active --quiet openvpn-client@pivpn; then
  log "ERROR: PiVPN did not become active."
  notify-send "NAS" "PiVPN failed ❌"
  exit 1
fi

if ! wait_for_nas; then
  log "ERROR: NAS still not reachable over PiVPN (routes/NAS IP/VPN config)"
  notify-send "NAS" "NAS unreachable over PiVPN ❌"
  exit 1
fi

log "Attempting mount after PiVPN is ready..."
if mount_all; then
  notify-send "NAS" "Mounted (PiVPN) ✅"
  exit 0
fi

notify-send "NAS" "Mount failed ❌"
exit 1

Create:

~/.config/omarchy/bin/nas-unmount-all.sh

~/.config/omarchy/bin/nas-unmount-all.sh:

#!/bin/bash
# nas-unmount-all.sh
#
# What this does:
# - Unmounts a list of NAS mountpoints (if they are currently mounted)
# - Uses mountpoint(1) to check safely before unmounting
# - Uses sudo for umount (will prompt if sudo isn't warmed)
#
# Publish note:
# - Mount paths are obfuscated for privacy. Replace with your real mountpoints.

set -euo pipefail

# PRIVATE: mount paths obfuscated for publishing
MOUNTS=(/mnt/mount_point_1 /mnt/mount_point_2 /mnt/mount_point_3 /mnt/mount_point_4)

for m in "${MOUNTS[@]}"; do
  # If it's a mountpoint, unmount it; otherwise do nothing.
  # "|| true" prevents a failure from killing the entire script.
  mountpoint -q "$m" && sudo umount "$m" || true
done

notify-send "NAS" "Unmount done ✅"

Make executable:

chmod +x ~/.config/omarchy/bin/nas-mount-smart.sh ~/.config/omarchy/bin/nas-unmount-all.sh

You will likely customize:

NAS IP

export paths

mountpoints list

📕Part 3.5 — Dotfiles Backup to NAS (rsync + tar.gz, VPN-aware)

This is my “save my whole setup” button.

It backs up all of ~/.config to my NAS in two ways:

  1. Rsync mirror (fast, incremental, easy to browse)
  2. Timestamped tar.gz archive (one-file snapshot you can store forever)

It’s also VPN-aware:

  • Tries to mount the NAS locally first
  • If it’s not reachable, it brings up PiVPN and retries
  • If PIA is currently connected, it can temporarily disconnect it so LAN/NAS routes work
  • When it’s done, it unmounts NAS mounts and restores PIA (if it was on originally)

You MUST customize these before running it

Open the script and edit:

  • NAS_MOUNT (the “main” NAS mount you check for)
  • NAS_DOTFILES_DIR (where backups should live on the NAS)
  • NAS_MOUNTS (all mountpoints you want auto-unmounted at the end)
  • MOUNT_SCRIPT (your NFS mounting helper — replace with your own if needed)
  • PIVPN_SERVICE and PIVPN_CONNECT (your PiVPN unit/script names)
  • PIACTL path (if yours isn’t /opt/piavpn/bin/piactl)
  • rsync --exclude list (optional)

Create the script

Create:

  • ~/dotfiles_backup.sh

~/dotfiles_backup.sh:

#!/bin/bash
# dotfiles_backup.sh
#
# What this does (high level):
# - Backs up your ~/.config directory to a NAS location using rsync
# - Also creates a timestamped tar.gz archive of ~/.config on the NAS
# - Tries a "local mount" first; if not mounted, it can bring up PiVPN and retry
# - Coordinates with PIA VPN so routing doesn’t block NAS access:
#     - If PIA was connected at the start, it disconnects it before mounting/backup
#     - After backup + unmount, it restores PIA if it was originally connected
#
# What it depends on:
# - Your NAS mount script (MOUNT_SCRIPT) that mounts all required NFS shares
# - PiVPN connect script (pivpn-connect.sh) + systemd OpenVPN unit
# - Optional: PIA CLI (piactl) for VPN coordination
# - rsync, tar, mountpoint, sudo, systemctl
#
# Publish note / privacy:
# - PRIVATE: NAS paths, mountpoints, and backup directory names are obfuscated.
# - The exclude list (BraveSoftware/, Element/, mise/) is not sensitive.
# - No passwords or tokens are stored here.

set -euo pipefail

echo "=== dotfiles-backup: starting ==="

# --- Paths / services ---

# PRIVATE: NAS mountpoint (obfuscated for publishing)
NAS_MOUNT="/mnt/NAS_MOUNT_HERE"

# PRIVATE: backup directory on the NAS (obfuscated for publishing)
NAS_DOTFILES_DIR="$NAS_MOUNT/backups/NAS_DOTFILES_BACKUP_DIR"

# Source directory to back up
SRC_CONFIG="$HOME/.config"

# All NAS mounts your mount_nfs.sh creates
# PRIVATE: mountpoints obfuscated for publishing
NAS_MOUNTS=(
  "/mnt/MOUNT_POINT_1"
  "/mnt/MOUNT_POINT_2"
  "/mnt/MOUNT_POINT_3"
  "/mnt/MOUNT_POINT_4"
)

# PRIVATE: mount script path/name (obfuscated for publishing)
MOUNT_SCRIPT="$HOME/MOUNT_SCRIPT_HERE.sh"

PIVPN_SERVICE="openvpn-client@pivpn"
PIVPN_CONNECT="$HOME/.local/bin/pivpn-connect.sh"

PIACTL="/opt/piavpn/bin/piactl"

# Remember original PIA state so we can restore it at the end
INITIAL_PIA_STATE="Unknown"
if [[ -x "$PIACTL" ]]; then
  INITIAL_PIA_STATE="$("$PIACTL" get connectionstate 2>/dev/null || echo "Unknown")"
  echo "[dotfiles-backup] Initial PIA state: $INITIAL_PIA_STATE"
else
  echo "[dotfiles-backup] PIA client not found at $PIACTL – skipping PIA handling."
fi

# --- Helper functions ---

try_mount() {
  echo "[dotfiles-backup] Attempting to mount NAS via $MOUNT_SCRIPT..."

  if [[ -x "$MOUNT_SCRIPT" ]]; then
    "$MOUNT_SCRIPT"
  else
    echo "[dotfiles-backup] ERROR: $MOUNT_SCRIPT not found or not executable." >&2
    return 1
  fi
}

ensure_pivpn() {
  if systemctl is-active --quiet "$PIVPN_SERVICE"; then
    echo "[dotfiles-backup] PiVPN is already active (service $PIVPN_SERVICE)."
    return 0
  fi

  echo "[dotfiles-backup] PiVPN is not active. Trying to bring it up with $PIVPN_CONNECT..."

  if [[ -x "$PIVPN_CONNECT" ]]; then
    # This may pop your polkit auth window (fingerprint/password), which is expected.
    "$PIVPN_CONNECT" >/dev/null 2>&1 || {
      echo "[dotfiles-backup] WARNING: pivpn-connect.sh returned a non-zero status." >&2
      return 1
    }
    echo "[dotfiles-backup] PiVPN connect script was invoked."
  else
    echo "[dotfiles-backup] ERROR: $PIVPN_CONNECT not found or not executable; cannot use PiVPN." >&2
    return 1
  fi
}

ensure_pia_disconnected() {
  # If PIA tools aren't present, nothing to do
  if [[ ! -x "$PIACTL" ]]; then
    return 0
  fi

  # Only disconnect if it *was* connected initially
  if [[ "$INITIAL_PIA_STATE" == "Connected" || "$INITIAL_PIA_STATE" == "Connecting" ]]; then
    echo "[dotfiles-backup] PIA was $INITIAL_PIA_STATE at start – disconnecting it so NAS is reachable..."
    "$PIACTL" disconnect || true
    sleep 3
  else
    echo "[dotfiles-backup] PIA was not connected at start – leaving it alone."
  fi
}

cleanup_nas_mounts() {
  echo "[dotfiles-backup] Cleaning up NAS mounts before finishing..."

  for m in "${NAS_MOUNTS[@]}"; do
    if mountpoint -q "$m"; then
      echo "  - Unmounting $m ..."
      # This may trigger a polkit auth prompt if sudo is needed
      sudo umount "$m" || echo "    ! Failed to unmount $m (you may need to check it manually)."
    fi
  done

  echo "[dotfiles-backup] NAS mount cleanup done."
}

restore_pia_if_needed() {
  if [[ ! -x "$PIACTL" ]]; then
    echo "[dotfiles-backup] PIA client not installed – nothing to restore."
    return
  fi

  if [[ "$INITIAL_PIA_STATE" == "Connected" || "$INITIAL_PIA_STATE" == "Connecting" ]]; then
    echo "[dotfiles-backup] Restoring PIA to its original state (connecting)..."
    "$PIACTL" connect || true
  else
    echo "[dotfiles-backup] PIA was not connected originally – not reconnecting."
  fi
}

fail_and_exit() {
  local msg="$1"
  echo "[dotfiles-backup] ERROR: $msg" >&2
  echo "=== dotfiles-backup: FAILED ==="
  # Try to clean up mounts if they exist
  cleanup_nas_mounts
  # Restore PIA if it was originally on
  restore_pia_if_needed
  exit 1
}

# --- 0) Handle PIA first so NAS is reachable ---
echo "[dotfiles-backup] Checking PIA VPN state..."
ensure_pia_disconnected

# --- 1) Check if NAS mount is available / mount it ---

echo "[dotfiles-backup] Checking if NAS mount ($NAS_MOUNT) is available..."

# Try local mount first (no VPN)
if ! mountpoint -q "$NAS_MOUNT"; then
  echo "[dotfiles-backup] $NAS_MOUNT is not mounted."
  echo "[dotfiles-backup] First, trying a normal local mount (no VPN)..."
  try_mount || echo "[dotfiles-backup] Local mount attempt failed or script returned non-zero."
fi

# If still not mounted, try via PiVPN
if ! mountpoint -q "$NAS_MOUNT"; then
  echo "[dotfiles-backup] NAS still not mounted. Assuming we might be remote."
  echo "[dotfiles-backup] Trying to bring up PiVPN tunnel and mount over it..."

  if ensure_pivpn; then
    echo "[dotfiles-backup] PiVPN should now be active. Retrying NAS mount..."
    try_mount || echo "[dotfiles-backup] Mount attempt over PiVPN returned non-zero."
  else
    fail_and_exit "Could not start PiVPN; cannot reach NAS remotely."
  fi
fi

# Final check
if ! mountpoint -q "$NAS_MOUNT"; then
  fail_and_exit "$NAS_MOUNT is still not mounted after local + PiVPN attempts. Is the NAS offline or PiVPN unreachable?"
fi

echo "[dotfiles-backup] NAS is mounted at $NAS_MOUNT ✅"

# --- 1.5) Verify NAS backup directory exists ---

echo "[dotfiles-backup] Verifying that $NAS_DOTFILES_DIR exists on the NAS..."

if [[ ! -d "$NAS_DOTFILES_DIR" ]]; then
  fail_and_exit "Expected NAS backup directory $NAS_DOTFILES_DIR does not exist. Create it and try again."
fi

echo "[dotfiles-backup] Found existing NAS backup directory: $NAS_DOTFILES_DIR ✅"

# --- 2) Rsync dotfiles to NAS ---

DEST_CONFIG="$NAS_DOTFILES_DIR/.config"
mkdir -p "$DEST_CONFIG"
echo "[dotfiles-backup] Preparing destination: $DEST_CONFIG"

echo "[dotfiles-backup] Rsyncing ~/.config -> $DEST_CONFIG"
echo "[dotfiles-backup] Excluding: BraveSoftware/, Element/, mise/"

rsync -aAHv \
  --exclude='BraveSoftware/' \
  --exclude='Element/' \
  --exclude='mise/' \
  "$SRC_CONFIG/" \
  "$DEST_CONFIG/"

echo "[dotfiles-backup] Rsync complete ✅"

# --- 3) Create tarball on NAS ---

TAR_PATH="$NAS_DOTFILES_DIR/dotfiles-config-$(date +%Y%m%d-%H%M%S).tar.gz"

echo "[dotfiles-backup] Creating tarball at:"
echo "  $TAR_PATH"
echo "[dotfiles-backup] (Excluding BraveSoftware, Element, mise)"

tar czf "$TAR_PATH" \
  --exclude='.config/BraveSoftware' \
  --exclude='.config/Element' \
  --exclude='.config/mise' \
  -C "$HOME" .config

echo "[dotfiles-backup] Tarball created ✅"

# --- 4) Unmount NAS mounts before PIA comes back ---

cleanup_nas_mounts

# --- 5) Restore PIA if needed ---

restore_pia_if_needed

echo "[dotfiles-backup] All done."
echo "[dotfiles-backup] Directory backup: $DEST_CONFIG"
echo "[dotfiles-backup] Archive backup:   $TAR_PATH"
echo "=== dotfiles-backup: finished successfully ==="

Make it executable:

chmod +x ~/dotfiles_backup.sh

Run it whenever you want to backup your dotfiles/config - this script uses my NAS mountpoints, and will connect to my homelab’s PiVPN if I’m remote, but you could modify it any way(s) you need.

📙Part 4 — Cheatsheets generated from your REAL Hyprland binds

Create:

~/.config/omarchy/bin/hypr-cheatgen.py

~/.config/omarchy/bin/hypr-cheatgen.py:

#!/usr/bin/env python3
# hypr-cheatgen.py
#
# What this does:
# - Pulls your current Hyprland keybinds via:
#     hyprctl binds -j
# - Converts Hyprland's bind JSON into a readable cheat sheet:
#     - ASCII (aligned, fits ~80 columns by default)
#     - Markdown table
# - Optionally renders the ASCII cheat sheet to a landscape PDF (via ReportLab)
#
# Why this is useful:
# - Your Hyprland config can be complex; this generates a "living" cheatsheet
#   from the binds Hypr is actually using right now.
#
# Dependencies / assumptions:
# - Requires `hyprctl` in PATH and Hyprland running
# - For PDF output: requires ReportLab (python-reportlab package on Arch)

import argparse
import json
import subprocess
import textwrap
from collections import defaultdict

# Hyprland modmask bits (from your output):
# 64=SUPER, 8=ALT, 4=CTRL, 1=SHIFT
MOD_BITS = [
    (64, "SUPER"),
    (8,  "ALT"),
    (4,  "CTRL"),
    (1,  "SHIFT"),
]

# Optional: common X11 keycode mapping (works on typical US layouts)
# If your hyprctl JSON includes "keycode", this makes numbers readable.
X11_KEYCODE_MAP = {
    10: "1", 11: "2", 12: "3", 13: "4", 14: "5", 15: "6", 16: "7", 17: "8", 18: "9", 19: "0",
    24: "Q", 25: "W", 26: "E", 27: "R", 28: "T", 29: "Y", 30: "U", 31: "I", 32: "O", 33: "P",
    38: "A", 39: "S", 40: "D", 41: "F", 42: "G", 43: "H", 44: "J", 45: "K", 46: "L",
    52: "Z", 53: "X", 54: "C", 55: "V", 56: "B", 57: "N", 58: "M",
    65: "SPACE", 36: "RETURN", 22: "BACKSPACE", 9: "ESCAPE", 23: "TAB",
    107: "PRINT", 119: "DELETE",
    # Add more if you want
}

def run(cmd: list[str]) -> str:
    # Run a command and return stdout as text
    return subprocess.check_output(cmd, text=True)

def decode_modmask(modmask) -> str:
    """
    Hypr returns either:
      - 'mod' as a string (sometimes)
      - or 'modmask' as a number (what you're seeing)
    We decode numeric modmasks into "SUPER + SHIFT + ..." style strings.
    """
    try:
        m = int(modmask)
    except Exception:
        return str(modmask).strip()

    names = [name for bit, name in MOD_BITS if (m & bit)]
    return " + ".join(names)

def key_from_bind(b: dict) -> str:
    """
    Hypr versions differ; try a few fields.
    Prefer printable key names, fall back to keycode/code when needed.
    """
    k = b.get("key")
    if k and str(k).strip():
        return str(k).strip()

    # Some builds include keycode
    kc = b.get("keycode")
    if kc is not None:
        try:
            kc_i = int(kc)
            return X11_KEYCODE_MAP.get(kc_i, f"code:{kc_i}")
        except Exception:
            pass

    # Fallback: sometimes "code" exists
    code = b.get("code")
    if code is not None:
        return f"code:{code}"

    return ""  # truly unknown

def friendly_action(b: dict) -> str:
    # Dispatcher + arg combined into a single friendly string
    disp = (b.get("dispatcher") or "").strip()
    arg  = (b.get("arg") or "").strip()
    return f"{disp} {arg}".strip()

def combo_string(b: dict) -> str:
    # Build a human-readable key combo string like:
    #   SUPER + SHIFT + Q
    mods = b.get("mod")
    if not mods:
        mods = b.get("modmask", "")
    mods_s = decode_modmask(mods) if mods != "" else ""
    key = key_from_bind(b)

    if mods_s and key:
        return f"{mods_s} + {key}"
    if mods_s and not key:
        return mods_s
    return key

def to_ascii_sections(binds: list[dict], width: int = 80) -> str:
    """
    Produce a strict, aligned, <=width ASCII cheat sheet:
      [SECTION]
      KEYS... (fixed column) | ACTION... (wrapped)
    """
    groups = defaultdict(list)
    for b in binds:
        disp = (b.get("dispatcher") or "unknown").strip()
        groups[disp].append((combo_string(b), friendly_action(b)))

    out = []
    for disp in sorted(groups.keys()):
        out.append(f"[{disp.upper()}]")
        rows = sorted(groups[disp], key=lambda x: (x[0], x[1]))

        # Tuned for 80 columns:
        # - key_w: fixed width for the keys column
        # - act_w: remaining width for action wrapping
        key_w = 26
        act_w = max(10, width - (key_w + 3))  # " | "

        for keys, action in rows:
            keys = (keys or "").strip()
            action = (action or "").strip()

            keys_lines = textwrap.wrap(keys, width=key_w) or [""]
            act_lines  = textwrap.wrap(action, width=act_w) or [""]

            n = max(len(keys_lines), len(act_lines))
            keys_lines += [""] * (n - len(keys_lines))
            act_lines  += [""] * (n - len(act_lines))

            for i in range(n):
                out.append(f"{keys_lines[i]:<{key_w}} | {act_lines[i]}")

        out.append("")  # blank line between sections

    return "\n".join(out).rstrip() + "\n"

def to_markdown(binds: list[dict]) -> str:
    # Markdown output groups binds by dispatcher and renders a table per section.
    groups = defaultdict(list)
    for b in binds:
        disp = (b.get("dispatcher") or "unknown").strip()
        groups[disp].append((combo_string(b), friendly_action(b)))

    md = ["# Hyprland Keybinds Cheat Sheet\n"]
    for disp in sorted(groups.keys()):
        md.append(f"## {disp}\n")
        md.append("| Keys | Action |")
        md.append("|---|---|")
        for keys, action in sorted(groups[disp], key=lambda x: (x[0], x[1])):
            md.append(f"| {keys} | {action} |")
        md.append("")
    return "\n".join(md)

def write_pdf_ascii(content: str, pdf_path: str, *, font_size: int = 10, line_h: int = 11):
    """
    Render ASCII content to a landscape PDF without LaTeX (ReportLab).
    Larger font looks nicer, but may push content onto more pages. That's OK.
    """
    try:
        import reportlab  # noqa: F401
    except ModuleNotFoundError:
        raise SystemExit("PDF generation needs ReportLab. Install: sudo pacman -S python-reportlab")

    from reportlab.pdfgen import canvas
    from reportlab.lib.pagesizes import letter, landscape
    from reportlab.lib.units import inch
    from reportlab.pdfbase import pdfmetrics
    from reportlab.pdfbase.ttfonts import TTFont

    # Monospace font for perfect alignment
    font = "Courier"
    try:
        pdfmetrics.registerFont(TTFont("DejaVuMono", "/usr/share/fonts/TTF/DejaVuSansMono.ttf"))
        font = "DejaVuMono"
    except Exception:
        # Courier fallback is fine
        pass

    pagesize = landscape(letter)
    c = canvas.Canvas(pdf_path, pagesize=pagesize)
    w, h = pagesize

    left = 0.5 * inch
    right = 0.5 * inch
    top = h - 0.5 * inch
    bottom = 0.5 * inch

    # If you want even larger text, bump font_size to 11 and line_h to 12.
    c.setFont(font, font_size)

    y = top
    for line in content.splitlines():
        if y < bottom:
            c.showPage()
            c.setFont(font, font_size)
            y = top
        # Avoid drawing outside page width (ReportLab won't wrap automatically)
        c.drawString(left, y, line)
        y -= line_h

    c.save()

def main():
    # CLI flags:
    # --format ascii|md
    # --width 80 (for ascii formatting)
    # --out <file> (otherwise prints to stdout)
    # --pdf <path> (ASCII only; uses reportlab)
    ap = argparse.ArgumentParser()
    ap.add_argument("--format", choices=["ascii", "md"], default="ascii")
    ap.add_argument("--width", type=int, default=80)
    ap.add_argument("--out", default=None, help="Write to a file instead of stdout")
    ap.add_argument("--pdf", default=None, help="Also generate a PDF to this path (ASCII only; uses reportlab)")
    ap.add_argument("--pdf-font-size", type=int, default=10, help="PDF font size (default 10)")
    ap.add_argument("--pdf-line-height", type=int, default=11, help="PDF line height (default 11)")
    args = ap.parse_args()

    # Pull binds from hyprctl as JSON
    binds = json.loads(run(["hyprctl", "binds", "-j"]))

    # Render desired output format
    if args.format == "ascii":
        content = to_ascii_sections(binds, width=args.width)
    else:
        content = to_markdown(binds)

    # Write to a file or stdout
    if args.out:
        with open(args.out, "w", encoding="utf-8") as f:
            f.write(content)
    else:
        print(content, end="")

    # Optional PDF generation (best with ASCII output)
    if args.pdf:
        if args.format != "ascii":
            raise SystemExit("--pdf is intended for --format ascii (so alignment stays perfect).")
        write_pdf_ascii(
            content,
            args.pdf,
            font_size=args.pdf_font_size,
            line_h=args.pdf_line_height,
        )

if __name__ == "__main__":
    main()

Create:

~/.config/omarchy/bin/show-cheatsheet.sh

~/.config/omarchy/bin/show-cheatsheet.sh:

#!/bin/bash
# show-cheatsheet.sh
#
# What this does:
# - Ensures your Hyprland keybind cheat sheet files exist in:
#     ~/.config/omarchy/cheats/
# - Regenerates the ASCII cheat sheet (fast) every time this runs
# - If called with --all, also generates:
#     - Markdown version
#     - PDF version (bigger font for readability)
# - Opens the ASCII cheat sheet in Alacritty using `less -SR`:
#     - -S: don't wrap long lines (keeps columns aligned)
#     - -R: allow raw control characters (color/formatting if ever used)
#
# Dependencies / assumptions:
# - hypr-cheatgen.py exists and is executable
# - alacritty exists at /usr/bin/alacritty
# - hyprctl is available (hypr-cheatgen.py calls it)

set -euo pipefail

# Ensure Hypr can find your scripts even if it doesn't inherit your shell PATH
# (common with graphical launchers / compositor environments).
export PATH="$HOME/.config/omarchy/bin:$HOME/.local/bin:/usr/local/bin:/usr/bin:$PATH"

# Output directory + files
CHEAT_DIR="$HOME/.config/omarchy/cheats"
TXT="$CHEAT_DIR/hypr-binds.txt"
MD="$CHEAT_DIR/hypr-binds.md"
PDF="$CHEAT_DIR/hypr-binds.pdf"

# Cheat sheet generator (the Python script you wrote)
CHEATGEN="$HOME/.config/omarchy/bin/hypr-cheatgen.py"

# Terminal used to display the cheat sheet
TERM_BIN="/usr/bin/alacritty"

# Ensure output directory exists
mkdir -p "$CHEAT_DIR"

# --- sanity checks with visible feedback ---------------------------------

# Ensure generator exists and is executable
if [[ ! -x "$CHEATGEN" ]]; then
  notify-send "Cheat sheet" "hypr-cheatgen.py not executable: $CHEATGEN"
  exit 1
fi

# Ensure terminal exists
if [[ ! -x "$TERM_BIN" ]]; then
  notify-send "Cheat sheet" "Alacritty not found at $TERM_BIN"
  exit 1
fi

# --- generate cheat sheets -----------------------------------------------

# Always regenerate TXT (fast)
"$CHEATGEN" --format ascii --width 80 --out "$TXT"

# Optional: regenerate MD + PDF when asked
if [[ "${1:-}" == "--all" ]]; then
  "$CHEATGEN" --format md --out "$MD"
  "$CHEATGEN" --format ascii --width 80 --out "$TXT" \
    --pdf "$PDF" --pdf-font-size 15 --pdf-line-height 17
fi

# --- display --------------------------------------------------------------

# Open in terminal with less (no wrap, keep alignment)
# exec "$TERM_BIN" --title "Hypr Cheat Sheet" -e bash -lc "less -SR '$TXT'"
exec "$TERM_BIN" --class CheatSheet -e bash -lc "less -SR '$TXT'"

Create:

~/.config/omarchy/bin/show-cheatsheet-pdf.sh

~/.config/omarchy/bin/show-cheatsheet-pdf.sh:

#!/bin/bash
# show-cheatsheet-pdf.sh
#
# What this does:
# - Regenerates your Hyprland keybind cheat sheet as:
#     - ASCII text (for alignment/debugging)
#     - PDF (for easy reading / fullscreen viewing)
# - Then opens the PDF in a viewer:
#     - Prefers zathura if installed
#     - Falls back to evince
#     - Otherwise uses xdg-open (system default)
#
# Notes:
# - You mentioned making the PDF viewer fullscreen via window rules
#   (e.g., Hyprland window rules for Zathura/Evince).
#
# Dependencies / assumptions:
# - hypr-cheatgen.py exists and can run (calls hyprctl)
# - ReportLab must be installed for PDF generation

set -euo pipefail

# Ensure Hypr can find your scripts even if it doesn't inherit your shell PATH
export PATH="$HOME/.config/omarchy/bin:$HOME/.local/bin:/usr/local/bin:/usr/bin:$PATH"

# Output directory + files
CHEAT_DIR="$HOME/.config/omarchy/cheats"
TXT="$CHEAT_DIR/hypr-binds.txt"
PDF="$CHEAT_DIR/hypr-binds.pdf"

# Cheat sheet generator
CHEATGEN="$HOME/.config/omarchy/bin/hypr-cheatgen.py"

# Ensure output directory exists
mkdir -p "$CHEAT_DIR"

# Regenerate (always current)
"$CHEATGEN" --format ascii --width 80 --out "$TXT" \
  --pdf "$PDF" --pdf-font-size 15 --pdf-line-height 17

# Open viewer (we'll make it fullscreen via window rules)
if command -v zathura >/dev/null 2>&1; then
  exec zathura "$PDF"
elif command -v evince >/dev/null 2>&1; then
  exec evince "$PDF"
else
  exec xdg-open "$PDF"
fi

Make executable:

chmod +x ~/.config/omarchy/bin/hypr-cheatgen.py
~/.config/omarchy/bin/show-cheatsheet.sh
~/.config/omarchy/bin/show-cheatsheet-pdf.sh

It generates files into:

~/.config/omarchy/cheats/

Create the ~/.config/omarchy/cheats directory.

📕Part 5 — “Customization Status” screen (safe even if NFS is dead)

Create:

~/.config/omarchy/bin/vpn-nas-status.sh

~/.config/omarchy/bin/vpn-nas-status.sh:

#!/usr/bin/env bash
# vpn-nas-status.sh
#
# What this does:
# - Shows a quick "Customization Status" dashboard in the terminal:
#     - Current sound theme + whether startup/shutdown MP3 files exist
#     - Current screensaver mode (stock vs long)
#     - PIA VPN connection state (via piactl)
#     - PiVPN service active/inactive (systemd)
#     - NAS reachability (ping)
#     - Whether key mountpoints are mounted (using /proc/self/mountinfo — safe even if NFS is dead)
#
# Publish note:
# - NAS IP and mountpoint paths are obfuscated for privacy.

set -euo pipefail

# PIA's CLI (path may vary per system; not a secret)
PIACTL="/opt/piavpn/bin/piactl"

# PiVPN systemd service unit name (not a secret)
PIVPN_SERVICE="openvpn-client@pivpn"

# PRIVATE: NAS IP on your LAN (obfuscated for publishing)
NAS_IP="NAS_IP_HERE"

# PRIVATE: mount paths obfuscated for publishing
MOUNTS=(
  "/mnt/mount_point_1"
  "/mnt/mount_point_2"
  "/mnt/mount_point_3"
  "/mnt/mount_point_4"
)

# Omarchy customization state + sound assets
SOUND_DIR="$HOME/.config/omarchy/sounds"
STATE_DIR="$HOME/.config/omarchy/state"
MODE_FILE="$STATE_DIR/screensaver_mode"
CURRENT_SOUND_FILE="$STATE_DIR/current_sound"
STARTUP_MP3="$SOUND_DIR/startup.mp3"
SHUTDOWN_MP3="$SOUND_DIR/shutdown.mp3"

# Allow Ctrl+C to exit cleanly without an ugly stack of errors
trap 'echo; echo "[status] interrupted"; exit 0' INT TERM

is_mounted_mountinfo() {
  # Check mounted status by reading /proc/self/mountinfo
  # (safe even if a network mount is unresponsive)
  local target="$1"
  awk -v t="$target" '$5==t {found=1} END{exit(found?0:1)}' /proc/self/mountinfo
}

mounted_source_mountinfo() {
  # If mounted, extract filesystem type + source from /proc/self/mountinfo
  # Output example: "nfs4 10.0.0.118:/mnt/pool/share"
  local target="$1"
  awk -v t="$target" '
    $5==t {
      for (i=1; i<=NF; i++) if ($i=="-") {dash=i; break}
      fstype=$(dash+1); source=$(dash+2);
      printf("%s %s", fstype, source);
      exit 0
    }
    END { exit 1 }
  ' /proc/self/mountinfo
}

current_sound() {
  # Stored by your sound picker script (if set)
  if [[ -f "$CURRENT_SOUND_FILE" ]]; then
    head -n 1 "$CURRENT_SOUND_FILE" | tr -d '\r'
  else
    echo "Unknown (not set)"
  fi
}

screensaver_mode() {
  # Stored by your screensaver mode scripts (if set)
  if [[ -f "$MODE_FILE" ]]; then
    m="$(head -n 1 "$MODE_FILE" | tr -d '\r')"
    case "$m" in
      stock) echo "Stock" ;;
      long)  echo "Long" ;;
      *)     echo "Unknown ($m)" ;;
    esac
  else
    echo "Unknown (not set)"
  fi
}

echo "=== CUSTOMIZATION STATUS ==="
echo

echo "Sounds:"
echo "  Theme: $(current_sound)"
echo "  Startup Sound:  $([[ -f "$STARTUP_MP3" ]] && echo "present ✅" || echo "missing ❌")"
echo "  Shutdown Sound: $([[ -f "$SHUTDOWN_MP3" ]] && echo "present ✅" || echo "missing ❌")"
echo

echo "Screensaver:"
echo "  Mode: $(screensaver_mode)"
echo

echo "PIA:"
if [[ -x "$PIACTL" ]]; then
  # Print PIA connection state (ignore failures)
  "$PIACTL" get connectionstate 2>/dev/null || true
else
  echo "piactl not found at $PIACTL"
fi
echo

echo "PiVPN ($PIVPN_SERVICE):"
systemctl is-active --quiet "$PIVPN_SERVICE" && echo "active ✅" || echo "inactive ❌"
echo

echo "NAS reachability ($NAS_IP):"
if ping -c 1 -W 1 "$NAS_IP" >/dev/null 2>&1; then
  echo "reachable ✅"
else
  echo "not reachable ❌"
fi
echo

echo "Mountpoints (from /proc/self/mountinfo — safe even if NFS is dead):"
for m in "${MOUNTS[@]}"; do
  if is_mounted_mountinfo "$m"; then
    src="$(mounted_source_mountinfo "$m" || true)"
    [[ -n "$src" ]] && echo "✅ $m   ($src)" || echo "✅ $m"
  else
    echo "❌ $m"
  fi
done

echo
read -n 1 -r -s -p "Press any key to close…"
echo

This script reads mount state via:

/proc/self/mountinfo

…so it won’t hang if NFS is down.

It also reads marker files:

~/.config/omarchy/state/screensaver_mode

~/.config/omarchy/state/current_sound

Create the directory:

mkdir -p ~/.config/omarchy/state

(Optional) seed defaults:

echo “stock” > ~/.config/omarchy/state/screensaver_mode echo “Windows XP” > ~/.config/omarchy/state/current_sound

📙Part 6 — Screensaver mode toggles (hypridle)

Create:

~/.config/omarchy/bin/screensaver-stock-mode.sh

~/.config/omarchy/bin/screensaver-long-mode.sh

Paste:

~/.config/omarchy/bin/screensaver-stock-mode.sh:

#!/usr/bin/env bash
# screensaver-stock-mode.sh
#
# What this does:
# - Switches Hypridle to your "stock" screensaver/idle configuration by:
#     1) Copying hypridle-stock.conf -> hypridle.conf
#     2) Restarting hypridle (systemd user service if present; otherwise manual restart)
#     3) Writing the selected mode ("stock") to Omarchy state:
#        ~/.config/omarchy/state/screensaver_mode
#
# Dependencies / assumptions:
# - Assumes Hyprland config lives in ~/.config/hypr
# - Assumes hypridle-stock.conf exists
# - Uses `rg` (ripgrep) to detect whether hypridle.service exists as a user unit

set -euo pipefail

CFG_DIR="$HOME/.config/hypr"
SRC="$CFG_DIR/hypridle-stock.conf"
DST="$CFG_DIR/hypridle.conf"

STATE_DIR="$HOME/.config/omarchy/state"
MODE_FILE="$STATE_DIR/screensaver_mode"

echo "=== Screensaver: STOCK mode ==="
echo "[1/3] Source:      $SRC"
echo "[1/3] Destination: $DST"
echo

# Ensure the source config exists before attempting to copy
if [[ ! -f "$SRC" ]]; then
  echo "ERROR: missing $SRC"
  command -v notify-send >/dev/null 2>&1 && notify-send "Screensaver" "Missing: hypridle-stock.conf" || true
  exit 1
fi

# Copy stock config into place as the active hypridle.conf
cp -f "$SRC" "$DST"
echo "[2/3] Copied stock config -> hypridle.conf"

# Restart hypridle:
# - Prefer systemd user service if present
# - Otherwise kill + relaunch manually
if systemctl --user list-unit-files 2>/dev/null | rg -q '^hypridle\.service'; then
  systemctl --user restart hypridle.service
  echo "[3/3] Restarted hypridle.service (user)"
else
  pkill -x hypridle 2>/dev/null || true
  nohup hypridle -c "$DST" >/dev/null 2>&1 &
  disown || true
  echo "[3/3] Restarted hypridle (manual)"
fi

# Record selected mode so your status script can display it
mkdir -p "$STATE_DIR"
echo "stock" > "$MODE_FILE"

# Notify user
command -v notify-send >/dev/null 2>&1 && notify-send "Screensaver" "Stock mode enabled ✅" || true
echo
read -n 1 -r -s -p "Press any key to close…"
echo

~/.config/omarchy/bin/screensaver-long-mode.sh:

#!/usr/bin/env bash
# screensaver-long-mode.sh
#
# What this does:
# - Switches Hypridle to your "long" screensaver/idle configuration by:
#     1) Copying hypridle-long.conf -> hypridle.conf
#     2) Restarting hypridle (systemd user service if present; otherwise manual restart)
#     3) Writing the selected mode ("long") to Omarchy state:
#        ~/.config/omarchy/state/screensaver_mode
#
# Dependencies / assumptions:
# - Assumes Hyprland config lives in ~/.config/hypr
# - Assumes hypridle-long.conf exists
# - Uses `rg` (ripgrep) to detect whether hypridle.service exists as a user unit

set -euo pipefail

CFG_DIR="$HOME/.config/hypr"
SRC="$CFG_DIR/hypridle-long.conf"
DST="$CFG_DIR/hypridle.conf"

STATE_DIR="$HOME/.config/omarchy/state"
MODE_FILE="$STATE_DIR/screensaver_mode"

echo "=== Screensaver: LONG mode ==="
echo "[1/3] Source:      $SRC"
echo "[1/3] Destination: $DST"
echo

# Ensure the source config exists before attempting to copy
if [[ ! -f "$SRC" ]]; then
  echo "ERROR: missing $SRC"
  command -v notify-send >/dev/null 2>&1 && notify-send "Screensaver" "Missing: hypridle-long.conf" || true
  exit 1
fi

# Copy long config into place as the active hypridle.conf
cp -f "$SRC" "$DST"
echo "[2/3] Copied long config -> hypridle.conf"

# Restart hypridle:
# - Prefer systemd user service if present
# - Otherwise kill + relaunch manually
if systemctl --user list-unit-files 2>/dev/null | rg -q '^hypridle\.service'; then
  systemctl --user restart hypridle.service
  echo "[3/3] Restarted hypridle.service (user)"
else
  pkill -x hypridle 2>/dev/null || true
  nohup hypridle -c "$DST" >/dev/null 2>&1 &
  disown || true
  echo "[3/3] Restarted hypridle (manual)"
fi

# Record selected mode so your status script can display it
mkdir -p "$STATE_DIR"
echo "long" > "$MODE_FILE"

# Notify user
command -v notify-send >/dev/null 2>&1 && notify-send "Screensaver" "Long mode enabled ✅" || true
echo
read -n 1 -r -s -p "Press any key to close…"
echo

These scripts assume you have two hypridle configs. You need “stock” and “long” versions.

~/.config/hypr/hypridle-stock.conf:

# ~/.config/hypr/hypridle-stock.conf
#
# STOCK screensaver/lock timing profile for hypridle.
# This file is used by:
#   ~/.config/omarchy/bin/screensaver-stock-mode.sh
# which copies it to:
#   ~/.config/hypr/hypridle.conf
# and restarts hypridle.
#
# What this profile does (high level):
# - Locks the session on sleep
# - Starts a screensaver relatively quickly
# - Locks the screen after a short idle period
# - Turns the display off shortly after locking
# - Restores DPMS + brightness state on resume
#
# Publish note:
# - No passwords, IPs, tokens, or hostnames are present here.
# - It references Omarchy helper commands (omarchy-lock-screen / omarchy-launch-screensaver).

general {
    lock_cmd = omarchy-lock-screen                         # lock screen and 1password
    before_sleep_cmd = loginctl lock-session               # lock before suspend.
    after_sleep_cmd = hyprctl dispatch dpms on             # to avoid having to press a key twice to turn on the display.
    inhibit_sleep = 3                                      # wait until screen is locked
}

listener {
    timeout = 150                                             # 2.5min
    on-timeout = pidof hyprlock || omarchy-launch-screensaver # start screensaver (if we haven't locked already)
}

listener {
    timeout = 300                      # 5min
    on-timeout = loginctl lock-session # lock screen when timeout has passed
}

listener {
    timeout = 330                                            # 5.5min
    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
}

~/.config/hypr/hypridle-long.conf:

# ~/.config/hypr/hypridle-long.conf
#
# LONG screensaver/lock timing profile for hypridle.
# This file is used by:
#   ~/.config/omarchy/bin/screensaver-long-mode.sh
# which copies it to:
#   ~/.config/hypr/hypridle.conf
# and restarts hypridle.
#
# What this profile does (high level):
# - Same behavior as the stock profile, but with longer idle timeouts
# - Screensaver starts later
# - Lock happens later
# - Display power-off happens later
#
# Publish note:
# - No passwords, IPs, tokens, or hostnames are present here.
# - It references Omarchy helper commands (omarchy-lock-screen / omarchy-launch-screensaver).

general {
    lock_cmd = omarchy-lock-screen                         # lock screen and 1password
    before_sleep_cmd = loginctl lock-session               # lock before suspend.
    after_sleep_cmd = hyprctl dispatch dpms on             # to avoid having to press a key twice to turn on the display.
    inhibit_sleep = 3                                      # wait until screen is locked
}

listener {
    timeout = 600                                             # 10min (600)
    on-timeout = pidof hyprlock || omarchy-launch-screensaver # start screensaver (if we haven't locked already)
}

listener {
    timeout = 900                      # 15min (900)
    on-timeout = loginctl lock-session # lock screen when timeout has passed
}

listener {
    timeout = 990                                            # 16.5min (990)
    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
}

📘Part 7 — Windows Startup + Shutdown Sounds

Step 1: Sound directory

Create:

mkdir -p ~/.config/omarchy/sounds

Step 2: Put sound files in place

You’ll need theme MP3s in:

~/.config/omarchy/sounds/

Example filenames:

winxpstartup.mp3, winxpshutdown.mp3

win2000startup.mp3, win2000shutdown.mp3

winvistastartup.mp3, winvistashutdown.mp3

win11startup.mp3, win11shutdown.mp3

I’ll provide these to readers as a ZIP download. Extract into ~/.config/omarchy/sounds/.

Active sounds will be copied to:

startup.mp3

shutdown.mp3

Step 3: Theme picker + shutdown action

Create:

~/.config/omarchy/bin/change-startup-shutdown-sounds.sh

~/.config/omarchy/bin/change-startup-shutdown-sounds.sh:

#!/usr/bin/env bash
# change-startup-shutdown-sounds.sh
#
# What this does:
# - Lets you pick a "sound theme" (Windows 2000 / XP / Vista / 11) from a dmenu-style selector
# - Copies the selected theme’s startup/shutdown MP3 files into:
#     - ~/.config/omarchy/sounds/startup.mp3
#     - ~/.config/omarchy/sounds/shutdown.mp3
# - Writes the selected theme name into:
#     - ~/.config/omarchy/state/current_sound
# - Shows a desktop notification + prints a summary in the terminal
#
# Dependencies / assumptions:
# - Requires `omarchy-launch-walker` (Omarchy's dmenu/launcher wrapper)
# - Assumes the MP3 files already exist in $SOUND_DIR

set -euo pipefail

# Where the sound files live
SOUND_DIR="$HOME/.config/omarchy/sounds"

# Where we store the selected theme name
STATE_DIR="$HOME/.config/omarchy/state"
CURRENT_FILE="$STATE_DIR/current_sound"

# Small helper for desktop notifications (silent fallback if notify-send isn't installed)
notify() {
  command -v notify-send >/dev/null 2>&1 && notify-send "Omarchy Sounds" "$1" || true
}

# Ensure folders exist
mkdir -p "$SOUND_DIR" "$STATE_DIR"

# Menu options shown to the user
options=$'Windows 2000\nWindows XP\nWindows Vista\nWindows 11'

# Launch the picker (dmenu-style). If the user cancels, exit cleanly.
choice="$(printf '%s\n' "$options" | omarchy-launch-walker --dmenu --width 360 --minheight 1 --maxheight 300 -p "Sound theme…" 2>/dev/null || true)"
[[ -z "${choice:-}" || "$choice" == "CNCLD" ]] && exit 0

# Map theme choice -> the corresponding MP3 files in SOUND_DIR
case "$choice" in
  "Windows 2000")
    startup_src="$SOUND_DIR/win2000startup.mp3"
    shutdown_src="$SOUND_DIR/win2000shutdown.mp3"
    ;;
  "Windows XP")
    startup_src="$SOUND_DIR/winxpstartup.mp3"
    shutdown_src="$SOUND_DIR/winxpshutdown.mp3"
    ;;
  "Windows Vista")
    startup_src="$SOUND_DIR/winvistastartup.mp3"
    shutdown_src="$SOUND_DIR/winvistashutdown.mp3"
    ;;
  "Windows 11")
    startup_src="$SOUND_DIR/win11startup.mp3"
    shutdown_src="$SOUND_DIR/win11shutdown.mp3"
    ;;
  *)
    notify "Unknown selection: $choice"
    exit 1
    ;;
esac

# Validate the startup sound exists
if [[ ! -f "$startup_src" ]]; then
  notify "Missing file: $(basename "$startup_src")"
  echo "Missing: $startup_src" >&2
  exit 1
fi

# Validate the shutdown sound exists
if [[ ! -f "$shutdown_src" ]]; then
  notify "Missing file: $(basename "$shutdown_src")"
  echo "Missing: $shutdown_src" >&2
  exit 1
fi

# Copy the selected theme into the "active" sound filenames used by the other scripts
cp -f "$startup_src"  "$SOUND_DIR/startup.mp3"
cp -f "$shutdown_src" "$SOUND_DIR/shutdown.mp3"

# Record which theme is active
echo "$choice" > "$CURRENT_FILE"

# Notify + print a quick summary for the user
notify "Sound theme set to: $choice ✅"

echo "Sound theme set to: $choice ✅"
echo
echo "startup.mp3  <- $(basename "$startup_src")"
echo "shutdown.mp3 <- $(basename "$shutdown_src")"
echo
read -n 1 -r -s -p "Press any key to close…"
echo

Create:

~/.config/omarchy/bin/action-with-shutdown-sound.sh

~/.config/omarchy/bin/action-with-shutdown-sound.sh:

#!/usr/bin/env bash
# action-with-shutdown-sound.sh
#
# What this does:
# - Ensures a default sound "theme" exists in ~/.config/omarchy/state/
# - Ensures shutdown.mp3 exists (seeds it from a fallback sound if needed)
# - Plays the shutdown sound *blocking* (so it completes before powering off)
# - Then performs the requested system action:
#     - shutdown -> systemctl poweroff
#     - reboot   -> systemctl reboot
#     - halt     -> systemctl halt
#   Or:
#     -- <command...> -> play sound, then exec the command

set -euo pipefail

# Where your sounds live
SOUND_DIR="$HOME/.config/omarchy/sounds"

# Where your customization state lives (stores "current_sound" label)
STATE_DIR="$HOME/.config/omarchy/state"
CURRENT_FILE="$STATE_DIR/current_sound"

# Preferred "active" shutdown sound (selected by your menu/sound picker)
SOUND_SHUTDOWN="$SOUND_DIR/shutdown.mp3"

# Fallback sound (used if user hasn't selected anything yet)
FALLBACK_SHUTDOWN="$SOUND_DIR/winxpshutdown.mp3"

ensure_defaults() {
  # Ensure required directories exist
  mkdir -p "$SOUND_DIR" "$STATE_DIR"

  # If no theme has been selected yet, seed a default label
  if [[ ! -f "$CURRENT_FILE" ]]; then
    echo "Windows XP" > "$CURRENT_FILE"
  fi

  # If shutdown.mp3 doesn't exist yet, seed it from fallback (if available)
  if [[ ! -f "$SOUND_SHUTDOWN" && -f "$FALLBACK_SHUTDOWN" ]]; then
    cp -f "$FALLBACK_SHUTDOWN" "$SOUND_SHUTDOWN"
  fi
}

play_sound() {
  local f="$1"

  # Blocking play (important): we want the sound to finish before shutdown.
  # Try several players in order, using the first one found.
  if command -v mpv >/dev/null 2>&1; then
    mpv --no-video --really-quiet --keep-open=no "$f" >/dev/null 2>&1 || true
  elif command -v ffplay >/dev/null 2>&1; then
    ffplay -nodisp -autoexit -loglevel quiet "$f" >/dev/null 2>&1 || true
  elif command -v pw-play >/dev/null 2>&1; then
    pw-play "$f" >/dev/null 2>&1 || true
  elif command -v paplay >/dev/null 2>&1; then
    paplay "$f" >/dev/null 2>&1 || true
  fi
}

# Ensure default files/dirs are present before we proceed
ensure_defaults

# Choose which shutdown sound to play:
# - Prefer shutdown.mp3 if present
# - Otherwise fall back to the theme fallback sound
# - Otherwise play nothing
SOUND_TO_PLAY=""
if [[ -f "$SOUND_SHUTDOWN" ]]; then
  SOUND_TO_PLAY="$SOUND_SHUTDOWN"
elif [[ -f "$FALLBACK_SHUTDOWN" ]]; then
  SOUND_TO_PLAY="$FALLBACK_SHUTDOWN"
fi

case "${1-}" in
  shutdown)
    [[ -n "$SOUND_TO_PLAY" ]] && play_sound "$SOUND_TO_PLAY"
    exec systemctl poweroff
    ;;
  reboot)
    [[ -n "$SOUND_TO_PLAY" ]] && play_sound "$SOUND_TO_PLAY"
    exec systemctl reboot
    ;;
  halt)
    [[ -n "$SOUND_TO_PLAY" ]] && play_sound "$SOUND_TO_PLAY"
    exec systemctl halt
    ;;
  --)
    # Generic wrapper mode:
    #   action-with-shutdown-sound.sh -- <command ...>
    # Plays the sound, then execs whatever command you pass.
    shift
    [[ -n "$SOUND_TO_PLAY" ]] && play_sound "$SOUND_TO_PLAY"
    exec "$@"
    ;;
  *)
    echo "Usage: $0 {shutdown|reboot|halt|-- <command...>}" >&2
    exit 2
    ;;
esac

Create (optional startup helper):

~/.config/omarchy/bin/hypr-startup-sound.sh

~/.config/omarchy/bin/hypr-startup-sound.sh:

#!/usr/bin/env bash
# hypr-startup-sound.sh
#
# What this does:
# - Ensures your Omarchy sound/state folders exist
# - Ensures a default "current sound theme" label exists (if not set yet)
# - Ensures startup.mp3 exists (seeds it from a fallback sound if needed)
# - Plays the startup sound (best effort) using the first available player:
#     mpv -> ffplay -> pw-play -> paplay

set -euo pipefail

# Where your sounds live
SOUND_DIR="$HOME/.config/omarchy/sounds"

# Where your customization state lives (stores "current_sound" label)
STATE_DIR="$HOME/.config/omarchy/state"
CURRENT_FILE="$STATE_DIR/current_sound"

# Preferred "active" startup sound (selected by your menu/sound picker)
SOUND_STARTUP="$SOUND_DIR/startup.mp3"

# Fallback sound (used if user hasn't selected anything yet)
FALLBACK_STARTUP="$SOUND_DIR/winxpstartup.mp3"

ensure_defaults() {
  # Ensure required directories exist
  mkdir -p "$SOUND_DIR" "$STATE_DIR"

  # If no theme has been selected yet, seed a default label
  if [[ ! -f "$CURRENT_FILE" ]]; then
    echo "Windows XP" > "$CURRENT_FILE"
  fi

  # If startup.mp3 doesn't exist yet, seed it from fallback (if available)
  if [[ ! -f "$SOUND_STARTUP" && -f "$FALLBACK_STARTUP" ]]; then
    cp -f "$FALLBACK_STARTUP" "$SOUND_STARTUP"
  fi
}

play_sound() {
  local f="$1"

  # Blocking play: the sound will play fully before the script exits.
  # Try several players in order, using the first one found.
  if command -v mpv >/dev/null 2>&1; then
    mpv --no-video --really-quiet --keep-open=no "$f" >/dev/null 2>&1 || true
  elif command -v ffplay >/dev/null 2>&1; then
    ffplay -nodisp -autoexit -loglevel quiet "$f" >/dev/null 2>&1 || true
  elif command -v pw-play >/dev/null 2>&1; then
    pw-play "$f" >/dev/null 2>&1 || true
  elif command -v paplay >/dev/null 2>&1; then
    paplay "$f" >/dev/null 2>&1 || true
  fi
}

# Ensure default files/dirs are present before we proceed
ensure_defaults

# Prefer startup.mp3; fall back to theme fallback sound; otherwise do nothing
if [[ -f "$SOUND_STARTUP" ]]; then
  play_sound "$SOUND_STARTUP"
elif [[ -f "$FALLBACK_STARTUP" ]]; then
  play_sound "$FALLBACK_STARTUP"
fi

Edit your main ~/.config/hypr/hyprland.conf with the following exec-once for that startup sound:

~/.config/hypr/hyprland.conf SNIPPET:

# Add these lines to hyprland.conf:

# Windows XP StartUp Sound
exec-once = $HOME/.config/omarchy/bin/hypr-startup-sound.sh

📗Part 8 — GParted fix (Wayland + pkexec)

Create:

~/.config/omarchy/bin/gparted-fixed.sh

~/.config/omarchy/bin/gparted-fixed.sh:

#!/usr/bin/env bash
# gparted-fixed.sh
#
# What this does:
# - Launches GParted as root via pkexec (PolicyKit)
# - Explicitly passes through the key environment variables that GUI apps often
#   need under Wayland/Hyprland, so GParted reliably opens instead of failing
#   with "cannot open display" / missing runtime dir issues.
#
# Why this exists:
# - Some GUI apps launched with pkexec don't inherit the right environment.
# - Setting XDG_RUNTIME_DIR / WAYLAND_DISPLAY / DISPLAY / XAUTHORITY makes the
#   privileged GUI session connect properly to your current desktop session.

set -euo pipefail

# Current user numeric ID (used to build /run/user/<uid>)
USER_ID="$(id -u)"

# Run GParted as root with a sane GUI environment
exec pkexec env \
  XDG_RUNTIME_DIR="/run/user/$USER_ID" \
  WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-1}" \
  DISPLAY="${DISPLAY:-:0}" \
  XAUTHORITY="${XAUTHORITY:-$HOME/.Xauthority}" \
  /usr/bin/gparted

Now you have two options:

Option A (recommended): update the GParted desktop launcher

Create/edit:

~/.local/share/applications/gparted.desktop

~/.local/share/applications/gparted.desktop:

[Desktop Entry]
Name=GParted
X-GNOME-FullName=GParted Partition Editor
Comment=Create, reorganize, and delete partitions
Exec=~/.config/omarchy/bin/gparted-fixed.sh %f
Icon=gparted
Terminal=false
Type=Application
Categories=GNOME;System;Filesystem;
Keywords=Partition;
StartupNotify=true

Option B: run the script manually ~/.config/omarchy/bin/gparted-fixed.sh

📕Part 9 — Fastfetch MOTD “Smart Mode” (logo only when it fits)

Create:

~/.config/omarchy/bin/terminal-motd.sh

~/.config/omarchy/bin/terminal-motd.sh:

#!/usr/bin/env bash
# terminal-motd.sh
#
# What this does:
# - Prints a nice "MOTD" / system info panel using fastfetch when you open a terminal
# - Only prints when:
#     - stdout is a real TTY (interactive terminal)
#     - it hasn't already been printed in the current terminal session
#     - fastfetch exists
# - Adapts output based on terminal width:
#     - Narrow terminal (<110 cols): compact, no logo, custom structure (includes localip)
#     - Wide terminal: chooses logo size based on how many Alacritty windows are open
#       on the current Hyprland workspace
#
# Dependencies / assumptions:
# - fastfetch installed
# - For workspace/window-aware behavior: hyprctl + jq installed and Hyprland running

set -euo pipefail

# If sourced, we can "return"; if executed, we must "exit".
_is_sourced() { [[ "${BASH_SOURCE[0]}" != "${0}" ]]; }
_die() { local rc="${1:-0}"; shift || true; _is_sourced && return "$rc" || exit "$rc"; }

# Only run when stdout is a TTY (real terminal)
[[ -t 1 ]] || _die 0

# Avoid re-printing inside the same terminal session
if [[ -n "${OMARCHY_MOTD_SHOWN:-}" ]]; then
  _die 0
fi
export OMARCHY_MOTD_SHOWN=1

# If fastfetch isn't available, do nothing (silent)
command -v fastfetch >/dev/null 2>&1 || _die 0

# Terminal width (columns). Default to 120 if tput fails.
cols="$(tput cols 2>/dev/null || echo 120)"

# Always go compact if the terminal is narrow
if (( cols < 110 )); then
  fastfetch --logo none \
    --structure "title:separator:os:kernel:uptime:packages:shell:terminal:wm:cpu:memory:disk:break:localip" \
    2>/dev/null || true
  _die 0
fi

# If Hyprland JSON tools aren't available, just do "no logo"
if ! command -v hyprctl >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then
  fastfetch --logo none 2>/dev/null || true
  _die 0
fi

# Give Hypr a split second to register the new client
sleep 0.05

# Get current workspace id
ws_id="$(hyprctl activeworkspace -j 2>/dev/null | jq -r '.id // empty' || true)"
if [[ -z "${ws_id:-}" ]]; then
  fastfetch --logo none 2>/dev/null || true
  _die 0
fi

# Count Alacritty windows on this workspace.
# NOTE: Hyprland "class" is usually "Alacritty" but sometimes it's "org.alacritty.Alacritty".
alacritty_count="$(
  hyprctl clients -j 2>/dev/null | jq --argjson ws "$ws_id" '
    [ .[]
      | select(.workspace.id == $ws)
      | select((.class // "" | ascii_downcase) | test("alacritty"))
    ] | length
  ' 2>/dev/null || echo 2
)"

# If this is the first/only Alacritty window on the workspace, show small logo.
# If multiple terminals are open, disable logo to keep output compact.
if [[ "${alacritty_count:-2}" -le 1 ]]; then
  fastfetch --logo small 2>/dev/null || true
else
  fastfetch --logo none 2>/dev/null || true
fi

_die 0

Now in your ~/.bashrc, you add a small block (do not blindly replace your whole bashrc).

Add to ~/.bashrc (append near the bottom)

In this post I’ll show the exact block to add:

~/.bashrc:

#Add these to end of .bashrc:

export PATH="$HOME/.config/omarchy/bin:$PATH"
export BAT_THEME="Catppuccin Mocha"

[[ $- == *i* ]] && ~/.config/omarchy/bin/terminal-motd.sh

Notes:

Runs only for interactive shells

Sets a Bat theme (optional)

Uses fastfetch with logic based on terminal width + number of Alacritty windows on current workspace

📗Part 10 — Hyprland config: add binds (do not replace whole file)

You’re adding binds, not replacing all of bindings.conf.

Open:

~/.config/hypr/bindings.conf

Add these lines (example includes cheatsheet + emote):

~/.config/hypr/bindings.conf:

# --- Omarchy customizations: add these binds to ~/.config/hypr/bindings.conf ---
#
# What these do:
# - SUPER+SHIFT+K  → Generate ALL cheat sheets (TXT + MD + PDF) and open the TXT view
# - SUPER+ALT+K    → Generate + open the PDF cheat sheet (fullscreen via your window rules)
# - SUPER+ALT+E    → Launch the emoji picker (`emote`)

# Custom cheatsheet (generate + show everything)
bind = SUPER SHIFT, K, exec, $HOME/.config/omarchy/bin/show-cheatsheet.sh --all

# Custom cheatsheet (open PDF viewer)
bind = SUPER ALT, K, exec, $HOME/.config/omarchy/bin/show-cheatsheet-pdf.sh

# Emoji picker
bind = SUPER ALT, E, exec, emote

Then reload Hyprland.

📘Part 11 — Waybar VPN modules (optional)

If you want the Waybar indicators/toggles, create these files:

~/.config/waybar/pia-status.sh

~/.config/waybar/pia-toggle.sh

~/.config/waybar/pia-pick-region.sh

~/.config/waybar/pivpn-status.sh

~/.config/waybar/pivpn-toggle.sh

Paste them:

~/.config/waybar/pia-status.sh:

#!/usr/bin/env bash
# pia-status.sh
#
# What this does:
# - Outputs JSON for a Waybar custom module showing PIA VPN status:
#     - "text": a toggle icon (on/off)
#     - "class": active/inactive (for CSS styling)
#     - "tooltip": multi-line details (state, region, public IP, VPN IP)
#
# Waybar usage:
# - This script is typically called by a Waybar "custom" module with `exec`
# - Waybar reads the JSON and displays the text + tooltip
#
# Dependencies / assumptions:
# - PIA is installed and `piactl` exists at /opt/piavpn/bin/piactl
# - Optional: `jq` (used for safer JSON escaping; script has a fallback escaper)

set -euo pipefail
export LC_ALL=C LANG=C

PIACTL="/opt/piavpn/bin/piactl"

# Helper that never hard-fails: returns empty/partial output if piactl errors.
safe_get() { "$PIACTL" get "$1" 2>/dev/null | tr -d '\r' || true; }

# Query PIA state fields
state="$(safe_get connectionstate)"
region="$(safe_get region)"
pubip="$(safe_get pubip)"
vpnip="$(safe_get vpnip)"

# Pick icon + CSS class based on connection state
if [[ "$state" == "Connected" ]]; then
  text=""; cls="active"
else
  text=""; cls="inactive"
fi

# Build tooltip with real newlines (no markup)
tip="PIA VPN: ${state:-Unknown}
Region: ${region:-auto}
Public IP: ${pubip:-unknown}
VPN IP: ${vpnip:-unknown}"

if command -v jq >/dev/null 2>&1; then
  # Use jq to JSON-escape fields so newlines render correctly
  printf '{"text":%s,"class":%s,"tooltip":%s}\n' \
    "$(printf %s "$text"  | jq -Rs .)" \
    "$(printf %s "$cls"   | jq -Rs .)" \
    "$(printf %s "$tip"   | jq -Rs .)"
else
  # Fallback escaper if jq isn't available
  esc() {
    local s=${1//\\/\\\\}
    s=${s//\"/\\\"}
    s=${s//$'\n'/\\n}
    printf '%s' "$s"
  }
  printf '{"text":"%s","class":"%s","tooltip":"%s"}\n' \
    "$(esc "$text")" "$(esc "$cls")" "$(esc "$tip")"
fi

~/.config/waybar/pia-toggle.sh:

#!/usr/bin/env bash
# pia-toggle.sh
#
# What this does:
# - Simple toggle for Private Internet Access (PIA) using piactl:
#     - If PIA is Connected or Connecting -> disconnect
#     - Otherwise -> connect
# - Enables `piactl background` so the daemon can run headless (no GUI needed)
#
# Typical usage:
# - Called from a Waybar button (custom module "on-click") or a keybind
#
# Dependencies / assumptions:
# - PIA is installed and `piactl` exists at /opt/piavpn/bin/piactl

PIACTL="/opt/piavpn/bin/piactl"

# Make sure daemon can run headless
$PIACTL background enable >/dev/null 2>&1

# Read current connection state
state="$($PIACTL get connectionstate 2>/dev/null)"
if [[ "$state" == "Connected" || "$state" == "Connecting" ]]; then
  # If already up (or coming up), disconnect
  $PIACTL disconnect
else
  # Otherwise connect
  $PIACTL connect
fi

~/.config/waybar/pia-pick-region.sh:

#!/usr/bin/env bash
# pia-pick-region.sh
#
# What this does:
# - Fetches available Private Internet Access (PIA) regions using piactl
# - Lets you pick a region using a simple UI picker:
#     - rofi (preferred)
#     - wofi
#     - zenity (GUI fallback)
# - Sets the selected region via:
#     piactl set region "<choice>"
# - Sends a desktop notification confirming the selection
#
# Dependencies / assumptions:
# - PIA is installed and `piactl` exists at /opt/piavpn/bin/piactl
# - You are logged in to PIA (otherwise regions may be empty)
# - One of rofi/wofi/zenity is installed for selection UI

PIACTL="/opt/piavpn/bin/piactl"

# Get regions from piactl, then normalize to one region per line
regions="$($PIACTL get regions 2>/dev/null | tr ' ' '\n' | sed '/^$/d')"
[ -z "$regions" ] && notify-send "PIA" "No regions yet. Try: piactl login" && exit 1

# Pick with rofi/wofi, fallback to zenity
if command -v rofi >/dev/null; then
  choice="$(echo "$regions" | rofi -dmenu -p 'PIA region')"
elif command -v wofi >/dev/null; then
  choice="$(echo "$regions" | wofi --dmenu -p 'PIA region')"
elif command -v zenity >/dev/null; then
  choice="$(echo "$regions" | zenity --list --column=Region)"
else
  notify-send "PIA" "Install rofi/wofi/zenity to pick regions."
  exit 1
fi

# If user cancelled, exit cleanly
[ -z "$choice" ] && exit 0

# Apply selected region and notify
$PIACTL set region "$choice" && notify-send "PIA" "Region set: $choice"

~/.config/waybar/pivpn-status.sh:

#!/usr/bin/env bash
# pivpn-status.sh
#
# What this does:
# - Outputs JSON for a Waybar custom module showing PiVPN status:
#     - "text": a toggle icon (on/off)
#     - "class": active/inactive (for CSS styling)
#     - "tooltip": multi-line details (state, tun interface, VPN IP)
#
# How it detects status:
# - Checks whether the systemd service `openvpn-client@pivpn` is active
# - Attempts to find a tun interface (tun0, tun1, etc.) and read its IPv4 address
#
# Dependencies / assumptions:
# - systemd
# - iproute2 (`ip`)
# - Optional: `jq` (used for safer JSON escaping; script has a fallback escaper)

set -euo pipefail
export LC_ALL=C LANG=C

SERVICE="openvpn-client@pivpn"

# Figure out if the PiVPN systemd service is active
if systemctl is-active --quiet "$SERVICE"; then
  state="Connected"
  text=""
  cls="active"
else
  state="Disconnected"
  text=""
  cls="inactive"
fi

# Try to detect tun interface + VPN IP
tun_if="$(ip -o link show | awk -F': ' '/tun[0-9]+/ {print $2; exit}' 2>/dev/null || true)"
vpn_ip=""
if [[ -n "${tun_if:-}" ]]; then
  vpn_ip="$(ip -o -4 addr show "$tun_if" 2>/dev/null | awk '{print $4}' | cut -d/ -f1 || true)"
fi

# Build tooltip with real newlines (no markup)
tip="PiVPN: ${state:-Unknown}
Interface: ${tun_if:-none}
VPN IP: ${vpn_ip:-unknown}"

if command -v jq >/dev/null 2>&1; then
  # Use jq to JSON-escape fields so newlines render correctly
  printf '{"text":%s,"class":%s,"tooltip":%s}\n' \
    "$(printf %s "$text" | jq -Rs .)" \
    "$(printf %s "$cls"  | jq -Rs .)" \
    "$(printf %s "$tip"  | jq -Rs .)"
else
  # Fallback escaper if jq isn't available
  esc() {
    local s=${1//\\/\\\\}
    s=${s//\"/\\\"}
    s=${s//$'\n'/\\n}
    printf '%s' "$s"
  }
  printf '{"text":"%s","class":"%s","tooltip":"%s"}\n' \
    "$(esc "$text")" "$(esc "$cls")" "$(esc "$tip")"
fi

~/.config/waybar/pivpn-toggle.sh:

#!/usr/bin/env bash
# pivpn-toggle.sh
#
# What this does:
# - Toggles your PiVPN connection using the systemd OpenVPN client unit:
#     openvpn-client@pivpn
# - If the service is active:
#     - runs your pivpn-disconnect.sh helper (silently)
# - If the service is inactive:
#     - runs your pivpn-connect.sh helper (silently)
#
# Why helper scripts are used:
# - Your connect/disconnect scripts can handle extra logic like:
#     - coordinating with PIA (disconnecting/restoring)
#     - routes, DNS, cleanup, notifications, etc.
#
# Dependencies / assumptions:
# - systemd + an OpenVPN client unit named openvpn-client@pivpn
# - These helper scripts exist:
#     ~/.local/bin/pivpn-connect.sh
#     ~/.local/bin/pivpn-disconnect.sh

set -euo pipefail

SERVICE="openvpn-client@pivpn"

if systemctl is-active --quiet "$SERVICE"; then
  # PiVPN is ON → disconnect (and maybe restore PIA)
  ~/.local/bin/pivpn-disconnect.sh >/dev/null 2>&1
else
  # PiVPN is OFF → connect (and maybe drop PIA)
  ~/.local/bin/pivpn-connect.sh >/dev/null 2>&1
fi

Then edit your Waybar config to include modules. Since every Waybar config differs, I’m going to provide:

an example module snippet to paste into your config.jsonc

optional CSS

~/.config/waybar/config.jsonc SNIPPET:

* Find the "modules-right": [...] array and add these two entries wherever you 
want the icons to appear:

  // --- Custom VPN buttons (add these entries to modules-right) ---
  "custom/pia",
  "custom/pivpn"

* Paste this anywhere at the top level of the JSON (most people put it near 
other custom/* blocks):

  // --- Custom VPN button: PIA ---
  // Requires these scripts:
  //   ~/.config/waybar/pia-status.sh
  //   ~/.config/waybar/pia-toggle.sh
  //   ~/.config/waybar/pia-pick-region.sh  (right-click)
  "custom/pia": {
    "format": "{}",
    "return-type": "json",
    "interval": 3,
    "exec": "$HOME/.config/waybar/pia-status.sh",
    "on-click": "$HOME/.config/waybar/pia-toggle.sh",
    "on-click-right": "$HOME/.config/waybar/pia-pick-region.sh"
  },
  // --- Custom VPN button: PiVPN ---
  // Requires these scripts:
  //   ~/.config/waybar/pivpn-status.sh
  //   ~/.config/waybar/pivpn-toggle.sh
  //
  // Note: pivpn-toggle.sh calls your helper scripts:
  //   ~/.local/bin/pivpn-connect.sh
  //   ~/.local/bin/pivpn-disconnect.sh
  "custom/pivpn": {
    "format": "{}",
    "exec": "$HOME/.config/waybar/pivpn-status.sh",
    "interval": 5,
    "return-type": "json",
    "on-click": "$HOME/.config/waybar/pivpn-toggle.sh"
  }

~/.config/waybar/style.css SNIPPET:

/* ---------------------------
   PIA + PiVPN buttons (Waybar)
   ---------------------------
   These IDs match the Waybar custom modules:
     "custom/pia"   -> #custom-pia
     "custom/pivpn" -> #custom-pivpn

   The scripts output JSON with:
     "class": "active"   or "inactive"
   ...so we can style connected vs disconnected states.
*/

/* Base styling for both VPN icons */
#custom-pia,
#custom-pivpn {
  
margin: 0 6px;
  font-size: 14px;
}

/* Extra tiny gap between the two VPN icons */
#custom-pivpn {
  margin-left: 7px;
}

/* PIA status styling */
#custom-pia.active {
  color: #a6e3a1;
}
#custom-pia.inactive {
  opacity: 0.7;
}

/* PiVPN status styling */
#custom-pivpn.active {
  color: #a6e3a1;
}
#custom-pivpn.inactive {
  opacity: 0.7;
}

Restart Waybar.

📙Part 12 — Fonts (Amiga vibes)

Copy your preferred .ttf fonts into:

~/.local/share/fonts/

Then:

fc-cache -fv

In this post, I’ll show the fonts I used (and/or provide a ZIP):

~/.local/share/fonts file list:

 MicroKnight_v1.0.ttf       omarchy.ttf            Topaz_a500_v1.0.ttf
 MicroKnightPlus_v1.0.ttf   P0T-NOoDLE_v1.0.ttf    TopazPlus_a1200_v1.0.ttf
 "mO'sOul_v1.0.ttf"         Topaz_a1200_v1.0.ttf   TopazPlus_a500_v1.0.ttf

Set your terminal font in Alacritty:

~/.config/alacritty/alacritty.toml:

general.import = [ "~/.config/omarchy/current/theme/alacritty.toml" ]

[env]
TERM = "xterm-256color"

[font]
normal = { family = "TopazPlus a600a1200a4000", style = "Regular" }
bold = { family = "TopazPlus a600a1200a4000", style = "Bold" }
italic = { family = "TopazPlus a600a1200a4000", style = "Italic" }
size = 12

[window]
padding.x = 14
padding.y = 14
decorations = "None"

[keyboard]
bindings = [
{ key = "F11", action = "ToggleFullscreen" },
{ key = "Insert", mods = "Shift", action = "Paste" },
{ key = "Insert", mods = "Control", action = "Copy" }
]

⬇️ Download the config and sound files HERE!

Let’s go TechHearters!!

TechHeart.life