feat(menu): VPN setup & management submenu (WireGuard/OpenVPN/Tailscale)
A dedicated System → VPN flow (nomarchy-vpn) past the wifi picker: - NetworkManager VPN/WireGuard connections shown ● active / ○ inactive, toggled up/down on select (networkmanager-group users need no sudo). - Import a *.conf / *.ovpn via the fd picker → nmcli connection import, type chosen by extension. networkmanager-openvpn ships system-side (networking.networkmanager.plugins, mkDefault) so the openvpn type is available; WireGuard is native to NetworkManager. - Tailscale block, self-gated on the CLI (= nomarchy.services.tailscale): read-only status + up/down + exit-node; privileged actions go through a sudo terminal (the snapshot pattern), no operator wiring. Waybar custom/vpn shield (nomarchy-vpn-status): self-hides unless a NM tunnel or Tailscale is up; @good tone; click opens the submenu. Wired into the generated bar and both summer whole-swaps. Decided import-first; creation deferred to nm-connection-editor. ROADMAP marked shipped. Eval + home/system builds green; both scripts pass bash -n. Pending an on-machine check (live nmcli import/connect + Tailscale paths). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -97,32 +97,36 @@ how to override it. Items marked ✓ are shipped.
|
|||||||
UI (an unthemed browser page). Validated: the generated menu script's
|
UI (an unthemed browser page). Validated: the generated menu script's
|
||||||
`bash -n` build check passes, and an eval confirms printing-on puts
|
`bash -n` build check passes, and an eval confirms printing-on puts
|
||||||
system-config-printer in `systemPackages`. Pending an on-machine check.
|
system-config-printer in `systemPackages`. Pending an on-machine check.
|
||||||
- **VPN setup & management menu:** a dedicated **System → VPN** flow that goes
|
- ✓ **VPN setup & management menu:** a dedicated **System → VPN** flow
|
||||||
past today's Network module (`networkmanager_dmenu` only connects/disconnects
|
(`nomarchy-vpn`, `modules/home/rofi.nix`) that goes past the Network module
|
||||||
*existing* VPN connections) to a guided **setup + management** surface across
|
(`networkmanager_dmenu` only connects/disconnects *existing* VPNs) to a guided
|
||||||
the three common kinds:
|
setup + management surface across the three common kinds:
|
||||||
- **WireGuard:** import a `.conf` into NetworkManager (`nmcli connection import
|
- **WireGuard:** import a `.conf` into NetworkManager (`nmcli connection import
|
||||||
type wireguard file …` — NM handles wg tunnels natively, no extra plugin)
|
type wireguard file …` — NM handles wg tunnels natively, no plugin) and
|
||||||
and toggle it up/down.
|
toggle it up/down.
|
||||||
- **OpenVPN:** import an `.ovpn` (`nmcli connection import type openvpn file …`),
|
- **OpenVPN:** import an `.ovpn` (`nmcli connection import type openvpn file …`);
|
||||||
which needs the `networkmanager-openvpn` plugin shipped — gate the import
|
the `networkmanager-openvpn` plugin ships system-side
|
||||||
entry on it (the wireguard path has no such dependency).
|
(`networking.networkmanager.plugins`, mkDefault) so the openvpn type is
|
||||||
- **Tailscale:** `tailscale up/down`, status, and **exit-node** selection — it
|
available — import type is chosen by file extension.
|
||||||
lives outside NetworkManager, so the menu drives the `tailscale` CLI
|
- **Tailscale:** status (read-only, no privilege) + `up`/`down` + **exit-node**
|
||||||
directly, self-gated on `nomarchy.services.tailscale` (already an opt-in
|
selection. It lives outside NetworkManager, so the menu drives the
|
||||||
service) like the other hardware-gated System entries.
|
`tailscale` CLI directly, **self-gated** on the CLI being present
|
||||||
Shape: a `nomarchy-menu vpn` rofi submenu under **System** (Connect/Disconnect
|
(= `nomarchy.services.tailscale`). Privileged actions go through a terminal +
|
||||||
with each connection's live up/down state like the wifi picker · Import config
|
`sudo` (the snapshot-tool pattern) — no operator wiring.
|
||||||
via the existing Files/`fd` picker pattern · the Tailscale block when present),
|
Shape: a `nomarchy-menu vpn` rofi submenu under **System** — NM
|
||||||
ending in `↩ Back`. A self-gating Waybar `custom/vpn` indicator (shield glyph
|
VPN/WireGuard connections shown ● active / ○ inactive and toggled on select
|
||||||
while a tunnel or Tailscale is up) for parity with the other status modules,
|
(networkmanager-group users need no sudo), Import via the Files/`fd` picker
|
||||||
added to the summer whole-swaps too. Secrets stay in the connection manager's
|
(`*.conf`/`*.ovpn`), the Tailscale block when present — ending in `↩ Back`. A
|
||||||
own store (NetworkManager / Tailscale state) — no new secret manager (cf. the
|
self-gating Waybar **`custom/vpn`** shield (`nomarchy-vpn-status`: shown only
|
||||||
deferred rofi-rbw/pass note above). Open question: how much in-menu *creation*
|
while a NM tunnel or Tailscale is up; `@good` tone; click opens the submenu),
|
||||||
(vs import) to support — a from-scratch WireGuard keypair/peer editor is a lot
|
wired into the generated bar **and the summer whole-swaps**. Secrets stay in
|
||||||
of rofi surface, so likely import-first, deferring creation to
|
the connection manager's own store (NetworkManager / Tailscale) — no new secret
|
||||||
`nm-connection-editor`. Complements the existing Network item rather than
|
manager (cf. the deferred rofi-rbw/pass note above). **Decided: import-first**
|
||||||
replacing it.
|
— a from-scratch WireGuard keypair/peer editor is too much rofi surface, so
|
||||||
|
creation is deferred to `nm-connection-editor`. Eval + build green; **pending
|
||||||
|
an on-machine check** (the nmcli import/up-down + Tailscale paths need a live
|
||||||
|
session with real configs). Remaining (optional): operator-set Tailscale so its
|
||||||
|
toggles skip the sudo terminal; richer exit-node display (country/city).
|
||||||
- **Theme parity with legacy:** summer-day/night now carry their legacy
|
- **Theme parity with legacy:** summer-day/night now carry their legacy
|
||||||
bar layouts as `waybar.jsonc` whole-swaps (adapted: dead legacy script
|
bar layouts as `waybar.jsonc` whole-swaps (adapted: dead legacy script
|
||||||
modules dropped, Nerd-Fonts-v2 codepoints remapped to FontAwesome/v3,
|
modules dropped, Nerd-Fonts-v2 codepoints remapped to FontAwesome/v3,
|
||||||
|
|||||||
@@ -246,6 +246,11 @@ ${themeRows}
|
|||||||
# its config from xdg.configFile below (told to drive rofi).
|
# its config from xdg.configFile below (told to drive rofi).
|
||||||
exec networkmanager_dmenu ;;
|
exec networkmanager_dmenu ;;
|
||||||
|
|
||||||
|
vpn)
|
||||||
|
# VPN setup + management (nomarchy-vpn): NM VPN/WireGuard connect +
|
||||||
|
# config import + Tailscale. Distinct from `network` (the wifi picker).
|
||||||
|
exec nomarchy-vpn ;;
|
||||||
|
|
||||||
bluetooth)
|
bluetooth)
|
||||||
# blueman-manager GUI (services.blueman.enable, system-side).
|
# blueman-manager GUI (services.blueman.enable, system-side).
|
||||||
exec blueman-manager ;;
|
exec blueman-manager ;;
|
||||||
@@ -373,6 +378,7 @@ ${themeRows}
|
|||||||
bats=(/sys/class/power_supply/BAT*)
|
bats=(/sys/class/power_supply/BAT*)
|
||||||
choice=$( {
|
choice=$( {
|
||||||
row "Network" network-wireless
|
row "Network" network-wireless
|
||||||
|
row "VPN" network-vpn
|
||||||
row "Bluetooth" bluetooth
|
row "Bluetooth" bluetooth
|
||||||
[ -e "''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/pulse/native" ] \
|
[ -e "''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/pulse/native" ] \
|
||||||
&& row "Audio" audio-volume-high
|
&& row "Audio" audio-volume-high
|
||||||
@@ -395,6 +401,7 @@ ${themeRows}
|
|||||||
case "$choice" in
|
case "$choice" in
|
||||||
"$BACK") exec "$0" ;;
|
"$BACK") exec "$0" ;;
|
||||||
*Network*) exec "$0" network ;;
|
*Network*) exec "$0" network ;;
|
||||||
|
*VPN*) exec "$0" vpn ;;
|
||||||
*Bluetooth*) exec "$0" bluetooth ;;
|
*Bluetooth*) exec "$0" bluetooth ;;
|
||||||
*Audio*) exec "$0" audio ;;
|
*Audio*) exec "$0" audio ;;
|
||||||
*Printers*) exec "$0" printers ;;
|
*Printers*) exec "$0" printers ;;
|
||||||
@@ -427,15 +434,104 @@ ${themeRows}
|
|||||||
esac ;;
|
esac ;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
echo "usage: nomarchy-menu [tools|system|power|power-profile|theme|clipboard|calc|files|emoji|web|network|bluetooth|audio|printers|capture|keybinds|ask|dnd|nightlight|autotimezone|snapshot]" >&2
|
echo "usage: nomarchy-menu [tools|system|power|power-profile|theme|clipboard|calc|files|emoji|web|network|bluetooth|audio|printers|capture|keybinds|ask|dnd|nightlight|autotimezone|vpn|snapshot]" >&2
|
||||||
exit 64 ;;
|
exit 64 ;;
|
||||||
esac
|
esac
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# VPN setup + management — a dedicated System submenu (and the custom/vpn
|
||||||
|
# Waybar indicator's click target). Goes past networkmanager_dmenu (which
|
||||||
|
# only connects existing VPNs) to importing configs and driving Tailscale:
|
||||||
|
# · NetworkManager VPN/WireGuard connections, ● active / ○ inactive, toggled
|
||||||
|
# up/down (networkmanager-group users need no sudo).
|
||||||
|
# · Import a WireGuard .conf or OpenVPN .ovpn (nmcli import; OpenVPN needs the
|
||||||
|
# networkmanager-openvpn plugin, shipped system-side).
|
||||||
|
# · Tailscale (self-gated on the CLI being present = nomarchy.services.tailscale):
|
||||||
|
# status is read-only; up/down/exit-node need root, so they go via a
|
||||||
|
# terminal + sudo (the snapshot-tool pattern), no operator wiring needed.
|
||||||
|
# Secrets stay in NetworkManager's / Tailscale's own store — no new manager.
|
||||||
|
nomarchy-vpn = pkgs.writeShellScriptBin "nomarchy-vpn" ''
|
||||||
|
set -u
|
||||||
|
BACK="↩ Back"
|
||||||
|
|
||||||
|
conn_rows() {
|
||||||
|
nmcli -t -f NAME,TYPE,STATE connection show 2>/dev/null \
|
||||||
|
| awk -F: '$2=="vpn"||$2=="wireguard"{ printf "%s %s\n", ($3=="activated"?"●":"○"), $1 }'
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle_conn() {
|
||||||
|
mark=''${1%% *}; name=''${1#* }
|
||||||
|
if [ "$mark" = "●" ]; then
|
||||||
|
nmcli connection down "$name" >/dev/null 2>&1 \
|
||||||
|
&& notify-send "VPN" "Disconnected $name" || notify-send "VPN" "Could not disconnect $name"
|
||||||
|
else
|
||||||
|
nmcli connection up "$name" >/dev/null 2>&1 \
|
||||||
|
&& notify-send "VPN" "Connected $name" || notify-send "VPN" "Could not connect $name (auth or bad config?)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
import_config() {
|
||||||
|
file=$( { fd -e conf -e ovpn . "$HOME" --type f --hidden \
|
||||||
|
--exclude .git --exclude .cache 2>/dev/null \
|
||||||
|
| sed "s|^$HOME/||"; printf '%s\n' "$BACK"; } \
|
||||||
|
| rofi -dmenu -p "Import VPN config" ) || return
|
||||||
|
{ [ -z "$file" ] || [ "$file" = "$BACK" ]; } && return
|
||||||
|
case "$file" in *.ovpn) type=openvpn ;; *) type=wireguard ;; esac
|
||||||
|
if nmcli connection import type "$type" file "$HOME/$file" >/dev/null 2>&1; then
|
||||||
|
notify-send "VPN" "Imported $type config: $(basename "$file")"
|
||||||
|
else
|
||||||
|
notify-send "VPN" "Import failed ($type plugin missing, or bad file?)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ts_state() { tailscale status --json 2>/dev/null | jq -r '.BackendState // "Unknown"'; }
|
||||||
|
ts_exit_node() {
|
||||||
|
node=$( { printf 'none\n'; tailscale exit-node list 2>/dev/null | awk 'NR>1 && $2 {print $2}'; \
|
||||||
|
printf '%s\n' "$BACK"; } | rofi -dmenu -p "Exit node" ) || return
|
||||||
|
{ [ -z "$node" ] || [ "$node" = "$BACK" ]; } && return
|
||||||
|
[ "$node" = none ] && node=""
|
||||||
|
${cfg.terminal} -e sudo tailscale set --exit-node="$node"
|
||||||
|
}
|
||||||
|
tailscale_menu() {
|
||||||
|
while :; do
|
||||||
|
st=$(ts_state)
|
||||||
|
choice=$( {
|
||||||
|
if [ "$st" = Running ]; then printf 'Disconnect\n'; else printf 'Connect\n'; fi
|
||||||
|
printf 'Exit node…\n'
|
||||||
|
printf '%s\n' "$BACK"
|
||||||
|
} | rofi -dmenu -p "Tailscale ($st)" ) || return
|
||||||
|
case "$choice" in
|
||||||
|
"$BACK"|"") return ;;
|
||||||
|
Connect) ${cfg.terminal} -e sudo tailscale up ;;
|
||||||
|
Disconnect) ${cfg.terminal} -e sudo tailscale down ;;
|
||||||
|
"Exit node…") ts_exit_node ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
have_ts=""
|
||||||
|
command -v tailscale >/dev/null 2>&1 && have_ts=1
|
||||||
|
choice=$( {
|
||||||
|
conn_rows
|
||||||
|
printf 'Import config…\n'
|
||||||
|
[ -n "$have_ts" ] && printf 'Tailscale ›\n'
|
||||||
|
printf '%s\n' "$BACK"
|
||||||
|
} | rofi -dmenu -p VPN ) || exit 0
|
||||||
|
case "$choice" in
|
||||||
|
"$BACK"|"") exec nomarchy-menu system ;;
|
||||||
|
"Import config…") import_config ;;
|
||||||
|
"Tailscale ›") tailscale_menu ;;
|
||||||
|
"● "*|"○ "*) toggle_conn "$choice" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf cfg.rofi.enable {
|
config = lib.mkIf cfg.rofi.enable {
|
||||||
home.packages = [
|
home.packages = [
|
||||||
nomarchy-menu
|
nomarchy-menu
|
||||||
|
nomarchy-vpn # VPN submenu (NM connect/import + Tailscale)
|
||||||
pkgs.fd # files module (fuzzy search over $HOME)
|
pkgs.fd # files module (fuzzy search over $HOME)
|
||||||
pkgs.xdg-utils # xdg-open for the files + web modules
|
pkgs.xdg-utils # xdg-open for the files + web modules
|
||||||
pkgs.nodejs # npx, for the Ask Claude module (claude-code from npm)
|
pkgs.nodejs # npx, for the Ask Claude module (claude-code from npm)
|
||||||
|
|||||||
@@ -51,6 +51,19 @@ let
|
|||||||
[ -n "$next" ] && powerprofilesctl set "$next"
|
[ -n "$next" ] && powerprofilesctl set "$next"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# VPN indicator — shows a shield when a NetworkManager VPN/WireGuard
|
||||||
|
# connection is active OR Tailscale is up; prints nothing otherwise so the
|
||||||
|
# module self-hides (like nightlight/updates). Click opens the VPN submenu.
|
||||||
|
vpnStatus = pkgs.writeShellScriptBin "nomarchy-vpn-status" ''
|
||||||
|
active=$(nmcli -t -f TYPE connection show --active 2>/dev/null | grep -Exm1 'vpn|wireguard')
|
||||||
|
ts=""
|
||||||
|
if command -v tailscale >/dev/null 2>&1; then
|
||||||
|
[ "$(tailscale status --json 2>/dev/null | jq -r '.BackendState // empty')" = Running ] && ts=1
|
||||||
|
fi
|
||||||
|
[ -n "$active" ] || [ -n "$ts" ] || exit 0
|
||||||
|
printf '{"text":"","tooltip":"VPN active (click to manage)","class":"on"}\n'
|
||||||
|
'';
|
||||||
|
|
||||||
# Per-theme override probe.
|
# Per-theme override probe.
|
||||||
assetDir = config.nomarchy.themesDir + "/${t.slug}";
|
assetDir = config.nomarchy.themesDir + "/${t.slug}";
|
||||||
styleOverride = assetDir + "/waybar.css";
|
styleOverride = assetDir + "/waybar.css";
|
||||||
@@ -77,7 +90,7 @@ let
|
|||||||
|
|
||||||
modules-left = [ "hyprland/workspaces" "hyprland/window" ];
|
modules-left = [ "hyprland/workspaces" "hyprland/window" ];
|
||||||
modules-center = [ "clock" ];
|
modules-center = [ "clock" ];
|
||||||
modules-right = [ "tray" "pulseaudio" "cpu" "memory" "custom/powerprofile" "custom/nightlight" ]
|
modules-right = [ "tray" "custom/vpn" "pulseaudio" "cpu" "memory" "custom/powerprofile" "custom/nightlight" ]
|
||||||
++ lib.optional showLanguage "hyprland/language"
|
++ lib.optional showLanguage "hyprland/language"
|
||||||
++ [ "battery" "custom/updates" "custom/notification" ];
|
++ [ "battery" "custom/updates" "custom/notification" ];
|
||||||
|
|
||||||
@@ -131,6 +144,15 @@ let
|
|||||||
on-click = "nomarchy-powerprofile-cycle";
|
on-click = "nomarchy-powerprofile-cycle";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# VPN shield — self-hides unless a NM VPN/WireGuard tunnel or Tailscale is
|
||||||
|
# up. Click opens the VPN submenu (nomarchy-vpn). 5s poll like powerprofile.
|
||||||
|
"custom/vpn" = {
|
||||||
|
exec = "nomarchy-vpn-status";
|
||||||
|
return-type = "json";
|
||||||
|
interval = 5;
|
||||||
|
on-click = "nomarchy-vpn";
|
||||||
|
};
|
||||||
|
|
||||||
# Night-light (hyprsunset) indicator. Self-gates: the moon shows only while
|
# Night-light (hyprsunset) indicator. Self-gates: the moon shows only while
|
||||||
# the schedule runs; otherwise the status helper prints nothing => hidden
|
# the schedule runs; otherwise the status helper prints nothing => hidden
|
||||||
# (enable / re-enable from the System menu). Click toggles instantly — writes
|
# (enable / re-enable from the System menu). Click toggles instantly — writes
|
||||||
@@ -226,11 +248,14 @@ let
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tray, #pulseaudio, #cpu, #memory, #custom-powerprofile, #custom-nightlight, #custom-updates, #language, #battery, #custom-notification {
|
#tray, #pulseaudio, #cpu, #memory, #custom-powerprofile, #custom-nightlight, #custom-updates, #custom-vpn, #language, #battery, #custom-notification {
|
||||||
color: @subtext;
|
color: @subtext;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* VPN active → accent green, reading as "connected / protected". */
|
||||||
|
#custom-vpn.on { color: @good; }
|
||||||
|
|
||||||
/* Night-light active → warm tone, matching the filter it represents. */
|
/* Night-light active → warm tone, matching the filter it represents. */
|
||||||
#custom-nightlight.on { color: @warn; }
|
#custom-nightlight.on { color: @warn; }
|
||||||
|
|
||||||
@@ -285,5 +310,5 @@ in
|
|||||||
# The power-profile helpers on PATH, so both the generated bar and the
|
# The power-profile helpers on PATH, so both the generated bar and the
|
||||||
# whole-swap themes' static waybar.jsonc can exec them by name.
|
# whole-swap themes' static waybar.jsonc can exec them by name.
|
||||||
home.packages = lib.optionals config.nomarchy.waybar.enable
|
home.packages = lib.optionals config.nomarchy.waybar.enable
|
||||||
[ powerProfileStatus powerProfileCycle ];
|
[ powerProfileStatus powerProfileCycle vpnStatus ];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,11 @@ in
|
|||||||
services.fwupd.enable = lib.mkDefault true;
|
services.fwupd.enable = lib.mkDefault true;
|
||||||
|
|
||||||
networking.networkmanager.enable = lib.mkDefault true;
|
networking.networkmanager.enable = lib.mkDefault true;
|
||||||
|
# OpenVPN support for the VPN menu's import/connect flow (nomarchy-vpn).
|
||||||
|
# WireGuard needs no plugin (NetworkManager imports wg .conf natively);
|
||||||
|
# this adds the openvpn type so `nmcli connection import type openvpn` works.
|
||||||
|
# mkDefault so a downstream can drop it to slim the closure.
|
||||||
|
networking.networkmanager.plugins = lib.mkDefault [ pkgs.networkmanager-openvpn ];
|
||||||
|
|
||||||
# No double-unlock on hibernate. Locking the session before sleep is
|
# No double-unlock on hibernate. Locking the session before sleep is
|
||||||
# right for suspend (resumes from RAM, no other gate), but an encrypted
|
# right for suspend (resumes from RAM, no other gate), but an encrypted
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
"modules-left": ["custom/launcher", "clock", "clock#date"],
|
"modules-left": ["custom/launcher", "clock", "clock#date"],
|
||||||
"modules-center": ["hyprland/workspaces"],
|
"modules-center": ["hyprland/workspaces"],
|
||||||
"modules-right": ["pulseaudio", "custom/powerprofile", "custom/nightlight", "custom/updates", "battery", "tray", "custom/notification", "custom/powermenu"],
|
"modules-right": ["pulseaudio", "custom/powerprofile", "custom/nightlight", "custom/updates", "battery", "tray", "custom/vpn", "custom/notification", "custom/powermenu"],
|
||||||
|
|
||||||
"hyprland/workspaces": {
|
"hyprland/workspaces": {
|
||||||
"disable-scroll": true,
|
"disable-scroll": true,
|
||||||
@@ -79,6 +79,13 @@
|
|||||||
"on-click": "nomarchy-powerprofile-cycle"
|
"on-click": "nomarchy-powerprofile-cycle"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"custom/vpn": {
|
||||||
|
"return-type": "json",
|
||||||
|
"interval": 5,
|
||||||
|
"exec": "nomarchy-vpn-status",
|
||||||
|
"on-click": "nomarchy-vpn"
|
||||||
|
},
|
||||||
|
|
||||||
"custom/nightlight": {
|
"custom/nightlight": {
|
||||||
"return-type": "json",
|
"return-type": "json",
|
||||||
"interval": 3,
|
"interval": 3,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
"modules-left": ["custom/nomarchy", "clock", "clock#date"],
|
"modules-left": ["custom/nomarchy", "clock", "clock#date"],
|
||||||
"modules-center": ["hyprland/workspaces"],
|
"modules-center": ["hyprland/workspaces"],
|
||||||
"modules-right": ["idle_inhibitor", "pulseaudio", "custom/powerprofile", "custom/nightlight", "custom/updates", "battery", "tray", "custom/notification", "custom/powermenu"],
|
"modules-right": ["idle_inhibitor", "pulseaudio", "custom/powerprofile", "custom/nightlight", "custom/updates", "battery", "tray", "custom/vpn", "custom/notification", "custom/powermenu"],
|
||||||
|
|
||||||
"hyprland/workspaces": {
|
"hyprland/workspaces": {
|
||||||
"disable-scroll": true,
|
"disable-scroll": true,
|
||||||
@@ -86,6 +86,13 @@
|
|||||||
"on-click": "nomarchy-powerprofile-cycle"
|
"on-click": "nomarchy-powerprofile-cycle"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"custom/vpn": {
|
||||||
|
"return-type": "json",
|
||||||
|
"interval": 5,
|
||||||
|
"exec": "nomarchy-vpn-status",
|
||||||
|
"on-click": "nomarchy-vpn"
|
||||||
|
},
|
||||||
|
|
||||||
"custom/nightlight": {
|
"custom/nightlight": {
|
||||||
"return-type": "json",
|
"return-type": "json",
|
||||||
"interval": 3,
|
"interval": 3,
|
||||||
|
|||||||
Reference in New Issue
Block a user