{ lib, config, inputs, pkgs, ... }: let cfg = config.waybar; isLaptop = cfg.profile == "laptop"; palette = if cfg.theme == "mocha" then { base = "#1E1E2E"; mantle = "#181825"; crust = "#11111B"; text = "#CDD6F4"; subtext0 = "#A6ADC8"; green = "#A6E3A1"; yellow = "#F9E2AF"; sky = "#89DCEB"; rose = "#F38BA8"; blue = "#89B4FA"; surface2 = "#585B70"; peach = "#FAB387"; } else { base = "#EFF1F5"; mantle = "#E6E9EF"; crust = "#DCE0E8"; text = "#4C4F69"; subtext0 = "#6C6F85"; green = "#40A02B"; yellow = "#DF8E1D"; sky = "#04A5E5"; rose = "#D20F39"; blue = "#1E66F5"; surface2 = "#ACB0BE"; peach = "#FE640B"; }; waybarConfig = { layer = "top"; position = "top"; margin-top = 10; margin-left = 14; margin-right = 14; spacing = 12; modules-left = [ "hyprland/workspaces" ]; modules-right = if isLaptop then [ "mpris" "bluetooth" "pulseaudio" "upower" "custom/tailscale" "custom/netbird" "network" "clock" ] else [ "mpris" "pulseaudio" "bluetooth" "custom/tailscale" "custom/netbird" "network" "clock" ]; mpris = { format = "{player_icon} {title} - {artist}"; format-paused = " {player_icon} {title}"; format-stopped = ""; player-icons = { default = ""; Feishin = ""; }; max-length = if isLaptop then 45 else 50; interval = 1; tooltip = true; tooltip-format = "{player} - {status}\n{artist}\n{title}\n{album}"; on-click = if isLaptop then "playerctl -p Feishin play-pause" else "playerctl play-pause"; } // lib.optionalAttrs isLaptop { player = "Feishin"; on-scroll-up = "playerctl -p Feishin next"; on-scroll-down = "playerctl -p Feishin previous"; }; bluetooth = { format = " {status}"; format-connected = " {device_alias}"; format-connected-battery = " {device_alias} {device_battery_percentage}%"; tooltip-format = "{controller_alias}\t{controller_address}\n\n{num_connections} connected"; tooltip-format-connected = "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}"; tooltip-format-enumerate-connected = "{device_alias}\t{device_address}"; tooltip-format-enumerate-connected-battery = "{device_alias}\t{device_address}\t{device_battery_percentage}%"; on-click = "pgrep .blueman-manage && pkill .blueman-manage || blueman-manager &"; }; network = { interface = if isLaptop then "wlp0s20f3" else [ "enp195s0" "enp196s0" ]; format = "{ifname}"; format-wifi = " {essid} ({signalStrength}%)"; format-ethernet = if isLaptop then "{ifname} " else " {ifname}"; format-disconnected = "no signal"; tooltip-format = "{ifname}"; tooltip-format-wifi = "{essid} ({signalStrength}%) "; tooltip-format-ethernet = "{ifname} "; tooltip-format-disconnected = "Disconnected"; max-length = 50; }; upower = { icon-size = 20; hide-if-empty = true; tooltip = true; tooltip-spacing = 20; }; pulseaudio = { format = "{icon} {volume}%"; format-bluetooth = "{icon}  {volume}%"; format-muted = ""; format-icons = { headphones = ""; handsfree = ""; headset = ""; phone = ""; portable = ""; car = ""; default = [ "" "" ]; }; on-click = "pgrep pavucontrol && pkill pavucontrol || pavucontrol &"; }; clock = { format = "{:%c}"; locale = "sv_SE.UTF-8"; tooltip = true; tooltip-format = "{:%A, %d %B %Y}\n %H:%M:%S"; interval = 1; }; "custom/tailscale" = { exec = "/home/fw/.config/waybar/scripts/tailscale.sh --status"; on-click = "/home/fw/.config/waybar/scripts/tailscale.sh --toggle"; exec-on-event = true; format = "TS {icon} {text}"; format-icons = { connected = ""; stopped = ""; connecting = ""; disconnecting = ""; }; tooltip = true; return-type = "json"; interval = 5; }; "custom/netbird" = { exec = "/home/fw/.config/waybar/scripts/netbird.sh --status"; on-click = "/home/fw/.config/waybar/scripts/netbird.sh --toggle"; exec-on-event = true; format = "NB {icon} {text}"; format-icons = { connected = ""; stopped = ""; connecting = ""; disconnecting = ""; }; tooltip = true; return-type = "json"; interval = 5; }; }; waybarStyle = '' @define-color base ${palette.base}; @define-color mantle ${palette.mantle}; @define-color crust ${palette.crust}; @define-color text ${palette.text}; @define-color subtext0 ${palette.subtext0}; @define-color green ${palette.green}; @define-color yellow ${palette.yellow}; @define-color sky ${palette.sky}; @define-color rose ${palette.rose}; @define-color blue ${palette.blue}; @define-color surface2 ${palette.surface2}; @define-color peach ${palette.peach}; * { font-family: "FiraCode Nerd Font", "Font Awesome 7 Free"; font-size: 15px; } .connecting { animation: pulse 0.8s ease-in-out infinite; } @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } } window#waybar { background: transparent; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35); transition: background-color .35s ease, color .35s ease; } window#waybar.hidden { opacity: 0.2; } window#waybar.termite { background: @base; } window#waybar.chromium { background: @base; border: none; } #workspaces, #clock, #mpris, #pulseaudio, #bluetooth, #upower, #network, #custom-tailscale, #custom-netbird { background: @base; border: none; border-radius: 5px; padding: 3px 8px; } .modules-left > widget, .modules-right > widget { margin: 0 15px; } .modules-left > widget:first-child { margin-left: 0; } .modules-right > widget:last-child { margin-right: 0; } button { border: none; border-radius: 8px; background: transparent; color: @text; transition: background-color .2s ease, box-shadow .2s ease; box-shadow: inset 0 -3px transparent; } button:hover { background: rgba(255, 255, 255, 0.08); box-shadow: inset 0 -3px @peach; } button:active { padding-top: 1px; padding-bottom: 1px; box-shadow: inset 0 -2px @peach; color: @base; } #workspaces { background: @base; } #workspaces button { padding: 2px 8px; margin: 0 2px; border-radius: 8px; transition: background-color .2s ease, box-shadow .2s ease; } #workspaces button:hover { background: rgba(255, 255, 255, 0.08); } #workspaces button.active, #workspaces button.focused { background: @peach; color: @base; box-shadow: inset 0 -3px @peach; } #workspaces button.urgent { background: @rose; color: @base; box-shadow: inset 0 -3px rgba(0, 0, 0, 0.2); } #clock, #mpris, #pulseaudio, #bluetooth, #upower, #custom-tailscale, #custom-netbird, #network { background: @peach; color: @base; font-weight: 600; letter-spacing: 0.2px; } @keyframes blink { to { background-color: @peach; color: @base; } } ''; in with lib; { options = { waybar = { enable = mkEnableOption "enables waybar"; profile = lib.mkOption { type = lib.types.enum [ "desktop" "laptop" ]; default = "desktop"; description = "Layout profile for modules and device-specific settings"; }; theme = lib.mkOption { type = lib.types.enum [ "latte" "mocha" ]; default = "latte"; description = "Color theme used for Waybar style generation"; }; }; }; config = mkIf config.waybar.enable { programs = { waybar = { enable = true; systemd = { enable = true; }; }; }; xdg.configFile."waybar/config.jsonc".text = builtins.toJSON waybarConfig; xdg.configFile."waybar/style.css".text = waybarStyle; xdg.configFile."waybar/scripts/tailscale.sh" = { text = '' #!${pkgs.bash}/bin/bash PATH=${ lib.makeBinPath [ pkgs.coreutils pkgs.jq pkgs.tailscale ] } STATE_FILE="/tmp/waybar_tailscale_state" CONNECTING_DURATION=2 # seconds to show transition states get_tailscale_status() { local status_json if status_json=$(tailscale status --json 2>/dev/null); then local backend_state=$(echo "$status_json" | jq -r '.BackendState // "NoState"') case "$backend_state" in "Running") echo "connected" ;; "Stopped"|"NoState"|"NeedsLogin") echo "stopped" ;; *) echo "stopped" ;; esac else echo "stopped" fi } get_tooltip() { local status_json if ! status_json=$(tailscale status --json 2>/dev/null); then echo "" return fi local backend_state=$(echo "$status_json" | jq -r '.BackendState // "NoState"') if [[ "$backend_state" != "Running" ]]; then echo "" return fi local hostname=$(echo "$status_json" | jq -r '.Self.HostName // "Unknown"') local tooltip="Hostname: $hostname\n" tooltip+="\nPeers:\n" local peers=$(echo "$status_json" | jq -r '.Peer // {} | to_entries[] | "\(.value.HostName):\(.value.Online)"' | sort) if [[ -n "$peers" ]]; then while IFS=: read -r peer_name peer_online; do if [[ "$peer_online" == "true" ]]; then tooltip+="\n $peer_name" else tooltip+="\n $peer_name" fi done <<< "$peers" else tooltip+="\nNo peers" fi echo "$tooltip" } show_status() { local status=$(get_tailscale_status) local text="" local alt="" local tooltip="" case $status in "connected") text="" alt="connected" tooltip=$(get_tooltip) ;; "stopped") text="" alt="stopped" tooltip="Tailscale is turned off" ;; esac if [[ -n "$tooltip" ]]; then echo "{\"text\":\"$text\",\"class\":\"$status\",\"alt\":\"$alt\",\"tooltip\":\"$tooltip\"}" else echo "{\"text\":\"$text\",\"class\":\"$status\",\"alt\":\"$alt\"}" fi } show_connecting() { echo "{\"text\":\"\",\"class\":\"connecting\",\"alt\":\"connecting\",\"tooltip\":\"Connecting...\"}" } show_disconnecting() { echo "{\"text\":\"\",\"class\":\"disconnecting\",\"alt\":\"disconnecting\",\"tooltip\":\"Disconnecting...\"}" } is_in_transition() { if [[ -f "$STATE_FILE" ]]; then local state_info=$(cat "$STATE_FILE") local state_time=$(echo "$state_info" | cut -d: -f1) local current_time=$(date +%s) if (( current_time - state_time < CONNECTING_DURATION )); then return 0 else rm -f "$STATE_FILE" return 1 fi fi return 1 } case "$1" in --status) if is_in_transition; then state_info=$(cat "$STATE_FILE") state_action=$(echo "$state_info" | cut -d: -f2) if [[ "$state_action" == "connecting" ]]; then show_connecting exit 0 elif [[ "$state_action" == "disconnecting" ]]; then show_disconnecting exit 0 fi fi show_status ;; --toggle) if is_in_transition; then state_info=$(cat "$STATE_FILE") state_action=$(echo "$state_info" | cut -d: -f2) if [[ "$state_action" == "connecting" ]]; then show_connecting elif [[ "$state_action" == "disconnecting" ]]; then show_disconnecting fi exit 0 fi current_status=$(get_tailscale_status) if [[ "$current_status" == "connected" ]]; then tailscale down show_status else echo "$(date +%s):connecting" > "$STATE_FILE" tailscale up show_connecting fi ;; *) echo "Usage: $0 {--status|--toggle}" exit 1 ;; esac ''; executable = true; }; xdg.configFile."waybar/scripts/netbird.sh" = { text = '' #!${pkgs.bash}/bin/bash PATH=${ lib.makeBinPath [ pkgs.coreutils pkgs.netbird ] } STATE_FILE="/tmp/waybar_netbird_state" CONNECTING_DURATION=2 # seconds to show transition states get_netbird_status() { local status_output local management_status="" if ! status_output=$(netbird status -d 2>/dev/null); then echo "stopped" return fi while IFS= read -r line; do case "$line" in "Management:"*) management_status="''${line#Management: }" ;; esac done <<< "$status_output" case "$management_status" in *Connected*) echo "connected" ;; *) echo "stopped" ;; esac } get_tooltip() { local status_output local management_status="" local signal_status="" local relays_available=0 local relays_total=0 local nameservers_available=0 local nameservers_total=0 local netbird_ip="" local peers_count="" local interface_type="" local section="" if ! status_output=$(netbird status -d 2>/dev/null); then echo "" return fi while IFS= read -r line; do case "$line" in "Management:"*) management_status="''${line#Management: }" section="" ;; "Signal:"*) signal_status="''${line#Signal: }" section="" ;; "Relays:"*) section="relays" ;; "Nameservers:"*) section="nameservers" ;; "NetBird IP:"*) netbird_ip="''${line#NetBird IP: }" section="" ;; "Interface type:"*) interface_type="''${line#Interface type: }" section="" ;; "Peers count:"*) peers_count="''${line#Peers count: }" section="" ;; "Peers detail:"*) section="" ;; " ["*) if [[ "$section" == "relays" ]]; then relays_total=$((relays_total + 1)) if [[ "$line" == *"Available"* ]]; then relays_available=$((relays_available + 1)) fi elif [[ "$section" == "nameservers" ]]; then nameservers_total=$((nameservers_total + 1)) if [[ "$line" == *"Available"* ]]; then nameservers_available=$((nameservers_available + 1)) fi fi ;; esac done <<< "$status_output" if [[ -z "$management_status" ]]; then echo "" return fi local tooltip="Management: $management_status" if [[ -n "$signal_status" ]]; then tooltip+="\nSignal: $signal_status" fi if (( relays_total > 0 )); then tooltip+="\nRelays: $relays_available/$relays_total Available" fi if (( nameservers_total > 0 )); then tooltip+="\nNameservers: $nameservers_available/$nameservers_total Available" fi if [[ -n "$netbird_ip" ]]; then tooltip+="\nNetBird IP: $netbird_ip" fi if [[ -n "$interface_type" ]]; then tooltip+="\nInterface: $interface_type" fi if [[ -n "$peers_count" ]]; then tooltip+="\nPeers: $peers_count" fi echo "$tooltip" } show_status() { local status=$(get_netbird_status) local text="" local alt="" local tooltip="" case $status in "connected") text="" alt="connected" tooltip=$(get_tooltip) ;; "stopped") text="" alt="stopped" tooltip="NetBird is turned off" ;; esac if [[ -n "$tooltip" ]]; then echo "{\"text\":\"$text\",\"class\":\"$status\",\"alt\":\"$alt\",\"tooltip\":\"$tooltip\"}" else echo "{\"text\":\"$text\",\"class\":\"$status\",\"alt\":\"$alt\"}" fi } show_connecting() { echo "{\"text\":\"\",\"class\":\"connecting\",\"alt\":\"connecting\",\"tooltip\":\"Connecting...\"}" } show_disconnecting() { echo "{\"text\":\"\",\"class\":\"disconnecting\",\"alt\":\"disconnecting\",\"tooltip\":\"Disconnecting...\"}" } is_in_transition() { if [[ -f "$STATE_FILE" ]]; then local state_info=$(cat "$STATE_FILE") local state_time=$(echo "$state_info" | cut -d: -f1) local current_time=$(date +%s) if (( current_time - state_time < CONNECTING_DURATION )); then return 0 else rm -f "$STATE_FILE" return 1 fi fi return 1 } case "$1" in --status) if is_in_transition; then state_info=$(cat "$STATE_FILE") state_action=$(echo "$state_info" | cut -d: -f2) if [[ "$state_action" == "connecting" ]]; then show_connecting exit 0 elif [[ "$state_action" == "disconnecting" ]]; then show_disconnecting exit 0 fi fi show_status ;; --toggle) if is_in_transition; then state_info=$(cat "$STATE_FILE") state_action=$(echo "$state_info" | cut -d: -f2) if [[ "$state_action" == "connecting" ]]; then show_connecting elif [[ "$state_action" == "disconnecting" ]]; then show_disconnecting fi exit 0 fi current_status=$(get_netbird_status) if [[ "$current_status" == "connected" ]]; then netbird down show_status else echo "$(date +%s):connecting" > "$STATE_FILE" netbird up show_connecting fi ;; *) echo "Usage: $0 {--status|--toggle}" exit 1 ;; esac ''; executable = true; }; }; # catppuccin.waybar = { # enable = true; # flavor = "latte"; # }; }