From 4cc10a22ed55d43fbbf1c966d5f23e7b3346bb76 Mon Sep 17 00:00:00 2001 From: alexion Date: Sun, 11 Oct 2026 17:53:44 -0400 Subject: [PATCH] refactor(waybar): register workspace window icons --- .../nixos/modules/desktop/apps/firefox.nix | 5 +++ .../nixos/modules/desktop/apps/ghostty.nix | 5 +++ .../nixos/modules/desktop/apps/obsidian.nix | 5 +++ configs/nixos/modules/desktop/apps/signal.nix | 5 +++ configs/nixos/modules/desktop/apps/steam.nix | 5 +++ .../nixos/modules/desktop/apps/vencord.nix | 5 +++ configs/nixos/modules/desktop/default.nix | 11 +++++ .../nixos/modules/desktop/session/waybar.nix | 44 ++++++++----------- configs/nixos/registry/window-icon.nix | 25 +++++++++++ 9 files changed, 84 insertions(+), 26 deletions(-) create mode 100644 configs/nixos/registry/window-icon.nix diff --git a/configs/nixos/modules/desktop/apps/firefox.nix b/configs/nixos/modules/desktop/apps/firefox.nix index f67a774..5021d13 100644 --- a/configs/nixos/modules/desktop/apps/firefox.nix +++ b/configs/nixos/modules/desktop/apps/firefox.nix @@ -26,6 +26,11 @@ in }; config = lib.mkIf cfg.enable { + registry.window-icon.${config.networking.hostName}.firefox = { + class = ".*[Ff]irefox.*"; + icon = "f269"; + }; + home-manager.users.${config.user.name} = hm: { programs.firefox = { enable = true; diff --git a/configs/nixos/modules/desktop/apps/ghostty.nix b/configs/nixos/modules/desktop/apps/ghostty.nix index 045d553..05d2ba6 100644 --- a/configs/nixos/modules/desktop/apps/ghostty.nix +++ b/configs/nixos/modules/desktop/apps/ghostty.nix @@ -20,6 +20,11 @@ in }; }; + registry.window-icon.${config.networking.hostName}.ghostty = { + class = "com.mitchellh.ghostty"; + icon = "f120"; + }; + home-manager.users.${user}.home.packages = [ pkgs.ghostty ]; }; } diff --git a/configs/nixos/modules/desktop/apps/obsidian.nix b/configs/nixos/modules/desktop/apps/obsidian.nix index dae280d..fb018cd 100644 --- a/configs/nixos/modules/desktop/apps/obsidian.nix +++ b/configs/nixos/modules/desktop/apps/obsidian.nix @@ -14,5 +14,10 @@ in config = lib.mkIf config.modules.desktop.apps.obsidian.enable { home-manager.users.${user}.home.packages = [ pkgs.obsidian ]; + + registry.window-icon.${config.networking.hostName}.obsidian = { + class = "obsidian"; + icon = "f02d"; + }; }; } diff --git a/configs/nixos/modules/desktop/apps/signal.nix b/configs/nixos/modules/desktop/apps/signal.nix index 27862d8..a28387f 100644 --- a/configs/nixos/modules/desktop/apps/signal.nix +++ b/configs/nixos/modules/desktop/apps/signal.nix @@ -13,5 +13,10 @@ in config = lib.mkIf config.modules.desktop.apps.signal.enable { home-manager.users.${user}.home.packages = [ pkgs.signal-desktop ]; + + registry.window-icon.${config.networking.hostName}.signal = { + class = "[Ss]ignal"; + icon = "f4ad"; + }; }; } diff --git a/configs/nixos/modules/desktop/apps/steam.nix b/configs/nixos/modules/desktop/apps/steam.nix index 2fa3c42..6b111ed 100644 --- a/configs/nixos/modules/desktop/apps/steam.nix +++ b/configs/nixos/modules/desktop/apps/steam.nix @@ -23,5 +23,10 @@ environment.systemPackages = [ pkgs.gamescope ]; + + registry.window-icon.${config.networking.hostName}.steam = { + class = "[Ss]team"; + icon = "f1b6"; + }; }; } diff --git a/configs/nixos/modules/desktop/apps/vencord.nix b/configs/nixos/modules/desktop/apps/vencord.nix index 9d8f098..79d9824 100644 --- a/configs/nixos/modules/desktop/apps/vencord.nix +++ b/configs/nixos/modules/desktop/apps/vencord.nix @@ -14,5 +14,10 @@ in config = lib.mkIf config.modules.desktop.apps.vencord.enable { home-manager.users.${user}.home.packages = [ pkgs.vesktop ]; + + registry.window-icon.${config.networking.hostName}.vesktop = { + class = "[Vv]esktop"; + icon = "f1ff"; + }; }; } diff --git a/configs/nixos/modules/desktop/default.nix b/configs/nixos/modules/desktop/default.nix index 800cc22..bfbfded 100644 --- a/configs/nixos/modules/desktop/default.nix +++ b/configs/nixos/modules/desktop/default.nix @@ -24,5 +24,16 @@ modules.desktop.utilities.theming.enable = lib.mkDefault true; modules.desktop.utilities.userdirs.enable = lib.mkDefault true; registry.env.${config.networking.hostName}.NIXOS_OZONE_WL = "1"; + registry.window-icon.${config.networking.hostName} = { + chromium = { + class = "chromium.*"; + icon = "f268"; + }; + + code = { + class = "[Cc]ode"; + icon = "f121"; + }; + }; }; } diff --git a/configs/nixos/modules/desktop/session/waybar.nix b/configs/nixos/modules/desktop/session/waybar.nix index d5a5b8d..76447d0 100644 --- a/configs/nixos/modules/desktop/session/waybar.nix +++ b/configs/nixos/modules/desktop/session/waybar.nix @@ -11,7 +11,6 @@ let # A Nerd Font Private-Use-Area codepoint as a literal character, decoded # through JSON so the glyph survives as bytes rather than an editor paste. g = code: builtins.fromJSON ''"\u${code}"''; - user = config.user.name; sleepOffIcon = builtins.fromJSON ''"\udb81\udcb3"''; @@ -19,7 +18,7 @@ let # With no mako daemon answering, the status reads "notifications on", keeping # the widget inert rather than broken. dndStatus = pkgs.writeShellScript "waybar-dnd-status" '' - if ${pkgs.mako}/bin/makoctl mode 2>/dev/null | ${pkgs.gnugrep}/bin/grep -qx dnd; then + if ${lib.getExe' pkgs.mako "makoctl"} mode 2>/dev/null | ${lib.getExe pkgs.gnugrep} -qx dnd; then printf '{"text":"${g "f1f6"}","tooltip":"Do not disturb: on","class":"dnd"}\n' else printf '{"text":"${g "f0f3"}","tooltip":"Notifications on","class":"active"}\n' @@ -27,14 +26,14 @@ let ''; dndToggle = pkgs.writeShellScript "waybar-dnd-toggle" '' - ${pkgs.mako}/bin/makoctl mode -t dnd >/dev/null 2>&1 - ${pkgs.procps}/bin/pkill -RTMIN+8 waybar + ${lib.getExe' pkgs.mako "makoctl"} mode -t dnd >/dev/null 2>&1 + ${lib.getExe' pkgs.procps "pkill"} -RTMIN+8 waybar ''; # Lit while a wf-recorder capture is running. # Empty text collapses the widget when idle, so it shows nothing until then. recStatus = pkgs.writeShellScript "waybar-recording-status" '' - if ${pkgs.procps}/bin/pgrep -x wf-recorder >/dev/null; then + if ${lib.getExe' pkgs.procps "pgrep"} -x wf-recorder >/dev/null; then printf '{"text":"${g "f03d"}","tooltip":"Recording","class":"recording"}\n' else printf '{"text":"","class":"idle"}\n' @@ -42,7 +41,7 @@ let ''; idleLockStatus = pkgs.writeShellScript "waybar-idle-lock-status" '' - if ${pkgs.systemd}/bin/systemctl --user is-active --quiet lockscreen-idle-inhibit.service; then + if ${lib.getExe' pkgs.systemd "systemctl"} --user is-active --quiet lockscreen-idle-inhibit.service; then printf '{"text":"${sleepOffIcon}","tooltip":"Idle locking inhibited","class":"inhibited"}\n' else printf '{"text":"","class":"idle"}\n' @@ -52,14 +51,14 @@ let vpnStatus = pkgs.writeShellScript "waybar-vpn-status" '' interface=${lib.escapeShellArg vpnClient.interface} - if ! ${pkgs.iproute2}/bin/ip link show dev "$interface" >/dev/null 2>&1; then + if ! ${lib.getExe' pkgs.iproute2 "ip"} link show dev "$interface" >/dev/null 2>&1; then printf '{"text":"","class":"idle"}\n' exit 0 fi - ipaddr=$(${pkgs.iproute2}/bin/ip -4 -o addr show dev "$interface" | ${pkgs.gawk}/bin/awk '{ print $4; exit }') + ipaddr=$(${lib.getExe' pkgs.iproute2 "ip"} -4 -o addr show dev "$interface" | ${lib.getExe' pkgs.gawk "awk"} '{ print $4; exit }') - ${pkgs.jq}/bin/jq -cn \ + ${lib.getExe pkgs.jq} -cn \ --arg text "󰲝" \ --arg tooltip "VPN $interface: ''${ipaddr:-no IPv4}" \ --arg class "active" \ @@ -68,12 +67,12 @@ let networkStatus = pkgs.writeShellScript "waybar-network-status" '' active=$( - ${pkgs.networkmanager}/bin/nmcli -t -f DEVICE,TYPE,STATE,CONNECTION device status \ - | ${pkgs.gawk}/bin/awk -F: '$3 == "connected" && $2 != "loopback" { print; exit }' + ${lib.getExe' pkgs.networkmanager "nmcli"} -t -f DEVICE,TYPE,STATE,CONNECTION device status \ + | ${lib.getExe' pkgs.gawk "awk"} -F: '$3 == "connected" && $2 != "loopback" { print; exit }' ) if [ -z "$active" ]; then - ${pkgs.jq}/bin/jq -cn \ + ${lib.getExe pkgs.jq} -cn \ --arg text "${g "f127"} off" \ --arg tooltip "Network disconnected" \ --arg class "disconnected" \ @@ -89,9 +88,9 @@ let *) icon="NET" ;; esac - ipaddr=$(${pkgs.iproute2}/bin/ip -4 -o addr show dev "$device" | ${pkgs.gawk}/bin/awk '{ print $4; exit }') + ipaddr=$(${lib.getExe' pkgs.iproute2 "ip"} -4 -o addr show dev "$device" | ${lib.getExe' pkgs.gawk "awk"} '{ print $4; exit }') - ${pkgs.jq}/bin/jq -cn \ + ${lib.getExe pkgs.jq} -cn \ --arg text "$icon $connection" \ --arg tooltip "$device: ''${ipaddr:-no IPv4}" \ --arg class "$type" \ @@ -106,7 +105,7 @@ in # codepoints Stylix's monospace font does not carry. fonts.packages = [ pkgs.nerd-fonts.symbols-only ]; - home-manager.users.${user}.programs.waybar = { + home-manager.users.${config.user.name}.programs.waybar = { enable = true; # A user service bound to graphical-session.target, which uwsm activates, @@ -151,16 +150,9 @@ in format = "{id} {windows}"; format-window-separator = " "; window-rewrite-default = g "f2d0"; - window-rewrite = { - "class" = g "f120"; - "class<.*[Ff]irefox.*>" = g "f269"; - "class" = g "f268"; - "class<[Cc]ode>" = g "f121"; - "class" = g "f02d"; - "class<[Ss]ignal>" = g "f4ad"; - "class<[Ss]team>" = g "f1b6"; - "class<[Vv]esktop>" = g "f1ff"; - }; + window-rewrite = lib.mapAttrs' ( + _name: windowIcon: lib.nameValuePair "class<${windowIcon.class}>" (g windowIcon.icon) + ) (lib.attrByPath [ config.networking.hostName ] { } config.registry.window-icon); }; clock = { @@ -190,7 +182,7 @@ in (g "f027") (g "f028") ]; - on-click = "${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"; + on-click = "${lib.getExe' pkgs.wireplumber "wpctl"} set-mute @DEFAULT_AUDIO_SINK@ toggle"; }; "custom/vpn" = { diff --git a/configs/nixos/registry/window-icon.nix b/configs/nixos/registry/window-icon.nix new file mode 100644 index 0000000..f91c6d8 --- /dev/null +++ b/configs/nixos/registry/window-icon.nix @@ -0,0 +1,25 @@ +{ lib, util, ... }: +{ + checks = [ + { + assertion = util.registry.predicates.uniquePerHost (entry: entry.value.class); + message = "Window icons must use unique class patterns per host."; + } + ]; + + type = lib.types.submodule { + options = { + class = lib.mkOption { + description = "Matches the window class reported by the compositor."; + type = lib.types.str; + example = ".*[Ff]irefox.*"; + }; + + icon = lib.mkOption { + description = "Sets the Nerd Font codepoint shown for matching windows."; + type = lib.types.str; + example = "f269"; + }; + }; + }; +}