From 5358188ad176d312ca9476d300b3825fabbd367d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jalil=20David=20Salam=C3=A9=20Messina?=
 <jalil.salame@gmail.com>
Date: Mon, 21 Apr 2025 14:01:24 +0200
Subject: [PATCH] refactor(hm/gui): reorganize modules

This way we are not `import`ing stuff which is slow.

It also makes it easier to turn off the sway module.
---
 justfile                           |   4 +-
 modules/hm/gui/default.nix         |  40 +----
 modules/hm/gui/keybindings.nix     | 118 ---------------
 modules/hm/gui/sway-config.nix     | 101 -------------
 modules/hm/gui/sway.nix            | 226 +++++++++++++++++++++++++++++
 modules/hm/gui/waybar-settings.nix | 127 ----------------
 modules/hm/gui/waybar.nix          | 159 ++++++++++++++++++++
 7 files changed, 392 insertions(+), 383 deletions(-)
 delete mode 100644 modules/hm/gui/keybindings.nix
 delete mode 100644 modules/hm/gui/sway-config.nix
 create mode 100644 modules/hm/gui/sway.nix
 delete mode 100644 modules/hm/gui/waybar-settings.nix
 create mode 100644 modules/hm/gui/waybar.nix

diff --git a/justfile b/justfile
index 4021089..483e46c 100644
--- a/justfile
+++ b/justfile
@@ -3,10 +3,10 @@ default:
 
 # Update a specific flake input
 update input:
-    nix flake lock --update-input {{input}} --commit-lock-file
+    nix flake lock --update-input "{{input}}" --commit-lock-file
 
 build-vm:
-    nixos-rebuild build-vm --flake .#vm --print-build-logs
+    nixos-rebuild build-vm --fallback --flake .#vm --print-build-logs
 
 run-vm: build-vm
     QEMU_OPTS="$QEMU_OPTS_WL" result/bin/run-nixos-vm
diff --git a/modules/hm/gui/default.nix b/modules/hm/gui/default.nix
index bc12a65..75d6cea 100644
--- a/modules/hm/gui/default.nix
+++ b/modules/hm/gui/default.nix
@@ -21,6 +21,11 @@ let
   };
 in
 {
+  imports = [
+    ./sway.nix
+    ./waybar.nix
+  ];
+
   config = lib.mkIf (jhome.enable && cfg.enable) {
     home.packages =
       (with pkgs; [
@@ -73,30 +78,6 @@ in
       };
       # Text editor
       nixvim.clipboard.providers.wl-copy.enable = lib.mkDefault true;
-      # Status bar
-      waybar = {
-        enable = true;
-        systemd.enable = true;
-        settings = lib.mkIf config.jhome.styling.enable (
-          import ./waybar-settings.nix { inherit config lib; }
-        );
-        # Style overrides to highlight workspaces with windows
-        style =
-          lib.pipe
-            # css
-            ''
-              .modules-left #workspaces button {
-                border-bottom: 3px solid @base01;
-              }
-              .modules-left #workspaces button.persistent {
-                border-bottom: 3px solid transparent;
-              }
-            ''
-            [
-              (lib.optionalString config.jhome.styling.enable)
-              lib.mkAfter
-            ];
-      };
       # Terminal
       wezterm = {
         enable = cfg.terminal == "wezterm";
@@ -173,17 +154,6 @@ in
       };
     };
 
-    # Window Manager
-    wayland.windowManager.sway = {
-      inherit (cfg.sway) enable;
-      package = swayPkg; # no sway package if it comes from the OS
-      config = import ./sway-config.nix { inherit config pkgs; };
-      systemd = {
-        enable = true;
-        xdgAutostart = true;
-      };
-    };
-
     # Set cursor style
     stylix = lib.mkIf config.jhome.styling.enable { inherit cursor; };
     home.pointerCursor = lib.mkIf config.jhome.styling.enable (
diff --git a/modules/hm/gui/keybindings.nix b/modules/hm/gui/keybindings.nix
deleted file mode 100644
index cfc8788..0000000
--- a/modules/hm/gui/keybindings.nix
+++ /dev/null
@@ -1,118 +0,0 @@
-{ pkgs, config }:
-let
-  cfg = config.jhome.gui.sway;
-  passmenu = "${pkgs.jpassmenu}/bin/jpassmenu";
-  selectAudio = "${pkgs.audiomenu}/bin/audiomenu";
-  swayconf = config.wayland.windowManager.sway.config;
-  mod = swayconf.modifier;
-  workspaces = map toString [
-    1
-    2
-    3
-    4
-    5
-    6
-    7
-    8
-    9
-  ];
-  dirs =
-    map
-      (dir: {
-        key = swayconf.${dir};
-        arrow = dir;
-        direction = dir;
-      })
-      [
-        "up"
-        "down"
-        "left"
-        "right"
-      ];
-  joinKeys = builtins.concatStringsSep "+";
-  # Generate a keybind from a modifier prefix and a key
-  keycombo = prefix: key: joinKeys (prefix ++ [ key ]);
-  modKeybind = keycombo [ mod ];
-  modCtrlKeybind = keycombo [
-    mod
-    "Ctrl"
-  ];
-  modShiftKeybind = keycombo [
-    mod
-    "Shift"
-  ];
-  modCtrlShiftKeybind = keycombo [
-    mod
-    "Ctrl"
-    "Shift"
-  ];
-  dir2resize.up = "resize grow height";
-  dir2resize.down = "resize shrink height";
-  dir2resize.right = "resize grow width";
-  dir2resize.left = "resize shrink width";
-  # Bind a key combo to an action
-  genKeybind = prefix: action: key: { "${prefix key}" = "${action key}"; };
-  genKey =
-    prefix: action: genKeybind ({ key, ... }: prefix key) ({ direction, ... }: action direction);
-  genArrow =
-    prefix: action: genKeybind ({ arrow, ... }: prefix arrow) ({ direction, ... }: action direction);
-  genArrowAndKey =
-    prefix: action: key:
-    (genKey prefix action key) // (genArrow prefix action key);
-  # Move window
-  moveWindowKeybinds = map (genArrowAndKey modShiftKeybind (dir: "move ${dir}")) dirs;
-  # Focus window
-  focusWindowKeybinds = map (genArrowAndKey modKeybind (dir: "focus ${dir}")) dirs;
-  # Resize window
-  resizeWindowKeybinds = map (genArrowAndKey modCtrlKeybind (dir: dir2resize.${dir})) dirs;
-  # Move container to workspace
-  moveWorkspaceKeybindings = map (genKeybind modShiftKeybind (
-    number: "move container to workspace number ${number}"
-  )) workspaces;
-  # Focus workspace
-  focusWorkspaceKeybindings = map (genKeybind modKeybind (
-    number: "workspace number ${number}"
-  )) workspaces;
-  # Move container to Workspace and focus on it
-  moveFocusWorkspaceKeybindings = map (genKeybind modCtrlShiftKeybind (
-    number: "move container to workspace number ${number}; workspace number ${number}"
-  )) workspaces;
-in
-builtins.foldl' (l: r: l // r)
-  {
-    "${mod}+Return" = "exec ${swayconf.terminal}";
-    "${mod}+D" = "exec ${swayconf.menu}";
-    "${mod}+P" = "exec ${passmenu}";
-    "${mod}+Shift+P" = "exec ${passmenu} --type";
-    "${mod}+F2" = "exec qutebrowser";
-    "${mod}+Shift+Q" = "kill";
-    "${mod}+F" = "fullscreen toggle";
-    # Media Controls
-    "${mod}+F10" = "exec ${selectAudio} select-sink";
-    "${mod}+Shift+F10" = "exec ${selectAudio} select-source";
-    "XF86AudioRaiseVolume" = "exec ${pkgs.avizo}/bin/volumectl up";
-    "XF86AudioLowerVolume" = "exec ${pkgs.avizo}/bin/volumectl down";
-    "XF86AudioMute" = "exec ${pkgs.avizo}/bin/volumectl toggle-mute";
-    "XF86ScreenSaver" = "exec ${pkgs.swaylock}/bin/swaylock --image ${cfg.background}";
-    "XF86MonBrightnessUp" = "exec ${pkgs.avizo}/bin/lightctl up";
-    "XF86MonBrightnessDown" = "exec ${pkgs.avizo}/bin/lightctl down";
-    # Floating
-    "${mod}+Space" = "floating toggle";
-    "${mod}+Shift+Space" = "focus mode_toggle";
-    # Scratchpad
-    "${mod}+Minus" = "scratchpad show";
-    "${mod}+Shift+Minus" = "move scratchpad";
-    # Layout
-    "${mod}+e" = "layout toggle split";
-    # Session control
-    "${mod}+r" = "reload";
-    "${mod}+Shift+m" = "exit";
-  }
-  (
-    focusWindowKeybinds
-    ++ moveWindowKeybinds
-    ++ resizeWindowKeybinds
-    ++ focusWorkspaceKeybindings
-    ++ moveWorkspaceKeybindings
-    ++ moveFocusWorkspaceKeybindings
-  )
diff --git a/modules/hm/gui/sway-config.nix b/modules/hm/gui/sway-config.nix
deleted file mode 100644
index e20e8aa..0000000
--- a/modules/hm/gui/sway-config.nix
+++ /dev/null
@@ -1,101 +0,0 @@
-{ config, pkgs }:
-let
-  cfg = config.jhome.gui.sway;
-  modifier = "Mod4";
-  inherit (config.jhome.gui) terminal;
-  termCmd =
-    if terminal == "wezterm" then
-      "wezterm start"
-    else if terminal == "alacritty" then
-      "alacritty -e"
-    else
-      builtins.abort "no command configured for ${terminal}";
-  menu = "${pkgs.fuzzel}/bin/fuzzel --terminal '${termCmd}'";
-  # currently, there is some friction between sway and gtk:
-  # https://github.com/swaywm/sway/wiki/GTK-3-settings-on-Wayland
-  # the suggested way to set gtk settings is with gsettings
-  # for gsettings to work, we need to tell it where the schemas are
-  # using the XDG_DATA_DIR environment variable
-  # run at the end of sway config
-  configure-gtk =
-    let
-      schema = pkgs.gsettings-desktop-schemas;
-      datadir = "${schema}/share/gsettings-schemas/${schema.name}";
-    in
-    pkgs.writers.writeDashBin "configure-gtk" ''
-      export XDG_DATA_DIRS="${datadir}:$XDG_DATA_DIRS"
-
-      gnome_schema=org.gnome.desktop.interface
-      config="${config.xdg.configHome}/gtk-3.0/settings.ini"
-      if [ ! -f "$config" ]; then exit 1; fi
-      # Read settings from gtk3
-      gtk_theme="$(${pkgs.gnugrep}/bin/grep 'gtk-theme-name' "$config" | ${pkgs.gnused}/bin/sed 's/.*\s*=\s*//')"
-      icon_theme="$(${pkgs.gnugrep}/bin/grep 'gtk-icon-theme-name' "$config" | ${pkgs.gnused}/bin/sed 's/.*\s*=\s*//')"
-      cursor_theme="$(${pkgs.gnugrep}/bin/grep 'gtk-cursor-theme-name' "$config" | ${pkgs.gnused}/bin/sed 's/.*\s*=\s*//')"
-      font_name="$(grep 'gtk-font-name' "$config" | sed 's/.*\s*=\s*//')"
-      ${pkgs.glib}/bin/gsettings set "$gnome_schema" gtk-theme "$gtk_theme"
-      ${pkgs.glib}/bin/gsettings set "$gnome_schema" icon-theme "$icon_theme"
-      ${pkgs.glib}/bin/gsettings set "$gnome_schema" cursor-theme "$cursor_theme"
-      ${pkgs.glib}/bin/gsettings set "$gnome_schema" font-name "$font_name"
-      ${pkgs.glib}/bin/gsettings set "$gnome_schema" color-scheme prefer-dark
-    '';
-  cmdOnce = command: { inherit command; };
-  cmdAlways = command: {
-    inherit command;
-    always = true;
-  };
-in
-{
-  inherit modifier terminal menu;
-  keybindings = import ./keybindings.nix { inherit config pkgs; };
-  # Appearance
-  bars = [ ]; # Waybar is started as a systemd service
-  gaps = {
-    smartGaps = true;
-    smartBorders = "on";
-    inner = 4;
-  };
-  output."*".bg = "${cfg.background} fill";
-  # Window Appearance
-  window = {
-    border = 2;
-    titlebar = false;
-    # Make certain windows floating
-    commands = [
-      {
-        command = "floating enable";
-        criteria.title = "zoom";
-      }
-      {
-        command = "floating enable";
-        criteria.class = "floating";
-      }
-      {
-        command = "floating enable";
-        criteria.app_id = "floating";
-      }
-    ];
-  };
-  # Startup scripts
-  startup =
-    [
-      (cmdAlways "${configure-gtk}/bin/configure-gtk")
-    ]
-    ++ (builtins.map cmdAlways cfg.exec.always)
-    ++ (builtins.map cmdOnce cfg.exec.once);
-  # Keyboard configuration
-  input."type:keyboard" = {
-    repeat_delay = "300";
-    repeat_rate = "50";
-    xkb_options = "caps:swapescape,compose:ralt";
-    xkb_numlock = "enabled";
-  };
-  # Touchpad
-  input."type:touchpad" = {
-    click_method = "clickfinger";
-    natural_scroll = "enabled";
-    scroll_method = "two_finger";
-    tap = "enabled";
-    tap_button_map = "lrm";
-  };
-}
diff --git a/modules/hm/gui/sway.nix b/modules/hm/gui/sway.nix
new file mode 100644
index 0000000..5ed2293
--- /dev/null
+++ b/modules/hm/gui/sway.nix
@@ -0,0 +1,226 @@
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
+let
+  cfg = config.jhome.gui.sway;
+in
+{
+  config = lib.mkIf (config.jhome.enable && config.jhome.gui.enable && cfg.enable) {
+    # Window Manager
+    wayland.windowManager.sway = {
+      inherit (cfg) enable;
+      config =
+        let
+          inherit (config.jhome.gui) terminal;
+          termCmd =
+            if terminal == "wezterm" then
+              "wezterm start"
+            else if terminal == "alacritty" then
+              "alacritty -e"
+            else
+              builtins.abort "no command configured for ${terminal}";
+          menu = "${pkgs.fuzzel}/bin/fuzzel --terminal '${termCmd}'";
+          cmdOnce = command: { inherit command; };
+          cmdAlways = command: {
+            inherit command;
+            always = true;
+          };
+        in
+        {
+          modifier = "Mod4";
+          inherit terminal menu;
+          # Appearance
+          bars = [ ]; # Waybar is started as a systemd service
+          gaps = {
+            smartGaps = true;
+            smartBorders = "on";
+            inner = 4;
+          };
+          output."*".bg = "${cfg.background} fill";
+          # Window Appearance
+          window = {
+            border = 2;
+            titlebar = false;
+            # Make certain windows floating
+            commands = [
+              {
+                command = "floating enable";
+                criteria.title = "zoom";
+              }
+              {
+                command = "floating enable";
+                criteria.class = "floating";
+              }
+              {
+                command = "floating enable";
+                criteria.app_id = "floating";
+              }
+            ];
+          };
+          # Startup scripts
+          startup =
+            let
+              # currently, there is some friction between sway and gtk:
+              # https://github.com/swaywm/sway/wiki/GTK-3-settings-on-Wayland
+              # the suggested way to set gtk settings is with gsettings
+              # for gsettings to work, we need to tell it where the schemas are
+              # using the XDG_DATA_DIR environment variable
+              # run at the end of sway config
+              schema = pkgs.gsettings-desktop-schemas;
+              datadir = "${schema}/share/gsettings-schemas/${schema.name}";
+            in
+            [
+              (cmdAlways "${pkgs.writers.writeDash "configure-gtk" ''
+                export XDG_DATA_DIRS="${datadir}:$XDG_DATA_DIRS"
+
+                gnome_schema=org.gnome.desktop.interface
+                config="${config.xdg.configHome}/gtk-3.0/settings.ini"
+                if [ ! -f "$config" ]; then exit 1; fi
+                # Read settings from gtk3
+                gtk_theme="$(${pkgs.gnugrep}/bin/grep 'gtk-theme-name' "$config" | ${pkgs.gnused}/bin/sed 's/.*\s*=\s*//')"
+                icon_theme="$(${pkgs.gnugrep}/bin/grep 'gtk-icon-theme-name' "$config" | ${pkgs.gnused}/bin/sed 's/.*\s*=\s*//')"
+                cursor_theme="$(${pkgs.gnugrep}/bin/grep 'gtk-cursor-theme-name' "$config" | ${pkgs.gnused}/bin/sed 's/.*\s*=\s*//')"
+                font_name="$(grep 'gtk-font-name' "$config" | sed 's/.*\s*=\s*//')"
+                ${pkgs.glib}/bin/gsettings set "$gnome_schema" gtk-theme "$gtk_theme"
+                ${pkgs.glib}/bin/gsettings set "$gnome_schema" icon-theme "$icon_theme"
+                ${pkgs.glib}/bin/gsettings set "$gnome_schema" cursor-theme "$cursor_theme"
+                ${pkgs.glib}/bin/gsettings set "$gnome_schema" font-name "$font_name"
+                ${pkgs.glib}/bin/gsettings set "$gnome_schema" color-scheme prefer-dark
+              ''}")
+            ]
+            ++ (builtins.map cmdAlways cfg.exec.always)
+            ++ (builtins.map cmdOnce cfg.exec.once);
+          # Keyboard configuration
+          input."type:keyboard" = {
+            repeat_delay = "300";
+            repeat_rate = "50";
+            xkb_options = "caps:swapescape,compose:ralt";
+            xkb_numlock = "enabled";
+          };
+          # Touchpad
+          input."type:touchpad" = {
+            click_method = "clickfinger";
+            natural_scroll = "enabled";
+            scroll_method = "two_finger";
+            tap = "enabled";
+            tap_button_map = "lrm";
+          };
+          # Keybinds
+          keybindings =
+            let
+              passmenu = "${pkgs.jpassmenu}/bin/jpassmenu";
+              selectAudio = "${pkgs.audiomenu}/bin/audiomenu";
+              swayconf = config.wayland.windowManager.sway.config;
+              mod = swayconf.modifier;
+              workspaces = map toString (lib.lists.range 1 9);
+              dirs =
+                map
+                  (dir: {
+                    key = swayconf.${dir};
+                    arrow = dir;
+                    direction = dir;
+                  })
+                  [
+                    "up"
+                    "down"
+                    "left"
+                    "right"
+                  ];
+              joinKeys = builtins.concatStringsSep "+";
+              # Generate a keybind from a modifier prefix and a key
+              keycombo = prefix: key: joinKeys (prefix ++ [ key ]);
+              modKeybind = keycombo [ mod ];
+              modCtrlKeybind = keycombo [
+                mod
+                "Ctrl"
+              ];
+              modShiftKeybind = keycombo [
+                mod
+                "Shift"
+              ];
+              modCtrlShiftKeybind = keycombo [
+                mod
+                "Ctrl"
+                "Shift"
+              ];
+              dir2resize.up = "resize grow height";
+              dir2resize.down = "resize shrink height";
+              dir2resize.right = "resize grow width";
+              dir2resize.left = "resize shrink width";
+              # Bind a key combo to an action
+              genKeybind = prefix: action: key: { "${prefix key}" = "${action key}"; };
+              genKey =
+                prefix: action: genKeybind ({ key, ... }: prefix key) ({ direction, ... }: action direction);
+              genArrow =
+                prefix: action: genKeybind ({ arrow, ... }: prefix arrow) ({ direction, ... }: action direction);
+              genArrowAndKey =
+                prefix: action: key:
+                (genKey prefix action key) // (genArrow prefix action key);
+              # Move window
+              moveWindowKeybinds = map (genArrowAndKey modShiftKeybind (dir: "move ${dir}")) dirs;
+              # Focus window
+              focusWindowKeybinds = map (genArrowAndKey modKeybind (dir: "focus ${dir}")) dirs;
+              # Resize window
+              resizeWindowKeybinds = map (genArrowAndKey modCtrlKeybind (dir: dir2resize.${dir})) dirs;
+              # Move container to workspace
+              moveWorkspaceKeybindings = map (genKeybind modShiftKeybind (
+                number: "move container to workspace number ${number}"
+              )) workspaces;
+              # Focus workspace
+              focusWorkspaceKeybindings = map (genKeybind modKeybind (
+                number: "workspace number ${number}"
+              )) workspaces;
+              # Move container to Workspace and focus on it
+              moveFocusWorkspaceKeybindings = map (genKeybind modCtrlShiftKeybind (
+                number: "move container to workspace number ${number}; workspace number ${number}"
+              )) workspaces;
+            in
+            builtins.foldl' (l: r: l // r)
+              {
+                "${mod}+Return" = "exec ${swayconf.terminal}";
+                "${mod}+D" = "exec ${swayconf.menu}";
+                "${mod}+P" = "exec ${passmenu}";
+                "${mod}+Shift+P" = "exec ${passmenu} --type";
+                "${mod}+F2" = "exec qutebrowser";
+                "${mod}+Shift+Q" = "kill";
+                "${mod}+F" = "fullscreen toggle";
+                # Media Controls
+                "${mod}+F10" = "exec ${selectAudio} select-sink";
+                "${mod}+Shift+F10" = "exec ${selectAudio} select-source";
+                "XF86AudioRaiseVolume" = "exec ${pkgs.avizo}/bin/volumectl up";
+                "XF86AudioLowerVolume" = "exec ${pkgs.avizo}/bin/volumectl down";
+                "XF86AudioMute" = "exec ${pkgs.avizo}/bin/volumectl toggle-mute";
+                "XF86ScreenSaver" = "exec ${pkgs.swaylock}/bin/swaylock --image ${cfg.background}";
+                "XF86MonBrightnessUp" = "exec ${pkgs.avizo}/bin/lightctl up";
+                "XF86MonBrightnessDown" = "exec ${pkgs.avizo}/bin/lightctl down";
+                # Floating
+                "${mod}+Space" = "floating toggle";
+                "${mod}+Shift+Space" = "focus mode_toggle";
+                # Scratchpad
+                "${mod}+Minus" = "scratchpad show";
+                "${mod}+Shift+Minus" = "move scratchpad";
+                # Layout
+                "${mod}+e" = "layout toggle split";
+                # Session control
+                "${mod}+r" = "reload";
+                "${mod}+Shift+m" = "exit";
+              }
+              (
+                focusWindowKeybinds
+                ++ moveWindowKeybinds
+                ++ resizeWindowKeybinds
+                ++ focusWorkspaceKeybindings
+                ++ moveWorkspaceKeybindings
+                ++ moveFocusWorkspaceKeybindings
+              );
+        };
+      systemd = {
+        enable = true;
+        xdgAutostart = true;
+      };
+    };
+  };
+}
diff --git a/modules/hm/gui/waybar-settings.nix b/modules/hm/gui/waybar-settings.nix
deleted file mode 100644
index 3fcf58c..0000000
--- a/modules/hm/gui/waybar-settings.nix
+++ /dev/null
@@ -1,127 +0,0 @@
-{ config, lib }:
-let
-  cfg = config.jhome.gui;
-in
-{
-  mainBar = {
-    layer = "top";
-    position = "top";
-    margin = "2 2 2 2";
-    # Choose the order of the modules
-    modules-left = [ "sway/workspaces" ];
-    modules-center = [ "clock" ];
-    modules-right =
-      [
-        "pulseaudio"
-        "backlight"
-        "battery"
-        "sway/language"
-        "memory"
-      ]
-      ++ lib.optional (cfg.tempInfo != null) "temperature"
-      ++ [ "tray" ];
-    "sway/workspaces" = {
-      disable-scroll = true;
-      persistent-workspaces = {
-        "1" = [ ];
-        "2" = [ ];
-        "3" = [ ];
-        "4" = [ ];
-        "5" = [ ];
-        "6" = [ ];
-        "7" = [ ];
-        "8" = [ ];
-        "9" = [ ];
-      };
-    };
-    "sway/language" = {
-      format = "{} ";
-      min-length = 5;
-      tooltip = false;
-    };
-    memory = {
-      format = "{used:0.1f}/{total:0.1f}GiB ";
-      interval = 3;
-    };
-    clock = {
-      timezone = "Europe/Berlin";
-      tooltip-format = "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
-      format = "{:%a, %d %b, %H:%M}";
-    };
-    pulseaudio = {
-      reverse-scrolling = 1;
-      format = "{volume}% {icon} {format_source}";
-      format-bluetooth = "{volume}% {icon} {format_source}";
-      format-bluetooth-muted = "{volume}% 󰖁 {icon} {format_source}";
-      format-muted = "{volume}% 󰖁 {format_source}";
-      format-source = "{volume}% ";
-      format-source-muted = "{volume}% 󰍭";
-      format-icons = {
-        headphone = "󰋋";
-        hands-free = "";
-        headset = "󰋎";
-        phone = "󰘂";
-        portable = "";
-        car = "";
-        default = [
-          "󰕿"
-          "󰖀"
-          "󰕾"
-        ];
-      };
-      on-click = "pavucontrol";
-      min-length = 13;
-    };
-    temperature = lib.optionalAttrs (cfg.tempInfo != null) {
-      inherit (cfg.tempInfo) hwmon-path;
-      critical-threshold = 80;
-      format = "{temperatureC}°C {icon}";
-      format-icons = [
-        ""
-        ""
-        ""
-        ""
-        ""
-      ];
-      tooltip = false;
-    };
-    backlight = {
-      device = "intel_backlight";
-      format = "{percent}% {icon}";
-      format-icons = [
-        "󰃚"
-        "󰃛"
-        "󰃜"
-        "󰃝"
-        "󰃞"
-        "󰃟"
-        "󰃠"
-      ];
-      min-length = 7;
-    };
-    battery = {
-      states.warning = 30;
-      states.critical = 15;
-      format = "{capacity}% {icon}";
-      format-charging = "{capacity}% 󰂄";
-      format-plugged = "{capacity}% 󰚥";
-      format-alt = "{time} {icon}";
-      format-icons = [
-        "󰁺"
-        "󰁻"
-        "󰁼"
-        "󰁽"
-        "󰁾"
-        "󰁿"
-        "󰂀"
-        "󰂁"
-        "󰂂"
-        "󰁹"
-      ];
-    };
-    tray = {
-      icon-size = 16;
-      spacing = 0;
-    };
-  };
-}
diff --git a/modules/hm/gui/waybar.nix b/modules/hm/gui/waybar.nix
new file mode 100644
index 0000000..eefdab4
--- /dev/null
+++ b/modules/hm/gui/waybar.nix
@@ -0,0 +1,159 @@
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
+let
+  inherit (config) jhome;
+  cfg = jhome.gui;
+  swayconf = config.wayland.windowManager.sway;
+in
+{
+  config = lib.mkIf (config.jhome.enable && cfg.enable) {
+    # Status bar
+    programs.waybar = {
+      enable = true;
+      systemd.enable = true;
+      settings = lib.mkIf config.jhome.styling.enable {
+        mainBar = {
+          layer = "top";
+          position = "top";
+          margin = "2 2 2 2";
+          # Choose the order of the modules
+          modules-left = [ "sway/workspaces" ];
+          modules-center = [ "clock" ];
+          modules-right =
+            [
+              "pulseaudio"
+              "backlight"
+              "battery"
+              "sway/language"
+              "memory"
+            ]
+            ++ lib.optional (cfg.tempInfo != null) "temperature"
+            ++ [ "tray" ];
+          "sway/workspaces" = lib.mkIf swayconf.enable {
+            disable-scroll = true;
+            persistent-workspaces = {
+              "1" = [ ];
+              "2" = [ ];
+              "3" = [ ];
+              "4" = [ ];
+              "5" = [ ];
+              "6" = [ ];
+              "7" = [ ];
+              "8" = [ ];
+              "9" = [ ];
+            };
+          };
+          "sway/language" = lib.mkIf swayconf.enable {
+            format = "{} ";
+            min-length = 5;
+            tooltip = false;
+          };
+          memory = {
+            format = "{used:0.1f}/{total:0.1f}GiB ";
+            interval = 3;
+          };
+          clock = {
+            timezone = "Europe/Berlin";
+            tooltip-format = "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
+            format = "{:%a, %d %b, %H:%M}";
+          };
+          wireplumber = {
+            reverse-scrolling = 1;
+            format = "{volume}% {icon} {format_source}";
+            format-bluetooth = "{volume}% {icon} {format_source}";
+            format-bluetooth-muted = "{volume}% 󰖁 {icon} {format_source}";
+            format-muted = "{volume}% 󰖁 {format_source}";
+            format-source = "{volume}% ";
+            format-source-muted = "{volume}% 󰍭";
+            format-icons = {
+              headphone = "󰋋";
+              hands-free = "";
+              headset = "󰋎";
+              phone = "󰘂";
+              portable = "";
+              car = "";
+              default = [
+                "󰕿"
+                "󰖀"
+                "󰕾"
+              ];
+            };
+            on-click = lib.getExe pkgs.helvum;
+            min-length = 13;
+          };
+          temperature = lib.optionalAttrs (cfg.tempInfo != null) {
+            inherit (cfg.tempInfo) hwmon-path;
+            critical-threshold = 80;
+            format = "{temperatureC}°C {icon}";
+            format-icons = [
+              ""
+              ""
+              ""
+              ""
+              ""
+            ];
+            tooltip = false;
+          };
+          backlight = {
+            device = "intel_backlight";
+            format = "{percent}% {icon}";
+            format-icons = [
+              "󰃚"
+              "󰃛"
+              "󰃜"
+              "󰃝"
+              "󰃞"
+              "󰃟"
+              "󰃠"
+            ];
+            min-length = 7;
+          };
+          battery = {
+            states.warning = 30;
+            states.critical = 15;
+            format = "{capacity}% {icon}";
+            format-charging = "{capacity}% 󰂄";
+            format-plugged = "{capacity}% 󰚥";
+            format-alt = "{time} {icon}";
+            format-icons = [
+              "󰁺"
+              "󰁻"
+              "󰁼"
+              "󰁽"
+              "󰁾"
+              "󰁿"
+              "󰂀"
+              "󰂁"
+              "󰂂"
+              "󰁹"
+            ];
+          };
+          tray = {
+            icon-size = 16;
+            spacing = 0;
+          };
+        };
+      };
+      # Style overrides to highlight workspaces with windows
+      style =
+        lib.pipe
+          # css
+          ''
+            .modules-left #workspaces button {
+              border-bottom: 3px solid @base01;
+            }
+            .modules-left #workspaces button.persistent {
+              border-bottom: 3px solid transparent;
+            }
+          ''
+          [
+            (lib.optionalString config.jhome.styling.enable)
+            lib.mkAfter
+          ];
+    };
+  };
+}