feat(waybar): add config
This commit is contained in:
179
waybar/config.jsonc
Normal file
179
waybar/config.jsonc
Normal 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
12
waybar/scripts/mic-status.sh
Executable 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
|
||||
14
waybar/scripts/notification-status.sh
Executable file
14
waybar/scripts/notification-status.sh
Executable 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
21
waybar/scripts/ssh-session.sh
Executable 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
40
waybar/scripts/vpn-status.sh
Executable 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
232
waybar/style.css
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user