{ inputs, config, ... }: let repo = config.repo; in { flake.modules.nixos.niri = { pkgs, ... }: { imports = [ inputs.niri.nixosModules.niri ]; nixpkgs.overlays = [ inputs.niri.overlays.niri ]; programs.niri.enable = true; programs.niri.package = pkgs.niri-unstable; programs.dconf.enable = true; services.gvfs.enable = true; services.udisks2.enable = true; xdg.portal.enable = true; }; flake.modules.homeManager.niri = { config, lib, osConfig, pkgs, ... }: let repoTheme = repo.theme.kanagawa; browserCommand = config.meta.desktop.browser.command; fileManagerPackage = config.meta.desktop.fileManager.package; terminalCommand = config.meta.desktop.terminal.command; terminalDesktopEntryName = config.meta.desktop.terminal.desktopEntryName; nixosConfigDir = repo.account.nixosConfigurationPath; secretsFile = "${nixosConfigDir}/modules/secrets/secrets.yaml"; outputs = lib.mapAttrs ( _: display: { position = { x = display.x; y = display.y; }; } // lib.optionalAttrs (display.primary or false) { "focus-at-startup" = true; } // lib.optionalAttrs (display.scale != null) { inherit (display) scale; } // lib.optionalAttrs (display.width != null && display.height != null && display.refresh != null) { mode = { inherit (display) width height refresh ; }; } ) osConfig.meta.machine.displays; terminalCommandArg = lib.escapeShellArg terminalCommand; mkTerminalScript = { name, title, appId ? "niri-ux-terminal", workdir ? nixosConfigDir, command ? null, runtimeInputs ? [ ], }: let terminalArgs = if terminalDesktopEntryName == "kitty" then [ "--class" appId "--title" title "--directory" workdir ] else if terminalDesktopEntryName == "foot" then [ "--app-id" appId "--title" title "--working-directory" workdir ] else [ ]; commandArgs = if command == null then [ ] else [ "--" "${pkgs.bash}/bin/bash" "-lc" command ]; execArgs = lib.concatMapStringsSep " " lib.escapeShellArg (terminalArgs ++ commandArgs); in pkgs.writeShellApplication { inherit name runtimeInputs; checkPhase = ""; text = '' # shellcheck disable=SC2016 cd ${lib.escapeShellArg workdir} exec ${terminalCommandArg} ${execArgs} ''; }; uxScripts = let vicinaePackage = config.programs.vicinae.package or pkgs.vicinae; in { nixosTerminal = mkTerminalScript { name = "niri-ux-nixos-terminal"; title = "NixOS Config"; }; nixosSwitch = mkTerminalScript { name = "niri-ux-nixos-switch"; title = "NixOS Switch"; appId = "niri-ux-float"; runtimeInputs = [ pkgs.coreutils pkgs.nh ]; command = '' set -o pipefail log_dir="''${XDG_STATE_HOME:-$HOME/.local/state}/nixos-switch" mkdir -p "$log_dir" log="$log_dir/$(date +%Y%m%d-%H%M%S).log" status_file="$(mktemp)" printf 'Running nh os switch in %s\n\n' ${lib.escapeShellArg nixosConfigDir} ( cd ${lib.escapeShellArg nixosConfigDir} nh os switch printf '%s' "$?" > "$status_file" ) 2>&1 | tee "$log" status="$(cat "$status_file")" rm -f "$status_file" printf '\nLog: %s\n' "$log" if [ "$status" -eq 0 ]; then printf 'NixOS switch completed successfully.\n' else printf 'NixOS switch failed with exit code %s.\n' "$status" fi printf 'Press Enter to close...' read -r _ exit "$status" ''; }; editSecrets = mkTerminalScript { name = "niri-ux-edit-secrets"; title = "Edit Secrets"; appId = "niri-ux-float"; runtimeInputs = [ pkgs.sops ]; command = '' sops edit ${lib.escapeShellArg secretsFile} ''; }; neovimProjects = mkTerminalScript { name = "niri-ux-neovim-projects"; title = "Neovim Projects"; command = '' nvim -c 'Telescope projects' ''; }; vicinaeCommand = pkgs.writeShellApplication { name = "niri-ux-vicinae-command"; runtimeInputs = [ vicinaePackage ]; text = '' case "''${1:-}" in files) link="vicinae://extensions/sameoldlab/fuzzy-files/find" ;; nix-options) link="vicinae://extensions/knoopx/nix/options" ;; home-manager-options) link="vicinae://extensions/knoopx/nix/home-manager-options" ;; nix-packages) link="vicinae://extensions/knoopx/nix/packages" ;; niri-windows) link="vicinae://extensions/knoopx/niri/windows" ;; *) printf 'unknown Vicinae command target: %s\n' "''${1:-}" >&2 exit 64 ;; esac exec vicinae deeplink "$link" ''; }; clipboardHistory = pkgs.writeShellApplication { name = "niri-ux-clipboard-history"; runtimeInputs = [ pkgs.cliphist vicinaePackage pkgs.wl-clipboard ]; text = '' selection="$(cliphist list | vicinae dmenu --navigation-title Clipboard --placeholder 'Search clipboard' --no-metadata)" if [ -z "$selection" ]; then exit 0 fi printf '%s' "$selection" | cliphist decode | wl-copy ''; }; pickColor = pkgs.writeShellApplication { name = "niri-ux-pick-color"; runtimeInputs = [ pkgs.libnotify pkgs.niri pkgs.wl-clipboard ]; text = '' color="$(niri msg pick-color)" printf '%s' "$color" | wl-copy notify-send 'Color picked' "$color" ''; }; }; uxCommands = lib.mapAttrs (_: lib.getExe) uxScripts; in { home.sessionVariables.NIXOS_OZONE_WL = "1"; dconf.settings = { "org/gnome/desktop/interface" = { color-scheme = "prefer-dark"; }; }; home.packages = with pkgs; [ playerctl brightnessctl xwayland-satellite ] ++ [ fileManagerPackage ] ++ lib.attrValues uxScripts; programs.niri.settings = { inherit outputs; environment.DISPLAY = ":0"; spawn-at-startup = [ { command = [ "xwayland-satellite" ]; } { command = [ "noctalia-shell" ]; } ]; prefer-no-csd = true; hotkey-overlay.skip-at-startup = true; screenshot-path = "${config.xdg.userDirs.pictures}/screenshots/%Y-%m-%dT%H:%M:%S.png"; animations.slowdown = 0.6; cursor = with config.home.pointerCursor; { size = size; theme = name; hide-after-inactive-ms = 3000; hide-when-typing = true; }; layout = { always-center-single-column = true; gaps = 14; focus-ring.enable = false; default-column-width.proportion = 1. / 2.; border = { enable = true; width = 3; active.color = repoTheme.palette.niri.border.active; inactive.color = repoTheme.palette.niri.border.inactive; urgent.color = repoTheme.palette.niri.border.urgent; }; }; window-rules = [ { geometry-corner-radius = let radius = 10.0; in { bottom-left = radius; bottom-right = radius; top-left = radius; top-right = radius; }; clip-to-geometry = true; } { matches = [ { app-id = "^niri-ux-float$"; } ]; open-floating = true; open-focused = true; } ]; debug.honor-xdg-activation-with-invalid-serial = true; gestures.hot-corners.enable = false; input = { focus-follows-mouse.enable = true; mouse."accel-speed" = 0.4; keyboard = { repeat-delay = 300; repeat-rate = 50; xkb.options = "caps:escape"; }; touchpad = { dwt = true; }; }; binds = import ./_bindings.nix { inherit browserCommand terminalCommand uxCommands ; }; }; }; }