From 065f094cdf7149012aca253c1d4c2aaf18dc76c1 Mon Sep 17 00:00:00 2001 From: Lucien Cartier-Tilet Date: Sun, 2 Nov 2025 17:56:58 +0100 Subject: [PATCH] feat: add wlr-which module and config --- users/modules/desktop/default.nix | 2 + users/modules/desktop/hyprland.nix | 9 +- users/modules/desktop/wlr-which.nix | 190 ++++++++++++++++++++++++++++ users/modules/dev/editors/emacs.nix | 25 ---- users/phundrak/home.nix | 128 ++++++++++++++++++- 5 files changed, 320 insertions(+), 34 deletions(-) create mode 100644 users/modules/desktop/wlr-which.nix diff --git a/users/modules/desktop/default.nix b/users/modules/desktop/default.nix index 6b1e8b1..38df732 100644 --- a/users/modules/desktop/default.nix +++ b/users/modules/desktop/default.nix @@ -16,6 +16,7 @@ in { ./rofi ./swaync.nix ./waybar.nix + ./wlr-which.nix ./wlsunset.nix ]; @@ -28,5 +29,6 @@ in { obs.enable = mkDefault cfg.fullDesktop; qt.enable = mkDefault cfg.fullDesktop; rofi.enable = mkDefault cfg.fullDesktop; + wlr-which-key.enable = mkDefault cfg.fullDesktop; }; } diff --git a/users/modules/desktop/hyprland.nix b/users/modules/desktop/hyprland.nix index 8ca43ad..6326987 100644 --- a/users/modules/desktop/hyprland.nix +++ b/users/modules/desktop/hyprland.nix @@ -117,8 +117,8 @@ in { $menu = rofi -combi-modi drun,calc -show combi bind = SUPER, Return, exec, ${pkgs.kitty}/bin/kitty ${pkgs.tmux}/bin/tmux - bind = SUPER, Space, submap, leader - bind = , Print, submap, screenshot + bind = SUPER, Space, exec, ${pkgs.wlr-which-key}/bin/wlr-which-key + bind = , Print, exec, ${pkgs.wlr-which-key}/bin/wlr-which-key -k s submap = leader bind = , l, exec, plock @@ -147,11 +147,13 @@ in { bind = , u, submap, reset bind = , escape, submap, reset bind = CTRL, g, submap, reset + submap = buffers bind = , d, killactive, bind = , d, submap, reset bind = , escape, submap, reset bind = CTRL, g, submap, reset + submap = resize binde = , $left, resizeactive, -10 0 binde = , $right, resizeactive, 10 0 @@ -160,6 +162,7 @@ in { bind = , q, submap, reset bind = , escape, submap, reset bind = CTRL, g, submap, reset + submap = rofi bind = , b, exec, rofi-bluetooth bind = , b, submap, reset @@ -173,6 +176,7 @@ in { bind = , y, submap, reset bind = , escape, submap, reset bind = CTRL, g, submap, reset + submap = screenshot bind = , Print, exec, screenshot bind = , Print, submap, reset @@ -188,6 +192,7 @@ in { bind = Shift, s, submap, reset bind = , escape, submap, reset bind = CTRL, g, submap, reset + submap = windows bind = , period, submap, resize bind = , $left, movefocus, l diff --git a/users/modules/desktop/wlr-which.nix b/users/modules/desktop/wlr-which.nix new file mode 100644 index 0000000..c4affac --- /dev/null +++ b/users/modules/desktop/wlr-which.nix @@ -0,0 +1,190 @@ +{ + config, + lib, + pkgs, + ... +}: let + inherit + (lib) + literalExpression + mkIf + mkOption + mkEnableOption + types + ; + cfg = config.home.desktop.wlr-which-key; + yamlFormat = pkgs.formats.yaml {}; + + # Convert kebab-case to snake_case + toSnakeCase = str: builtins.replaceStrings ["-"] ["_"] str; + + # Recursively filter out null values and convert kebab-case keys to snake_case + filterNulls = value: + if lib.isAttrs value + then lib.mapAttrs' (n: v: lib.nameValuePair (toSnakeCase n) (filterNulls v)) (lib.filterAttrs (n: v: v != null) value) + else if lib.isList value + then map filterNulls value + else value; + menuEntryType = types.submodule { + freeformType = yamlFormat.type; + options = with types; { + key = mkOption { + type = str; + example = "p"; + }; + desc = mkOption { + type = str; + example = "Power"; + }; + cmd = mkOption { + type = nullOr str; + default = null; + example = "echo example"; + }; + keep-open = mkOption { + type = nullOr bool; + default = null; + example = true; + }; + submenu = mkOption { + type = nullOr (listOf menuEntryType); + default = null; + example = literalExpression '' + [ + { key = "s"; desc = "Suspend"; cmd = "systemctl suspend"; } + { key = "r"; desc = "Reboot"; cmd = "systemctl reboot"; } + { key = "o"; desc = "Poweroff"; cmd = "systemctl poweroff"; } + ] + ''; + }; + }; + }; + settingsType = types.submodule { + freeformType = yamlFormat.type; + options = with types; { + background = mkOption { + type = nullOr str; + default = null; + example = "#282828FF"; + }; + color = mkOption { + type = nullOr str; + default = null; + example = "#FBF1C7FF"; + }; + border = mkOption { + type = nullOr str; + default = null; + example = "#8EC07CFF"; + }; + anchor = mkOption { + type = nullOr (enum ["center" "top" "bottom" "left" "right" "top-left" "top-right" "bottom-left" "bottom-right"]); + default = null; + example = "top-left"; + }; + margin-top = mkOption { + type = nullOr int; + default = null; + example = "0"; + }; + margin-right = mkOption { + type = nullOr int; + default = null; + example = "0"; + }; + margin-bottom = mkOption { + type = nullOr int; + default = null; + example = "0"; + }; + margin-left = mkOption { + type = nullOr int; + default = null; + example = "0"; + }; + font = mkOption { + type = nullOr str; + default = null; + example = "monospace 10"; + }; + separator = mkOption { + type = nullOr str; + default = null; + example = " ➜ "; + }; + border-width = mkOption { + type = nullOr (either float int); + default = null; + example = 4.0; + }; + corder-r = mkOption { + type = nullOr (either float int); + default = null; + example = 20.0; + }; + padding = mkOption { + type = nullOr (either float int); + default = null; + example = 15.0; + }; + rows-per-column = mkOption { + type = nullOr int; + default = null; + example = 5; + }; + column-padding = mkOption { + type = nullOr (either float int); + default = null; + example = 25.0; + }; + inhibit-compositor-keyboard-shortcuts = mkOption { + type = bool; + default = true; + example = false; + }; + auto_kbd_layout = mkOption { + type = bool; + default = true; + example = false; + }; + namespace = mkOption { + type = nullOr str; + default = null; + example = "wlr_which_key"; + }; + menu = mkOption { + type = listOf menuEntryType; + default = []; + example = literalExpression '' + [ + { + key = "p"; + desc = "Power"; + submenu = [ + { key = "s"; desc = "Suspend"; cmd = "systemctl suspend"; } + { key = "r"; desc = "Reboot"; cmd = "systemctl reboot"; } + { key = "o"; desc = "Poweroff"; cmd = "systemctl poweroff"; } + ]; + } + ] + ''; + }; + }; + }; +in { + options.home.desktop.wlr-which-key = { + enable = mkEnableOption "Enables wlr-which-key"; + package = lib.mkPackageOption pkgs "wlr-which-key" {}; + settings = mkOption { + type = settingsType; + default = {}; + description = "Configuration written to {file}`$XDG_CONFIG_HOME/wlr-which-key/config.yaml`."; + }; + }; + config = mkIf cfg.enable { + home.packages = [cfg.package]; + xdg.configFile = { + "wlr-which-key/config.yaml".source = yamlFormat.generate "wlr-which-key-config.yml" (filterNulls cfg.settings); + }; + }; +} diff --git a/users/modules/dev/editors/emacs.nix b/users/modules/dev/editors/emacs.nix index 893b850..2c85ffb 100644 --- a/users/modules/dev/editors/emacs.nix +++ b/users/modules/dev/editors/emacs.nix @@ -10,31 +10,6 @@ with lib; let with epkgs; [ mu4e pdf-tools - tree-sitter - tree-sitter-langs - (treesit-grammars.with-grammars (grammar: - with grammar; [ - tree-sitter-bash - tree-sitter-c - tree-sitter-cpp - tree-sitter-css - tree-sitter-dockerfile - tree-sitter-http - tree-sitter-javascript - tree-sitter-jsdoc - tree-sitter-json - tree-sitter-just - tree-sitter-markdown - tree-sitter-markdown-inline - tree-sitter-nix - tree-sitter-rust - tree-sitter-sql - tree-sitter-toml - tree-sitter-typescript - tree-sitter-typst - tree-sitter-vue - tree-sitter-yaml - ])) ] )); cfg = config.home.dev.editors.emacs; diff --git a/users/phundrak/home.nix b/users/phundrak/home.nix index b65eb2f..bc5056e 100644 --- a/users/phundrak/home.nix +++ b/users/phundrak/home.nix @@ -1,6 +1,7 @@ { pkgs, config, + lib, ... }: { imports = [ @@ -11,10 +12,36 @@ ]; config = let - emacsPkg = with pkgs; ((emacsPackagesFor emacs).emacsWithPackages ( - epkgs: [ - epkgs.mu4e - epkgs.pdf-tools + emacsPackage = with pkgs; ((emacsPackagesFor emacs).emacsWithPackages ( + epkgs: + with epkgs; [ + mu4e + pdf-tools + tree-sitter + tree-sitter-langs + (treesit-grammars.with-grammars (grammar: + with grammar; [ + tree-sitter-bash + tree-sitter-c + tree-sitter-cpp + tree-sitter-css + tree-sitter-dockerfile + tree-sitter-http + tree-sitter-javascript + tree-sitter-jsdoc + tree-sitter-json + tree-sitter-just + tree-sitter-markdown + tree-sitter-markdown-inline + tree-sitter-nix + tree-sitter-rust + tree-sitter-sql + tree-sitter-toml + tree-sitter-typescript + tree-sitter-typst + tree-sitter-vue + tree-sitter-yaml + ])) ] )); askpass = import ../modules/cli/scripts/askpass.nix {inherit pkgs;}; @@ -30,14 +57,101 @@ home = { sessionVariables = { - EDITOR = "${emacsPkg}/bin/emacsclient -c -a ${emacsPkg}/bin/emacs"; + EDITOR = "${emacsPackage}/bin/emacsclient -c -a ${emacsPackage}/bin/emacs"; LAUNCH_EDITOR = "${launchWithEmacsclient}/bin/launch-with-emacsclient"; SUDO_ASKPASS = "${askpass}/bin/askpass"; LSP_USE_PLISTS = "true"; }; - desktop.waybar.style = ./config/waybar/style.css; - dev.ai.claude.enable = true; + desktop = { + waybar.style = ./config/waybar/style.css; + wlr-which-key.settings = { + font = "Cascadia Code 12"; + background = "#3b4252d0"; + color = "#eceff4"; + border = "#2e3440"; + border_width = 2; + corner_r = 10; + rows_per_column = 5; + column_padding = 25; + inhibit_compositor_keyboard_shortcuts = true; + auto_kbd_layout = true; + menu = let + left = "c"; + down = "t"; + up = "s"; + right = "r"; + in [ + { + key = "a"; + desc = "Apps"; + submenu = [ + { key = "b"; desc = "Browser"; cmd = "zen"; } + { key = "B"; desc = "Qutebrowser"; cmd = "${pkgs.qutebrowser}/bin/qutebrowser"; } + { key = "d"; desc = "Discord"; cmd = "${pkgs.vesktop}/bin/vesktop"; } + { key = "e"; desc = "Emacs"; cmd = "${emacsPackage}/bin/emacsclient -c -n"; } + { key = "g"; desc = "Gimp"; cmd = "${pkgs.gimp}/bin/gimp"; } + { key = "n"; desc = "Nemo"; cmd = "${pkgs.nemo}/bin/nemo"; } + { + key = "r"; + desc = "Rofi"; + submenu = [ + { key = "b"; desc = "Bluetooth"; cmd = "rofi-bluetooth"; } + { key = "e"; desc = "Emoji"; cmd = "rofi -show emoji"; } + { key = "r"; desc = "App Menu"; cmd = "rofi -combi-modi drun,calc -show combi"; } + { key = "s"; desc = "SSH"; cmd = "rofi -show ssh"; } + { key = "y"; desc = "YouTube"; cmd = "ytplay"; } + ]; + } + ]; + } + { + key = "b"; + desc = "Buffers"; + submenu = [ + { key = "d"; desc = "Close"; cmd = "echo close"; } + { key = "f"; desc = "Fullscreen"; cmd = "echo fullscreen"; } + { key = "F"; desc = "Float"; cmd = "echo float"; } + { + key = "."; + desc = "Resize"; + submenu = [ + { key = left; desc = "Decrease Width"; cmd = "echo decrease width"; keep-open = true; } + { key = down; desc = "Increase Height"; cmd = "echo decrease height"; keep-open = true; } + { key = up; desc = "Decrease Height"; cmd = "echo decrease height"; keep-open = true; } + { key = right; desc = "Increase Width"; cmd = "echo increase width"; keep-open = true; } + ]; + } + ]; + } + { + key = "p"; + desc = "Power"; + submenu = [ + { key = "s"; desc = "Suspend"; cmd = "systemctl suspend"; } + { key = "r"; desc = "Reboot"; cmd = "systemctl reboot"; } + { key = "o"; desc = "Poweroff"; cmd = "systemctl poweroff"; } + ]; + } + { + key = "s"; + desc = "Screenshots"; + submenu = [ + { key = "Print"; desc = "Screenshot"; cmd = "screenshot"; } + { key = "d"; desc = "Delayed"; cmd = "screenshot -d 3"; } + { key = "D"; desc = "Select, Delay, Edit, and Copy"; cmd = "screenshot -secd 3"; } + { key = "e"; desc = "Select, Edit, and Copy"; cmd = "screenshot -sec"; } + { key = "g"; desc = "Select, Gimp, and Copy"; cmd = "screenshot -sgc"; } + { key = "s"; desc = "Select and Copy"; cmd = "screenshot -sc"; } + ]; + } + ]; + }; + }; + dev = { + ai.claude.enable = true; + editors.emacs.package = emacsPackage; + }; fullDesktop = true; shell.fish.enable = true; };