Customize waybar with Catppuccin Latte theme and niri modules

- Replace sway modules with niri/workspaces, niri/window, niri/language
- Remove battery, backlight, mpd (desktop machine)
- Add custom modules: GPU (nvidia-smi), weather (wttr.in), mail (Thunderbird)
- Add disk, privacy modules
- Catppuccin Latte colors with Mocha dark background
- Wi-Fi signal level icon, bold 15px font
- Scripts for GPU, weather, and mail monitoring

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikhail Kilin
2026-03-01 17:53:49 +03:00
parent d0813ba423
commit 698c953c55
5 changed files with 332 additions and 409 deletions

View File

@@ -1,214 +1,160 @@
// -*- mode: jsonc -*- // -*- mode: jsonc -*-
{ {
// "layer": "top", // Waybar at top layer "layer": "top",
// "position": "bottom", // Waybar position (top|bottom|left|right) "height": 32,
"height": 30, // Waybar height (to be removed for auto height) "spacing": 8,
// "width": 1280, // Waybar width
"spacing": 4, // Gaps between modules (4px)
// Choose the order of the modules
"modules-left": [ "modules-left": [
"sway/workspaces", "niri/workspaces"
"sway/mode",
"sway/scratchpad"
], ],
"modules-center": [ "modules-center": [
"sway/window" "niri/window"
], ],
"modules-right": [ "modules-right": [
"privacy",
"custom/mail",
"idle_inhibitor", "idle_inhibitor",
"custom/weather",
"pulseaudio", "pulseaudio",
"network", "network",
"power-profiles-daemon", "power-profiles-daemon",
"cpu", "cpu",
"memory", "memory",
"temperature", "temperature",
"backlight", "custom/gpu",
"sway/language", "disk",
"battery", "niri/language",
"clock", "clock",
"tray" "tray"
], ],
// Modules configuration
// "sway/workspaces": { "niri/workspaces": {
// "disable-scroll": true, "format": "{index}"
// "all-outputs": true,
// "warp-on-scroll": false,
// "format": "{name}: {icon}",
// "format-icons": {
// "1": "",
// "2": "",
// "3": "",
// "4": "",
// "5": "",
// "urgent": "",
// "focused": "",
// "default": ""
// }
// },
"keyboard-state": {
"numlock": true,
"capslock": true,
"format": "{name} {icon}",
"format-icons": {
"locked": "",
"unlocked": ""
}
}, },
"sway/mode": {
"format": "<span style=\"italic\">{}</span>" "niri/window": {
"format": "{}",
"max-length": 50
}, },
"sway/scratchpad": {
"format": "{icon} {count}", "niri/language": {
"show-empty": false, "format": "{short}"
"format-icons": ["", ""], },
"privacy": {
"icon-spacing": 4,
"icon-size": 16,
"transition-duration": 250,
"modules": [
{ "type": "screenshare" },
{ "type": "audio-in" },
{ "type": "audio-out" }
]
},
"custom/mail": {
"format": "{}",
"return-type": "json",
"exec": "python3 ~/.config/waybar/scripts/mail.py",
"interval": 60,
"tooltip": true, "tooltip": true,
"tooltip-format": "{app}: {title}" "on-click": "flatpak run org.mozilla.Thunderbird"
},
"mpd": {
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ {volume}% ",
"format-disconnected": "Disconnected ",
"format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ",
"unknown-tag": "N/A",
"interval": 5,
"consume-icons": {
"on": " "
},
"random-icons": {
"off": "<span color=\"#f53c3c\"></span> ",
"on": " "
},
"repeat-icons": {
"on": " "
},
"single-icons": {
"on": "1 "
},
"state-icons": {
"paused": "",
"playing": ""
},
"tooltip-format": "MPD (connected)",
"tooltip-format-disconnected": "MPD (disconnected)"
}, },
"idle_inhibitor": { "idle_inhibitor": {
"format": "{icon}", "format": "{icon}",
"format-icons": { "format-icons": {
"activated": "", "activated": "",
"deactivated": "" "deactivated": ""
} }
}, },
"tray": {
// "icon-size": 21, "custom/weather": {
"spacing": 10, "format": "{}",
// "icons": { "return-type": "json",
// "blueman": "bluetooth", "exec": "~/.config/waybar/scripts/weather.sh",
// "TelegramDesktop": "$HOME/.local/share/icons/hicolor/16x16/apps/telegram.png" "interval": 900,
// } "tooltip": true
}, },
"custom/gpu": {
"format": " {}",
"return-type": "json",
"exec": "~/.config/waybar/scripts/gpu.sh",
"interval": 5,
"tooltip": true
},
"disk": {
"format": "{percentage_used}% ",
"path": "/",
"tooltip-format": "{used} / {total} ({percentage_used}%)"
},
"tray": {
"spacing": 10
},
"clock": { "clock": {
// "timezone": "America/New_York",
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>", "tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
"format": "{:%H:%M}",
"format-alt": "{:%Y-%m-%d}" "format-alt": "{:%Y-%m-%d}"
}, },
"cpu": { "cpu": {
"format": "{usage}% ", "format": "{usage}% ",
"tooltip": false "tooltip": false
}, },
"memory": { "memory": {
"format": "{}% " "format": "{}% "
}, },
"temperature": { "temperature": {
// "thermal-zone": 2,
// "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input",
"critical-threshold": 80, "critical-threshold": 80,
// "format-critical": "{temperatureC}°C {icon}",
"format": "{temperatureC}°C {icon}", "format": "{temperatureC}°C {icon}",
"format-icons": ["", "", ""] "format-icons": ["", "", ""]
},
"backlight": {
// "device": "acpi_video1",
"format": "{percent}% {icon}",
"format-icons": ["🌑", "🌘", "🌗", "🌖", "🌕"]
},
"battery": {
"states": {
// "good": 95,
"warning": 30,
"critical": 15
},
"format": "{capacity}% {icon}",
"format-full": "{capacity}% {icon}",
"format-charging": "{capacity}% ",
"format-plugged": "{capacity}% ",
"format-alt": "{time} {icon}",
// "format-good": "", // An empty format will hide the module
// "format-full": "",
"format-icons": ["", "", "", "", ""]
},
"battery#bat2": {
"bat": "BAT2"
},
"power-profiles-daemon": {
"format": "{icon}",
"tooltip-format": "Power profile: {profile}\nDriver: {driver}",
"tooltip": true,
"format-icons": {
"default": "",
"performance": "",
"balanced": "",
"power-saver": ""
}
}, },
"network": { "network": {
// "interface": "wlp2*", // (Optional) To force the use of this interface "format-wifi": "{icon}",
"format-wifi": "{essid} ({signalStrength}%) ", "format-ethernet": "",
"format-ethernet": "{ipaddr}/{cidr} ", "format-icons": ["󰤯", "󰤟", "󰤢", "󰤥", "󰤨"],
"tooltip-format": "{ifname} via {gwaddr} ", "tooltip-format-wifi": "{essid} ({signalStrength}%)\n{ipaddr}/{cidr}",
"format-linked": "{ifname} (No IP) ", "tooltip-format-ethernet": "{ifname}\n{ipaddr}/{cidr}",
"format-disconnected": "Disconnected ⚠", "format-linked": "{ifname} (No IP) ",
"format-alt": "{ifname}: {ipaddr}/{cidr}" "format-disconnected": "󰤭",
"on-click": ""
}, },
"pulseaudio": { "pulseaudio": {
// "scroll-step": 1, // %, can be a float
"format": "{volume}% {icon} {format_source}", "format": "{volume}% {icon} {format_source}",
"format-bluetooth": "{volume}% {icon} {format_source}", "format-bluetooth": "{volume}% {icon} {format_source}",
"format-bluetooth-muted": " {icon} {format_source}", "format-bluetooth-muted": " {icon} {format_source}",
"format-muted": " {format_source}", "format-muted": " {format_source}",
"format-source": "{volume}% ", "format-source": "{volume}% ",
"format-source-muted": "", "format-source-muted": "",
"format-icons": { "format-icons": {
"headphone": "", "headphone": "",
"hands-free": "", "hands-free": "",
"headset": "", "headset": "",
"phone": "", "phone": "",
"portable": "", "portable": "",
"car": "", "car": "",
"default": ["", "", ""] "default": ["", "", ""]
}, },
"on-click": "pavucontrol" "on-click": "pavucontrol"
}, },
"custom/media": {
"format": "{icon} {text}", "power-profiles-daemon": {
"return-type": "json", "format": "{icon}",
"max-length": 40, "tooltip-format": "Power profile: {profile}\nDriver: {driver}",
"tooltip": true,
"format-icons": { "format-icons": {
"spotify": "", "default": "",
"default": "🎜" "performance": "",
}, "balanced": "",
"escape": true, "power-saver": ""
"exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null" // Script in resources folder }
// "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name
},
"custom/power": {
"format" : "⏻ ",
"tooltip": false,
"menu": "on-click",
"menu-file": "$HOME/.config/waybar/power_menu.xml", // Menu file in resources folder
"menu-actions": {
"shutdown": "shutdown",
"reboot": "reboot",
"suspend": "systemctl suspend",
"hibernate": "systemctl hibernate"
}
} }
} }

View File

@@ -0,0 +1,9 @@
#!/bin/bash
data=$(nvidia-smi --query-gpu=utilization.gpu,temperature.gpu --format=csv,noheader,nounits 2>/dev/null)
if [ -z "$data" ]; then
echo '{"text": "N/A", "tooltip": "nvidia-smi not available"}'
exit 0
fi
usage=$(echo "$data" | cut -d',' -f1 | tr -d ' ')
temp=$(echo "$data" | cut -d',' -f2 | tr -d ' ')
echo "{\"text\": \"${usage}% ${temp}°C\", \"tooltip\": \"GPU: ${usage}%\\nTemp: ${temp}°C\", \"class\": \"\"}"

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env python3
import glob
import json
import sqlite3
import os
profile_dirs = glob.glob(
os.path.expanduser("~/.var/app/org.mozilla.Thunderbird/.thunderbird/*.default-esr")
) or glob.glob(
os.path.expanduser("~/.thunderbird/*.default-esr")
)
if not profile_dirs:
print(json.dumps({"text": "", "tooltip": "No Thunderbird profile found"}))
exit()
db_path = os.path.join(profile_dirs[0], "global-messages-db.sqlite")
if not os.path.exists(db_path):
print(json.dumps({"text": "", "tooltip": "No message database"}))
exit()
try:
conn = sqlite3.connect(f"file:{db_path}?mode=ro&nolock=1", uri=True)
cursor = conn.cursor()
cursor.execute(
"SELECT COUNT(*) FROM messages WHERE jsonAttributes LIKE '%\"read\":false%' OR jsonAttributes LIKE '%\"read\": false%'"
)
count = cursor.fetchone()[0]
conn.close()
except Exception:
# fallback: try folderStatus
try:
conn = sqlite3.connect(f"file:{db_path}?mode=ro&nolock=1", uri=True)
cursor = conn.cursor()
cursor.execute("SELECT SUM(numNewMessages) FROM folderlocations WHERE numNewMessages > 0")
row = cursor.fetchone()
count = row[0] if row and row[0] else 0
conn.close()
except Exception:
count = 0
if count > 0:
print(json.dumps({"text": f"󰇮 {count}", "tooltip": f"{count} unread emails", "class": "unread"}))
else:
print(json.dumps({"text": "󰇮", "tooltip": "No unread emails"}))

View File

@@ -0,0 +1,8 @@
#!/bin/bash
weather=$(curl -s "wttr.in/?format=%c%t" 2>/dev/null)
if [ -z "$weather" ] || echo "$weather" | grep -q "Unknown"; then
echo '{"text": "N/A", "tooltip": "Weather unavailable"}'
exit 0
fi
tooltip=$(curl -s "wttr.in/?format=%l:+%C+%t+%w+%h" 2>/dev/null)
echo "{\"text\": \"${weather}\", \"tooltip\": \"${tooltip}\"}"

View File

@@ -1,12 +1,40 @@
/* Catppuccin Latte */
@define-color rosewater #dc8a78;
@define-color flamingo #dd7878;
@define-color pink #ea76cb;
@define-color mauve #8839ef;
@define-color red #d20f39;
@define-color maroon #e64553;
@define-color peach #fe640b;
@define-color yellow #df8e1d;
@define-color green #40a02b;
@define-color teal #179299;
@define-color sky #04a5e5;
@define-color sapphire #209fb5;
@define-color blue #1e66f5;
@define-color lavender #7287fd;
@define-color text #4c4f69;
@define-color subtext1 #5c5f77;
@define-color subtext0 #6c6f85;
@define-color overlay2 #7c7f93;
@define-color overlay1 #8c8fa1;
@define-color overlay0 #9ca0b0;
@define-color surface2 #acb0be;
@define-color surface1 #bcc0cc;
@define-color surface0 #ccd0da;
@define-color base #eff1f5;
@define-color mantle #e6e9ef;
@define-color crust #dce0e8;
* { * {
font-family: 'Noto Sans Mono', 'Font Awesome 6 Free', 'Font Awesome 6 Brands', monospace; font-family: 'JetBrainsMono Nerd Font', 'Noto Sans Mono', monospace;
font-size: 13px; font-size: 15px;
font-weight: bold;
} }
window#waybar { window#waybar {
background-color: rgba(43, 48, 59, 0.5); background-color: #1e1e2e;
border-bottom: 3px solid rgba(100, 114, 125, 0.5); color: @text;
color: #ffffff;
transition-property: background-color; transition-property: background-color;
transition-duration: .5s; transition-duration: .5s;
} }
@@ -15,86 +43,56 @@ window#waybar.hidden {
opacity: 0.2; opacity: 0.2;
} }
/*
window#waybar.empty {
background-color: transparent;
}
window#waybar.solo {
background-color: #FFFFFF;
}
*/
window#waybar.termite {
background-color: #3F3F3F;
}
window#waybar.chromium {
background-color: #000000;
border: none;
}
button { button {
/* Use box-shadow instead of border so the text isn't offset */
box-shadow: inset 0 -3px transparent; box-shadow: inset 0 -3px transparent;
/* Avoid rounded borders under each button name */
border: none; border: none;
border-radius: 0; border-radius: 0;
} }
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
button:hover { button:hover {
background: inherit; background: inherit;
box-shadow: inset 0 -3px #ffffff; box-shadow: inset 0 -2px @blue;
}
/* you can set a style on hover for any module like this */
#pulseaudio:hover {
background-color: #a37800;
} }
/* Workspaces */
#workspaces button { #workspaces button {
padding: 0 5px; padding: 0 6px;
background-color: transparent; background-color: transparent;
color: #ffffff; color: #a6adc8;
} }
#workspaces button:hover { #workspaces button:hover {
background: rgba(0, 0, 0, 0.2); background: alpha(@surface0, 0.5);
} }
#workspaces button.focused { #workspaces button.active {
background-color: #64727D; color: #cdd6f4;
box-shadow: inset 0 -3px #ffffff; box-shadow: inset 0 -2px @blue;
} }
#workspaces button.urgent { #workspaces button.urgent {
background-color: #eb4d4b; background-color: alpha(@red, 0.2);
} color: @red;
#mode {
background-color: #64727D;
box-shadow: inset 0 -3px #ffffff;
} }
/* Common module styling */
#clock, #clock,
#battery,
#cpu, #cpu,
#memory, #memory,
#disk, #disk,
#temperature, #temperature,
#backlight,
#network, #network,
#pulseaudio, #pulseaudio,
#wireplumber, #wireplumber,
#custom-media,
#tray, #tray,
#mode,
#idle_inhibitor, #idle_inhibitor,
#scratchpad,
#power-profiles-daemon, #power-profiles-daemon,
#mpd { #language,
#custom-gpu,
#custom-weather,
#privacy {
padding: 0 10px; padding: 0 10px;
color: #ffffff; color: @text;
} }
#window, #window,
@@ -102,139 +100,135 @@ button:hover {
margin: 0 4px; margin: 0 4px;
} }
/* If workspaces is the leftmost module, omit left margin */ #window {
.modules-left > widget:first-child > #workspaces { color: #cdd6f4;
margin-left: 0;
}
/* If workspaces is the rightmost module, omit right margin */
.modules-right > widget:last-child > #workspaces {
margin-right: 0;
} }
/* Clock */
#clock { #clock {
background-color: #64727D; color: @blue;
} font-weight: bold;
#battery {
background-color: #ffffff;
color: #000000;
}
#battery.charging, #battery.plugged {
color: #ffffff;
background-color: #26A65B;
}
@keyframes blink {
to {
background-color: #ffffff;
color: #000000;
}
}
/* Using steps() instead of linear as a timing function to limit cpu usage */
#battery.critical:not(.charging) {
background-color: #f53c3c;
color: #ffffff;
animation-name: blink;
animation-duration: 0.5s;
animation-timing-function: steps(12);
animation-iteration-count: infinite;
animation-direction: alternate;
}
#power-profiles-daemon {
padding-right: 15px;
}
#power-profiles-daemon.performance {
background-color: #f53c3c;
color: #ffffff;
}
#power-profiles-daemon.balanced {
background-color: #2980b9;
color: #ffffff;
}
#power-profiles-daemon.power-saver {
background-color: #2ecc71;
color: #000000;
}
label:focus {
background-color: #000000;
} }
/* CPU */
#cpu { #cpu {
background-color: #2ecc71; color: @green;
color: #000000;
} }
/* Memory */
#memory { #memory {
background-color: #9b59b6; color: @mauve;
}
#disk {
background-color: #964B00;
}
#backlight {
background-color: #90b1b1;
}
#network {
background-color: #2980b9;
}
#network.disconnected {
background-color: #f53c3c;
}
#pulseaudio {
background-color: #f1c40f;
color: #000000;
}
#pulseaudio.muted {
background-color: #90b1b1;
color: #2a5c45;
}
#wireplumber {
background-color: #fff0f5;
color: #000000;
}
#wireplumber.muted {
background-color: #f53c3c;
}
#custom-media {
background-color: #66cc99;
color: #2a5c45;
min-width: 100px;
}
#custom-media.custom-spotify {
background-color: #66cc99;
}
#custom-media.custom-vlc {
background-color: #ffa000;
} }
/* Temperature */
#temperature { #temperature {
background-color: #f0932b; color: @peach;
} }
#temperature.critical { #temperature.critical {
background-color: #eb4d4b; color: @red;
} }
/* Network */
#network {
color: @sapphire;
font-size: 20px;
}
#network.disconnected {
color: @red;
}
/* Pulseaudio */
#pulseaudio {
color: @yellow;
}
#pulseaudio.muted {
color: @overlay1;
}
/* Language */
#language {
color: @teal;
font-weight: bold;
}
/* Idle inhibitor */
#idle_inhibitor {
color: @overlay1;
}
#idle_inhibitor.activated {
color: @peach;
}
/* Power profiles */
#power-profiles-daemon {
color: @subtext0;
}
#power-profiles-daemon.performance {
color: @red;
}
#power-profiles-daemon.balanced {
color: @blue;
}
#power-profiles-daemon.power-saver {
color: @green;
}
/* Mail */
#custom-mail {
color: @lavender;
}
#custom-mail.unread {
color: @red;
}
/* Disk */
#disk {
color: @flamingo;
}
/* GPU */
#custom-gpu {
color: @green;
}
/* Weather */
#custom-weather {
color: @sky;
}
/* Privacy */
#privacy {
padding: 0;
}
#privacy-item {
padding: 0 5px;
color: @base;
}
#privacy-item.screenshare {
background-color: @peach;
}
#privacy-item.audio-in {
background-color: @red;
}
#privacy-item.audio-out {
background-color: @blue;
}
/* Tray */
#tray { #tray {
background-color: #2980b9; color: @text;
} }
#tray > .passive { #tray > .passive {
@@ -243,84 +237,5 @@ label:focus {
#tray > .needs-attention { #tray > .needs-attention {
-gtk-icon-effect: highlight; -gtk-icon-effect: highlight;
background-color: #eb4d4b; color: @red;
}
#idle_inhibitor {
background-color: #2d3436;
}
#idle_inhibitor.activated {
background-color: #ecf0f1;
color: #2d3436;
}
#mpd {
background-color: #66cc99;
color: #2a5c45;
}
#mpd.disconnected {
background-color: #f53c3c;
}
#mpd.stopped {
background-color: #90b1b1;
}
#mpd.paused {
background-color: #51a37a;
}
#language {
background: #00b093;
color: #740864;
padding: 0 5px;
margin: 0 5px;
min-width: 16px;
}
#keyboard-state {
background: #97e1ad;
color: #000000;
padding: 0 0px;
margin: 0 5px;
min-width: 16px;
}
#keyboard-state > label {
padding: 0 5px;
}
#keyboard-state > label.locked {
background: rgba(0, 0, 0, 0.2);
}
#scratchpad {
background: rgba(0, 0, 0, 0.2);
}
#scratchpad.empty {
background-color: transparent;
}
#privacy {
padding: 0;
}
#privacy-item {
padding: 0 5px;
color: white;
}
#privacy-item.screenshare {
background-color: #cf5700;
}
#privacy-item.audio-in {
background-color: #1ca000;
}
#privacy-item.audio-out {
background-color: #0069d4;
} }