{ outputs, config, lib, pkgs, inputs, ... }: let commonDeps = with pkgs; [coreutils gnugrep systemd]; # Function to simplify making waybar outputs mkScript = { name ? "script", deps ? [], script ? "", }: lib.getExe (pkgs.writeShellApplication { inherit name; text = script; runtimeInputs = commonDeps ++ deps; }); # Specialized for JSON outputs mkScriptJson = { name ? "script", deps ? [], pre ? "", text ? "", tooltip ? "", alt ? "", class ? "", percentage ? "", }: mkScript { inherit name; deps = [pkgs.jq] ++ deps; script = '' ${pre} jq -cn \ --arg text "${text}" \ --arg tooltip "${tooltip}" \ --arg alt "${alt}" \ --arg class "${class}" \ --arg percentage "${percentage}" \ '{text:$text,tooltip:$tooltip,alt:$alt,class:$class,percentage:$percentage}' ''; }; hyprlandCfg = config.wayland.windowManager.hyprland; in { # Let it try to start a few more times systemd.user.services.waybar = { Unit.StartLimitBurst = 30; }; programs.waybar = { enable = true; package = pkgs.waybar.overrideAttrs (oa: { mesonFlags = (oa.mesonFlags or []) ++ ["-Dexperimental=true"]; }); systemd.enable = true; settings = { primary = { exclusive = false; passthrough = false; height = 46; margin = "6"; position = "top"; modules-left = [ "custom/menu" "custom/currentplayer" "custom/player" "hyprland/workspaces" "hyprland/submap" ]; modules-center = [ "cpu" "memory" "disk" "clock" "pulseaudio" "battery" "idle_inhibitor" # "custom/unread-mail" ]; modules-right = [ # "custom/gammastep" TODO: currently broken for some reason "network" "bluetooth" "tray" "privacy" "custom/webcam" "custom/hostname" ]; clock = { interval = 1; format = "{:%d/%m %H:%M:%S}"; format-alt = "{:%Y-%m-%d %H:%M:%S %z}"; on-click-left = "mode"; tooltip-format = '' {:%Y %B} {calendar}''; }; cpu = { format = " {usage}%"; }; memory = { format = " {}%"; interval = 5; }; disk = { interval = 5; unit = "GB"; format = "󰋊 {percentage_used}%"; }; pulseaudio = { format = "{icon} {volume}%"; format-muted = " 0%"; format-icons = { headphone = "󰋋 "; headset = "󰋎 "; portable = " "; default = [ " " " " " " ]; }; on-click = lib.getExe pkgs.pavucontrol; }; idle_inhibitor = { format = "{icon}"; format-icons = { activated = "󰅶 "; deactivated = "󰾫 "; }; tooltip-format-activated = "Caffinated! device will not sleep."; tooltip-format-deactivated = "no caffeine :( device will sleep when not in use."; }; battery = { interval = 5; bat = "BAT1"; # full-at = 94; format = "{icon} {capacity}%"; format-icons = [ "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹" ]; states = { battery-10 = 10; battery-20 = 20; battery-30 = 30; battery-40 = 40; battery-50 = 50; battery-60 = 60; battery-70 = 70; battery-80 = 80; battery-90 = 90; battery-100 = 100; }; # # the wiki lies about this, does not match # /sys/class/power_supply/BAT0/status format-plugged = "󰚥 AC"; format-charging-battery-10 = "󰢜 {capacity}%"; format-charging-battery-20 = "󰂆 {capacity}%"; format-charging-battery-30 = "󰂇 {capacity}%"; format-charging-battery-40 = "󰂈 {capacity}%"; format-charging-battery-50 = "󰢝 {capacity}%"; format-charging-battery-60 = "󰂉 {capacity}%"; format-charging-battery-70 = "󰢞 {capacity}%"; format-charging-battery-80 = "󰂊 {capacity}%"; format-charging-battery-90 = "󰂋 {capacity}%"; format-charging-battery-100 = "󰂅 {capacity}%"; }; "hyprland/workspaces" = { format = "{icon} {windows}"; window-rewrite-default = " "; window-rewrite-seperator = ""; window-rewrite = { "title<.*github.*>" = "󰊤"; "title<.*youtube.*>" = ""; "title<*Gmail*>" = "󰊫"; "class" = ""; "class" = ""; "title" = ""; "alacritty" = ""; "code" = "󰨞"; "slack" = "󰒱"; "initialtitle" = "󰓇"; "class" = "󰉓"; "class" = ""; }; }; network = { interval = 3; format-wifi = "{icon} {essid}"; format-icons = [ "󰤟" "󰤢" "󰤥" "󰤨" ]; format-ethernet = "󰈁 Connected"; format-disconnected = "󱐤 "; tooltip-format = '' {ifname} {ipaddr}/{cidr} Up: {bandwidthUpBits} Down: {bandwidthDownBits}''; on-click = mkScript { deps = [pkgs.wpa_supplicant pkgs.notify-desktop]; script = ''wpa_cli reconnect; notify-desktop "reconnecting to wifi" -t 1200''; }; }; bluetooth = { format-on = "󰂯"; format-off = "󰂲"; format-disabled = "󰂲"; format-connected = "󰂱 {num_connections}"; format-connected-battery = "󰂱 {device_alias} ({device_battery_percentage}%) ({num_connections})"; on-click = "overskride"; }; "custom/menu" = { interval = 1; return-type = "json"; exec = mkScriptJson { deps = lib.optional hyprlandCfg.enable hyprlandCfg.package; text = " "; tooltip = ''$(grep PRETTY_NAME /etc/os-release | cut -d '"' -f2)''; class = let isFullScreen = if hyprlandCfg.enable then "hyprctl activewindow -j | jq -e '.fullscreen' &>/dev/null" else "false"; in "$(if ${isFullScreen}; then echo fullscreen; fi)"; }; }; "custom/hostname" = { exec = mkScript {script = ''echo "$USER@$HOSTNAME"'';}; on-click = mkScript {script = "systemctl --user restart waybar";}; }; "custom/unread-mail" = { interval = 5; return-type = "json"; exec = mkScriptJson { deps = [pkgs.findutils pkgs.procps]; pre = '' count=$(find ~/Mail/*/Inbox/new -type f | wc -l) if pgrep mbsync &>/dev/null; then status="syncing" else if [ "$count" == "0" ]; then status="read" else status="unread" fi fi ''; text = "$count"; alt = "$status"; }; format = "{icon} ({})"; format-icons = { "read" = "󰇯"; "unread" = "󰇮"; "syncing" = "󰁪"; }; }; "custom/currentplayer" = { interval = 2; return-type = "json"; exec = mkScriptJson { deps = [pkgs.playerctl]; pre = '' player="$(playerctl status -f "{{playerName}}" 2>/dev/null || echo "No player active" | cut -d '.' -f1)" count="$(playerctl -l 2>/dev/null | wc -l)" if ((count > 1)); then more=" +$((count - 1))" else more="" fi ''; alt = "$player"; tooltip = "$player ($count available)"; text = "$more"; }; format = "{icon}{}"; format-icons = { "No player active" = " "; "Celluloid" = "󰎁 "; "spotify" = "󰓇 "; "ncspot" = "󰓇 "; "qutebrowser" = "󰖟 "; "firefox" = " "; "discord" = " 󰙯 "; "sublimemusic" = " "; "kdeconnect" = "󰄡 "; "chromium" = " "; }; }; privacy = { "icon-spacing" = 0; "icon-size" = 18; "transition-duration" = 250; modules = [ { type = "screenshare"; tooltip = true; "tooltip-icon-size" = 24; } { type = "audio-out"; tooltip = true; "tooltip-icon-size" = 24; } { type = "audio-in"; tooltip = true; "tooltip-icon-size" = 24; } ]; }; "custom/webcam" = { return-type = "json"; interval = 2; exec = mkScript { deps = [pkgs.jq pkgs.psmisc]; script = '' # get programs using the video0 endpoint ps -eo user,pid,cmd -q "$(fuser /dev/video0 2>/dev/null | xargs)" |\ # omit the column headings and the first line which is wireplumber sed -n "1,2!p" |\ # just get the pid and program columns awk '{print $2 " " $3}' |\ # filter out the program path awk -F "/" '{print "{\"tooltip\": \"" $1 " " $NF "\"}"}' |\ jq -s 'if length > 0 then {text: "󰄀 ", tooltip: (map(.tooltip) | join("\r"))} else {text: "", tooltip: ""} end' |\ jq --unbuffered --compact-output ''; }; }; "custom/player" = { exec-if = mkScript { deps = [pkgs.playerctl]; script = "playerctl status 2>/dev/null"; }; exec = let format = ''{"text": "{{title}} - {{artist}}", "alt": "{{status}}", "tooltip": "{{title}} - {{artist}} ({{album}})"}''; in mkScript { deps = [pkgs.playerctl]; script = "playerctl metadata --format '${format}' 2>/dev/null"; }; return-type = "json"; interval = 2; max-length = 30; format = "{icon} {}"; format-icons = { "Playing" = "󰐊"; "Paused" = "󰏤 "; "Stopped" = "󰓛"; }; on-click = mkScript { deps = [pkgs.playerctl]; script = "playerctl play-pause"; }; }; }; }; # Cheatsheet: # x -> all sides # x y -> vertical, horizontal # x y z -> top, horizontal, bottom # w x y z -> top, right, bottom, left style = let inherit (inputs.nix-colors.lib.conversions) hexToRGBString; inherit (config.colorscheme) colors; toRGBA = color: opacity: "rgba(${hexToRGBString "," (lib.removePrefix "#" color)},${opacity})"; in /* css */ '' * { font-family: Fira Sans, FiraCode Nerd Font; font-size: 12pt; padding: 0; margin: 0 0.4em; } window#waybar { padding: 0; border-radius: 0.5em; background-color: shade(@surface0, 0.7); color: @surface2 } .modules-left { margin-left: -0.65em; } .modules-right { margin-right: -0.65em; } #workspaces button { background-color: @surface0; color: @surface2; padding-left: 0.2em; padding-right: 0.2em; margin-left: 0.25em; margin-right: 0.25em; margin-top: 0.4em; margin-bottom: 0.4em; } #workspaces button.hidden { background-color: @surface0; color: @surface2; } #workspaces button.focused, #workspaces button.active { background-color: shade(@blue, 0.7); color: @green; } #workspaces button:hover { background-color: @surface3; color: @surface1; } #privacy-item { margin-left: 0.1em; margin-right: 0.1em; } #clock { padding-right: 1em; padding-left: 1em; border-radius: 0.5em; } #custom-menu { background-color: @surface3; color: @blue; padding-right: 1.5em; padding-left: 1em; margin-right: 0; border-radius: 0.5em; } #custom-menu.fullscreen { background-color: @blue; color: @green; } #custom-hostname { background-color: @surface3; color: @blue; padding-right: 1em; padding-left: 1em; margin-left: 0; border-radius: 0.5em; } #custom-currentplayer { padding-right: 0; } #custom-gpu, #cpu, #memory { margin-left: 0.05em; margin-right: 0.55em; } ''; }; }