From 97875329beeafb46a78bbefc961bef519adbbdf9 Mon Sep 17 00:00:00 2001 From: alexion Date: Wed, 22 Jul 2026 15:52:21 -0400 Subject: [PATCH] feat(desktop): add the rofi launcher (task 0024) Add a rofi module to the desktop group, enabled by the aggregator. It combines drun, run and window into one combi prompt, with calc and emoji as further modes, bound on Super+R. A session menu built on the same themed rofi is bound on Super+Shift+X, and every rofi -dmenu call inherits the Stylix theme, so later utility menus reuse it for free. Also give layer surfaces their own near-instant fade-in (layersIn and fadeLayersIn), so the launcher appears quicker than the windows it sits over. --- .claude/tasks/0024-rofi-launcher.md | 23 +++++++--- modules/desktop/desktop.nix | 1 + modules/desktop/hyprland/hyprland.nix | 3 ++ modules/desktop/rofi.nix | 61 +++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 modules/desktop/rofi.nix diff --git a/.claude/tasks/0024-rofi-launcher.md b/.claude/tasks/0024-rofi-launcher.md index 42f8b5a..3837257 100644 --- a/.claude/tasks/0024-rofi-launcher.md +++ b/.claude/tasks/0024-rofi-launcher.md @@ -13,8 +13,21 @@ Structure it so it is reusable as the dmenu-style frontend for later utility men ## Acceptance criteria -- [ ] A rofi module exists in the desktop group and is enabled by the aggregator. -- [ ] rofi combines application-run, binary-run, and window-switch modes, plus math evaluation and emoji. -- [ ] rofi opens on `Super+R`. -- [ ] rofi is usable as a dmenu-style frontend for utility menus, and a power menu is provided through it. -- [ ] neogaia builds green under `nix flake check`. +- [x] A rofi module exists in the desktop group and is enabled by the aggregator. +- [x] rofi combines application-run, binary-run, and window-switch modes, plus math evaluation and emoji. +- [x] rofi opens on `Super+R`. +- [x] rofi is usable as a dmenu-style frontend for utility menus, and a power menu is provided through it. +- [x] neogaia builds green under `nix flake check`. + +## Implementation Notes + +- `rofi-wayland` no longer exists as a separate package: nixpkgs merged the Wayland fork into `pkgs.rofi` (now 2.0.0), and `rofi-emoji-wayland` into `rofi-emoji`. + The module uses the plain `pkgs.rofi`, which is the Wayland-capable build. +- The combined prompt is rofi's `combi` mode: `combi-modi = "drun,run,window"` merges the three run/switch modes into one list, and `modi = "combi,calc,emoji"` makes calc (via the `rofi-calc` plugin) and emoji (via `rofi-emoji`) the two further modes the same window tabs between. + `Super+R` runs `rofi -show combi`. +- The dmenu-style reuse is the themed rofi itself, not a separate abstraction: any `rofi -dmenu` invocation reads the same config and Stylix theme, so the power menu — and later clipboard/utility menus — look uniform for free. +- The launcher and power-menu keybinds live in this module rather than in `hyprland.nix`, contributed through `settings.bind`, which the module system concatenates with the compositor's own binds in the single `hyprland.conf`. + This keeps each command next to its binding and referenced by store path, so a rename cannot silently break the bind. +- The power menu is bound to `Super+Shift+X`, chosen by the operator. + It pairs with lock on `Super+X` (a key the spec's table does list), while the parent spec's table has no power-menu key of its own. +- No Waybar `window-rewrite` icon mapping was added: rofi renders as a Wayland layer-shell overlay, not a tiled window with a class on a workspace, so it never appears on the workspace indicator the convention governs. diff --git a/modules/desktop/desktop.nix b/modules/desktop/desktop.nix index 6492d2c..aa6b80d 100644 --- a/modules/desktop/desktop.nix +++ b/modules/desktop/desktop.nix @@ -11,6 +11,7 @@ in config = lib.mkIf cfg.enable { modules.desktop.hyprland.enable = lib.mkDefault true; modules.desktop.login.enable = lib.mkDefault true; + modules.desktop.rofi.enable = lib.mkDefault true; modules.desktop.terminal.enable = lib.mkDefault true; modules.desktop.theming.enable = lib.mkDefault true; modules.desktop.waybar.enable = lib.mkDefault true; diff --git a/modules/desktop/hyprland/hyprland.nix b/modules/desktop/hyprland/hyprland.nix index 8908aad..15667cb 100644 --- a/modules/desktop/hyprland/hyprland.nix +++ b/modules/desktop/hyprland/hyprland.nix @@ -98,6 +98,9 @@ in animation = [ "windows, 1, 3, ease" "fade, 1, 3, ease" + # Layer surfaces like the launcher appear near-instantly. + "layersIn, 1, 1, ease" + "fadeLayersIn, 1, 1, ease" "workspaces, 1, 3, ease" "border, 1, 3, ease" ]; diff --git a/modules/desktop/rofi.nix b/modules/desktop/rofi.nix new file mode 100644 index 0000000..2d423b8 --- /dev/null +++ b/modules/desktop/rofi.nix @@ -0,0 +1,61 @@ +{ + config, + lib, + pkgs, + ... +}: +# rofi as the desktop launcher and dmenu-style menu frontend. +let + cfg = config.modules.desktop.rofi; + user = config.user.name; + + # rofi carrying the plugins that back its calc and emoji modes. + rofiPkg = pkgs.rofi.override { + plugins = [ + pkgs.rofi-calc + pkgs.rofi-emoji + ]; + }; + + # A session menu built on the same themed rofi, so the power actions look + # like every other menu the launcher drives. + powerMenu = pkgs.writeShellScript "rofi-power-menu" '' + chosen=$(printf '%s\n' Lock Suspend 'Log out' Reboot 'Shut down' \ + | ${rofiPkg}/bin/rofi -dmenu -i -p Power) + case "$chosen" in + Lock) ${pkgs.systemd}/bin/loginctl lock-session ;; + Suspend) ${pkgs.systemd}/bin/systemctl suspend ;; + 'Log out') ${pkgs.systemd}/bin/loginctl terminate-session "$XDG_SESSION_ID" ;; + Reboot) ${pkgs.systemd}/bin/systemctl reboot ;; + 'Shut down') ${pkgs.systemd}/bin/systemctl poweroff ;; + esac + ''; +in +{ + options.modules.desktop.rofi.enable = lib.mkEnableOption "the rofi launcher"; + + config = lib.mkIf cfg.enable { + home-manager.users.${user} = { + # No theme is set here: Stylix's rofi target supplies the Nord one. + programs.rofi = { + enable = true; + package = rofiPkg; + + # drun, run and window merge into one prompt through combi. + # calc and emoji are the other two modes the same window tabs between. + extraConfig = { + modi = "combi,calc,emoji"; + combi-modi = "drun,run,window"; + show-icons = true; + }; + }; + + # The launcher and the session menu, kept next to the tool they drive. + # $mod is defined by the compositor config these binds share. + wayland.windowManager.hyprland.settings.bind = [ + "$mod, R, exec, ${rofiPkg}/bin/rofi -show combi" + "$mod SHIFT, X, exec, ${powerMenu}" + ]; + }; + }; +}