feat(waybar): add config

This commit is contained in:
2026-03-12 17:43:03 +01:00
parent f82afb4611
commit d7cb42a64c
6 changed files with 498 additions and 0 deletions

179
waybar/config.jsonc Normal file
View File

@@ -0,0 +1,179 @@
[
{
"layer": "top",
"position": "top",
"height": 30,
"spacing": 1,
"margin": 0,
"modules-left": [
"sway/workspaces",
"sway/mode"
],
"modules-center": [
"custom/ssh",
"custom/notification",
"systemd-failed-units"
],
"modules-right": [
"mpris",
"custom/vpn",
"cpu",
"memory",
"battery",
"wireplumber",
"custom/mic",
"backlight",
"sway/language",
"bluetooth",
"network",
"clock"
],
"sway/workspaces": {
"disable-scroll": true,
"format": "{name}"
},
"sway/mode": {
"format": "{}"
},
"sway/language": {
"format": "{short}",
"on-click": "swaymsg input type:keyboard xkb_switch_layout next",
"on-click-right": "~/.config/eww/scripts/popup.sh keyboard-popup"
},
"custom/ssh": {
"format": "{}",
"return-type": "json",
"interval": 15,
"exec": "~/.config/waybar/scripts/ssh-session.sh",
"on-click": "~/.config/waybar/scripts/ssh-session.sh disconnect"
},
"custom/notification": {
"format": "{}",
"return-type": "json",
"exec": "~/.config/waybar/scripts/notification-status.sh",
"on-click": "sleep 0.1 && swaync-client -t -sw",
"on-click-right": "sleep 0.1 && swaync-client -d -sw",
"escape": true
},
"systemd-failed-units": {
"hide-on-ok": true,
"format": "!{nr_failed}",
"format-ok": "",
"system": true,
"user": true,
"on-click": "ghostty -e sh -c 'systemctl --failed; systemctl --user --failed; read'"
},
// right: media
"mpris": {
"format": "{artist} - {title}",
"format-paused": "{artist} - {title} [paused]",
"format-stopped": "",
"max-length": 35,
"tooltip-format": "{player}: {artist} - {title} ({album})",
"on-click": "playerctl play-pause",
"on-click-right": "~/.config/eww/scripts/popup.sh media-popup",
"on-scroll-up": "playerctl next",
"on-scroll-down": "playerctl previous"
},
// right: connectivity
"network": {
"interval": 5,
"format-ethernet": "󰈀 {ipaddr}",
"format-wifi": "󰖩 {ipaddr}",
"format-linked": "󰈀 (no ip)",
"format-disconnected": "󰖪",
"tooltip-format": "{ifname} {ipaddr}/{cidr}\n{gwaddr}\n{bandwidthUpBits}up {bandwidthDownBits}down",
"on-click-right": "~/.config/eww/scripts/popup.sh network-popup"
},
"bluetooth": {
"format": "󰂯",
"format-connected": "󰂯 {num_connections}",
"format-connected-battery": "󰂯 {num_connections}",
"tooltip-format-connected": "{device_enumerate}",
"on-click-right": "~/.config/eww/scripts/popup.sh bluetooth-popup"
},
"custom/vpn": {
"format": "{}",
"return-type": "json",
"interval": 10,
"exec": "~/.config/waybar/scripts/vpn-status.sh",
"on-click-right": "~/.config/eww/scripts/popup.sh vpn-popup"
},
"cpu": {
"format": "󰻠 {usage}%",
"tooltip": false,
"on-click-right": "~/.config/eww/scripts/popup.sh system-popup"
},
"memory": {
"interval": 10,
"format": "󰍛 {percentage}%",
"tooltip-format": "total: {total:0.2f}GiB\nused: {used:0.2f}GiB\navailable: {avail:0.2f}GiB\nswap: {swapUsed:0.2f}/{swapTotal:0.2f}GiB",
"on-click-right": "~/.config/eww/scripts/popup.sh system-popup"
},
// right: battery
"battery": {
"states": {
"warning": 30,
"critical": 15
},
"format": "󰁹 {capacity}%",
"format-charging": "󰂄 {capacity}%",
"format-plugged": "󰚥 {capacity}%",
"format-full": "󰁹 full",
"format-alt": "{time}",
"tooltip-format": "{timeTo}\n{power}W",
"on-click": "p=$(powerprofilesctl get); case $p in power-saver) n=balanced;; balanced) n=performance;; *) n=power-saver;; esac; powerprofilesctl set $n",
"on-click-right": "~/.config/eww/scripts/popup.sh battery-popup"
},
// right: audio
"wireplumber": {
"format": "󰕾 {volume}%",
"format-muted": "󰖁 muted",
"on-click": "pamixer -t",
"on-click-right": "~/.config/eww/scripts/popup.sh volume-popup",
"on-scroll-up": "pamixer -i 5",
"on-scroll-down": "pamixer -d 5",
"tooltip-format": "{node_name}: {volume}%"
},
"custom/mic": {
"format": "{}",
"return-type": "json",
"interval": 2,
"exec": "~/.config/waybar/scripts/mic-status.sh",
"on-click": "pamixer --default-source -t",
"on-click-right": "~/.config/eww/scripts/popup.sh volume-popup",
"on-scroll-up": "pamixer --default-source -i 5",
"on-scroll-down": "pamixer --default-source -d 5"
},
"backlight": {
"format": "󰃟 {percent}%",
"tooltip": false,
"on-click-right": "~/.config/eww/scripts/popup.sh volume-popup"
},
// right: clock
"clock": {
"interval": 1,
"format": "{:%d/%m %H:%M:%S}",
"tooltip-format": "<big><tt>{calendar}</tt></big>"
}
}
]

12
waybar/scripts/mic-status.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# mic volume for waybar custom module
vol=$(pamixer --default-source --get-volume 2>/dev/null || echo 0)
muted=$(pamixer --default-source --get-mute 2>/dev/null || echo false)
if [[ "$muted" == "true" ]]; then
printf '{"text": "󰍭", "class": "muted", "tooltip": "mic muted"}\n'
else
printf '{"text": "󰍬 %d%%", "class": "", "tooltip": "mic %d%%"}\n' "$vol" "$vol"
fi

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
# wrap swaync subscription, hide when no notifications
swaync-client -swb 2>/dev/null | while read -r line; do
count=$(echo "$line" | jq -r '.text // "0"')
class=$(echo "$line" | jq -r '.class // "none"')
if [[ "$count" == "0" ]]; then
echo '{"text": "", "class": "none"}'
else
jq -nc --arg text "󰂚 $count" --arg class "$class" '{$text,$class}'
fi
done

21
waybar/scripts/ssh-session.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# detect remote ssh sessions and optionally disconnect them
# outputs waybar JSON; empty when no remote sessions
if [[ "$1" == "disconnect" ]]; then
pkill -HUP -f 'sshd-session:.*@' 2>/dev/null
exit 0
fi
count=$(pgrep -cf 'sshd-session:.*@' 2>/dev/null || echo 0)
if [[ "$count" -gt 0 ]]; then
# get remote session details
sessions=$(who 2>/dev/null | awk '$NF ~ /\([0-9]/ {gsub(/[()]/, "", $NF); print $1 "@" $NF}')
tooltip=${sessions:-"$count remote sessions"}
# replace newlines with \n for valid JSON
tooltip=${tooltip//$'\n'/\\n}
tooltip=${tooltip//\"/\\\"}
printf '{"text": "●", "class": "active", "tooltip": "%s"}\n' "$tooltip"
fi

40
waybar/scripts/vpn-status.sh Executable file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
# check wireguard and tailscale status
# outputs waybar JSON; empty when inactive
parts=()
tooltip_parts=()
if command -v wg &>/dev/null; then
ifaces=$(wg show interfaces 2>/dev/null)
if [[ -n "$ifaces" ]]; then
parts+=("󰖂")
tooltip_parts+=("wireguard: $ifaces")
fi
fi
if command -v tailscale &>/dev/null; then
ts_json=$(tailscale status --json 2>/dev/null)
state=$(jq -r '.BackendState // empty' <<<"$ts_json")
if [[ "$state" == "Running" ]]; then
ts_ip=$(jq -r '.TailscaleIPs[0] // empty' <<<"$ts_json")
ts_name=$(jq -r '.Self.HostName // empty' <<<"$ts_json")
ts_exit=$(jq -r '.ExitNodeStatus.ID // empty' <<<"$ts_json")
parts+=("󰒒")
tip="tailscale: ${ts_name} ${ts_ip}"
if [[ -n "$ts_exit" ]]; then
tip="$tip (exit node)"
fi
tooltip_parts+=("$tip")
fi
fi
if [[ ${#parts[@]} -gt 0 ]]; then
text="${parts[*]}"
tip=$(printf '%s\\n' "${tooltip_parts[@]}")
# strip trailing \n
tip=${tip%\\n}
jq -nc --arg text "$text" --arg tooltip "$tip" --arg class "active" \
'{$text,$class,$tooltip}'
fi

232
waybar/style.css Normal file
View File

@@ -0,0 +1,232 @@
/* gruvbox-material-soft-dark palette
* mapped from ghostty/themes/gruvbox-material-soft-dark
* ansi 0=#252423 1=#ea6962 2=#a9b665 3=#d8a657
* 4=#7daea3 5=#d3869b 6=#89b482 7=#d4be98
* bg=#32302f fg=#d4be98 selection=#45403d */
@define-color bg #32302f;
@define-color bg_dim #252423;
@define-color bg_sel #45403d;
@define-color fg #d4be98;
@define-color red #ea6962;
@define-color green #a9b665;
@define-color yellow #d8a657;
@define-color blue #7daea3;
@define-color magenta #d3869b;
@define-color cyan #89b482;
@define-color gray #928374;
* {
font-family: "JetBrainsMono Nerd Font";
font-size: 14px;
font-weight: bold;
}
/* bar: dark background, modules float inside */
window#waybar {
background-color: @bg_dim;
color: @fg;
}
window#waybar.hidden {
opacity: 0.2;
}
button {
box-shadow: none;
border: none;
border-radius: 0;
}
button:hover {
background: inherit;
box-shadow: none;
}
/* workspaces */
#workspaces {
background-color: @bg;
border-radius: 8px;
margin: 3px 2px;
padding: 0;
}
#workspaces button {
padding: 0 8px;
background-color: transparent;
color: @gray;
margin: 0;
border-radius: 8px;
}
#workspaces button:hover {
background-color: @bg_sel;
color: @fg;
box-shadow: none;
}
#workspaces button.focused {
background-color: @bg_sel;
color: @fg;
box-shadow: none;
}
#workspaces button.urgent {
background-color: @red;
color: @bg_dim;
}
/* floating pill style for all modules */
#mode,
#clock,
#battery,
#cpu,
#memory,
#backlight,
#network,
#wireplumber,
#mpris,
#bluetooth,
#language,
#custom-mic,
#custom-vpn,
#custom-ssh,
#custom-notification,
#systemd-failed-units {
background-color: @bg;
border-radius: 8px;
color: @fg;
padding: 0 10px;
margin: 3px 2px;
}
/* center: alert zone */
#custom-ssh.active {
color: @cyan;
font-size: 20px;
}
#custom-notification.notification {
color: @yellow;
}
#custom-notification.dnd-notification {
color: @red;
}
#custom-notification.dnd-none {
color: @gray;
}
#systemd-failed-units {
color: @red;
}
/* right: media (hidden by default, visible only when playing/paused) */
#mpris {
padding: 0;
margin: 0;
min-width: 0;
background-color: transparent;
color: @green;
}
#mpris.playing {
padding: 0 10px;
margin: 3px 2px;
background-color: @bg;
}
#mpris.paused {
padding: 0 10px;
margin: 3px 2px;
background-color: @bg;
color: @gray;
}
/* right: connectivity */
#network.disconnected {
color: @red;
}
#bluetooth {
color: @blue;
}
#custom-vpn {
color: @cyan;
}
/* right: system */
#cpu {
margin-right: 0;
border-radius: 8px 0 0 8px;
}
#memory {
margin-left: 0;
border-radius: 0 8px 8px 0;
}
/* right: battery */
#battery.charging,
#battery.plugged {
color: @green;
}
#battery.warning:not(.charging) {
color: @yellow;
}
@keyframes blink {
to {
background-color: @red;
color: @bg_dim;
}
}
#battery.critical:not(.charging) {
color: @red;
animation-name: blink;
animation-duration: 0.5s;
animation-timing-function: steps(12);
animation-iteration-count: infinite;
animation-direction: alternate;
}
/* right: audio */
#wireplumber.muted {
color: @red;
}
#custom-mic.muted {
color: @red;
}
/* right: audio group (volume + mic + brightness) */
#wireplumber {
margin-right: 0;
border-radius: 8px 0 0 8px;
}
#custom-mic {
margin-left: 0;
margin-right: 0;
border-radius: 0;
}
#backlight {
margin-left: 0;
border-radius: 0 8px 8px 0;
}
/* right: clock */
#clock {
color: @fg;
}
/* mode highlight */
#mode {
color: @bg_dim;
background-color: @yellow;
padding: 0 10px;
}