{ config, lib, pkgs, osConfig, ... }: with config.lib.niri.actions; let playerctl = spawn "${pkgs.playerctl}/bin/playerctl"; ssPath = "${toString config.hm.conf.xdg-dirs.storagePath}/pics/ss/$(date +%Y-%m-%d_%H-%M-%S)"; obsEnabled = config.hm.soft.obs.enable or false; obsPass = if obsEnabled then osConfig.sops.secrets."obs/websocket_password".path else ""; otter-menu = pkgs.writeShellScriptBin "otter-menu" '' ${pkgs.foot}/bin/footclient -a otter-launcher otter-launcher & ${pkgs.niri}/bin/niri msg event-stream | while read -r line; do if echo "$line" | ${pkgs.ripgrep-all}/bin/rga -aq "Window focus changed"; then ${pkgs.procps}/bin/pkill -f "otter-launcher" exit 0 fi done ''; in { # --- System & Media --- "Mod+Shift+E".action = quit; "Mod+Slash".action = show-hotkey-overlay; # Audio (System-wide via wireplumber) XF86AudioRaiseVolume.action = spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "2%+"; XF86AudioLowerVolume.action = spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "2%-"; XF86AudioMicMute.action = spawn "sh" "-c" "wpctlwpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle && toggle-mute-notify"; XF86AudioMute.action = spawn "sh" "-c" "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle && wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle && toggle-mute-notify"; # Media Control XF86AudioPlay.action = playerctl "play-pause"; XF86AudioPrev.action = playerctl "previous"; XF86AudioNext.action = playerctl "next"; # Brightness XF86MonBrightnessUp.action = spawn "brightnessctl" "set" "5%+"; XF86MonBrightnessDown.action = spawn "brightnessctl" "set" "5%-"; # --- Applications --- "Mod+Return".action = spawn "footclient"; "Mod+D".action = spawn "fuzzel"; "Mod+Shift+D".action = spawn "sh" "-c" "cliphist list | fuzzel --dmenu | cliphist decode | wl-copy"; "Super+C".action = spawn "qalculate-gtk"; "Super+D".action = spawn "equibop"; "Super+B".action = spawn "librewolf"; "Super+S".action = spawn "spotify"; "Super+E".action = spawn "thunar"; "Super+Space".action = spawn "${otter-menu}/bin/otter-menu"; # --- Navigation: Columns & Workspaces --- "Mod+H".action = focus-column-or-monitor-left; "Mod+L".action = focus-column-or-monitor-right; "Mod+J".action = focus-window-or-workspace-down; "Mod+K".action = focus-window-or-workspace-up; "Super+H".action = focus-monitor-left; "Super+L".action = focus-monitor-right; "Super+Shift+H".action = move-column-to-monitor-left; "Super+Shift+L".action = move-column-to-monitor-right; "Mod+WheelScrollDown".action = focus-column-right; "Mod+WheelScrollUp".action = focus-column-left; "Super+WheelScrollDown".action = focus-window-or-workspace-down; "Super+WheelScrollUp".action = focus-window-or-workspace-up; "Mod+Tab" = { action = spawn "sh" "-c" "niri msg action focus-column-right; niri msg action move-column-to-last; niri msg action focus-window-previous"; repeat = false; }; XF86Tools = { action = spawn "sh" "-c" "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle && toggle-mute-notify"; repeat = false; }; "Mod+XF86Tools" = { action = spawn "sh" "-c" "niri msg action set-dynamic-cast-window --id $(niri msg --json pick-window | ${pkgs.jq}/bin/jq .id)"; repeat = false; }; # --- Window Management --- "Mod+Shift+Q".action = close-window; "Mod+Shift+V".action = toggle-window-floating; "Mod+O" = { action = toggle-overview; repeat = false; }; # Moving Windows/Columns "Mod+Shift+H".action = move-column-left-or-to-monitor-left; "Mod+Shift+L".action = move-column-right-or-to-monitor-right; "Mod+Shift+J".action = move-window-down-or-to-workspace-down; "Mod+Shift+K".action = move-window-up-or-to-workspace-up; "Mod+Shift+WheelScrollUp".action = move-column-left; "Mod+Shift+WheelScrollDown".action = move-column-right; "Super+Shift+WheelScrollDown".action = move-window-down-or-to-workspace-down; "Super+Shift+WheelScrollUp".action = move-window-up-or-to-workspace-up; # Container Management (Consume/Expel) "Mod+BracketLeft".action = consume-or-expel-window-left; "Mod+BracketRight".action = consume-or-expel-window-right; "Mod+Comma".action = consume-window-into-column; "Mod+Period".action = expel-window-from-column; XF86Launch5.action = consume-or-expel-window-right; XF86Launch6.action = consume-or-expel-window-left; XF86Launch7.action = switch-preset-column-width; # --- Layout & Sizing --- "Mod+F".action = maximize-column; "Mod+Shift+F".action = fullscreen-window; "Mod+W".action = toggle-column-tabbed-display; "Mod+R".action = switch-preset-column-width; "Mod+Shift+R".action = reset-window-height; "Mod+Minus".action = set-column-width "-10%"; "Mod+Equal".action = set-column-width "+10%"; "Mod+Shift+Minus".action = set-window-height "-10%"; "Mod+Shift+Equal".action = set-window-height "+10%"; # --- Screenshots --- "Mod+S" = { action = spawn "sh" "-c" '' wayfreeze & sleep 0.1 GEOM=$(slurp) if [ -n "$GEOM" ]; then grim -g "$GEOM" - | tee ${ssPath}.png | wl-copy fi pkill -n wayfreeze ''; repeat = false; }; "Mod+Shift+S" = { action = spawn "sh" "-c" '' wayfreeze & sleep 0.1 GEOM=$(slurp) if [ -n "$GEOM" ]; then grim -g "$GEOM" -t ppm - | satty --filename - --output-filename "${ssPath}-edited.png" fi pkill -n wayfreeze ''; repeat = false; }; "Mod+grave".action.focus-workspace = 0; "Mod+Shift+grave".action.move-column-to-workspace = 0; } // (lib.optionalAttrs obsEnabled { "Super+Return".action = spawn "sh" "-c" "OBS_WEBSOCKET_URL=$(cat ${obsPass}) obs-cmd replay save"; }) // (builtins.listToAttrs ( builtins.concatMap ( keyNum: let key = toString keyNum; targetWS = keyNum + 1; in [ { name = "Mod+${key}"; value.action = focus-workspace targetWS; } { name = "Mod+Shift+${key}"; value.action.move-column-to-workspace = [ targetWS ]; } ] ) (lib.range 1 9) ))