personal nixos config and packages

initial config

Asa Paparo 9f8c60b6

+16
.gitignore
··· 1 + * 2 + 3 + !.gitignore 4 + !README.md 5 + !flake.nix 6 + !flake.lock 7 + !/config/ 8 + !/config/** 9 + !/hardware/ 10 + !/hardware/** 11 + !/modules/ 12 + !/modules/** 13 + !/programs/ 14 + !/programs/** 15 + !/utils/ 16 + !/utils/**
+4
README.md
··· 1 + personal nixos config and package flake 2 + 3 + ## Cool Stuff 4 + [wrap.nix](./utils/wrap.nix)
+39
config/theme.nix
··· 1 + # hex codes for atom one dark pro colorscheme 2 + # from helix onedark theme 3 + { 4 + colors = rec { 5 + yellow = "E5C07B"; 6 + blue = "61AFEF"; 7 + red = "E06C75"; 8 + purple = "C678DD"; 9 + green = "98C379"; 10 + gold = "D19A66"; 11 + cyan = "56B6C2"; 12 + white = "ABB2BF"; 13 + black = "282C34"; 14 + light-black = "2C323C"; 15 + gray = "3E4452"; 16 + faint-gray = "3B4048"; 17 + light-gray = "5C6370"; 18 + linenr = "4B5263"; 19 + 20 + darker = "20252C"; 21 + 22 + bg-darker = darker; 23 + bg-regular = black; 24 + bg-light = light-black; 25 + fg-regular = white; 26 + fg-dim = light-gray; 27 + }; 28 + 29 + # in px 30 + lengths = { 31 + font-lg = "14"; 32 + font-md = "12"; 33 + font-sm = "10"; 34 + border-radius = "8"; 35 + border-width = "2"; 36 + margin = "8"; 37 + padding = "8"; 38 + }; 39 + }
+44
flake.lock
··· 1 + { 2 + "nodes": { 3 + "hardware": { 4 + "locked": { 5 + "lastModified": 1754564048, 6 + "narHash": "sha256-dz303vGuzWjzOPOaYkS9xSW+B93PSAJxvBd6CambXVA=", 7 + "owner": "nixos", 8 + "repo": "nixos-hardware", 9 + "rev": "26ed7a0d4b8741fe1ef1ee6fa64453ca056ce113", 10 + "type": "github" 11 + }, 12 + "original": { 13 + "owner": "nixos", 14 + "ref": "master", 15 + "repo": "nixos-hardware", 16 + "type": "github" 17 + } 18 + }, 19 + "nixpkgs": { 20 + "locked": { 21 + "lastModified": 1754725699, 22 + "narHash": "sha256-iAcj9T/Y+3DBy2J0N+yF9XQQQ8IEb5swLFzs23CdP88=", 23 + "owner": "nixos", 24 + "repo": "nixpkgs", 25 + "rev": "85dbfc7aaf52ecb755f87e577ddbe6dbbdbc1054", 26 + "type": "github" 27 + }, 28 + "original": { 29 + "owner": "nixos", 30 + "ref": "nixos-unstable", 31 + "repo": "nixpkgs", 32 + "type": "github" 33 + } 34 + }, 35 + "root": { 36 + "inputs": { 37 + "hardware": "hardware", 38 + "nixpkgs": "nixpkgs" 39 + } 40 + } 41 + }, 42 + "root": "root", 43 + "version": 7 44 + }
+52
flake.nix
··· 1 + { 2 + description = "Asa's personal NixOS configuration"; 3 + 4 + inputs = { 5 + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 6 + 7 + hardware.url = "github:nixos/nixos-hardware/master"; 8 + }; 9 + 10 + outputs = 11 + { 12 + nixpkgs, 13 + hardware, 14 + ... 15 + }@inputs: 16 + let 17 + forAllSystems = 18 + function: 19 + nixpkgs.lib.genAttrs [ 20 + "x86_64-linux" # <- useful 21 + "aarch64-linux" # <- aspirational 22 + # "risc64-linux" # <- aspirational 23 + "aarch64-darwin" # <- useless 24 + ] (system: function nixpkgs.legacyPackages.${system}); 25 + in 26 + rec { 27 + nixosConfigurations.asa-fw = nixpkgs.lib.nixosSystem { 28 + system = "x86_64-linux"; 29 + specialArgs = { 30 + inherit inputs; 31 + packages = packages."x86_64-linux"; 32 + }; 33 + modules = [ 34 + modules/system.nix 35 + modules/desktop.nix 36 + modules/apps.nix 37 + modules/fonts.nix 38 + modules/networking.nix 39 + modules/audio.nix 40 + modules/dev.nix 41 + hardware/fw-13.nix 42 + hardware.nixosModules.framework-13-7040-amd 43 + ]; 44 + }; 45 + 46 + packages = forAllSystems (pkgs: import ./programs { inherit pkgs; }); 47 + 48 + devShells = forAllSystems (pkgs: pkgs.nil); 49 + 50 + formatter = forAllSystems (pkgs: pkgs.nixfmt-tree); 51 + }; 52 + }
+53
hardware/fw-13.nix
··· 1 + # Do not modify this file! It was generated by ‘nixos-generate-config’ 2 + # and may be overwritten by future invocations. Please make changes 3 + # to /etc/nixos/configuration.nix instead. 4 + { 5 + config, 6 + lib, 7 + pkgs, 8 + modulesPath, 9 + ... 10 + }: 11 + 12 + { 13 + imports = [ 14 + (modulesPath + "/installer/scan/not-detected.nix") 15 + ]; 16 + 17 + boot.initrd.availableKernelModules = [ 18 + "nvme" 19 + "xhci_pci" 20 + "thunderbolt" 21 + "usb_storage" 22 + "sd_mod" 23 + ]; 24 + boot.initrd.kernelModules = [ ]; 25 + boot.kernelModules = [ "kvm-amd" ]; 26 + boot.extraModulePackages = [ ]; 27 + 28 + fileSystems."/" = { 29 + device = "/dev/disk/by-uuid/5180e190-2e04-4a59-ae1e-1c7c5694960d"; 30 + fsType = "btrfs"; 31 + }; 32 + 33 + fileSystems."/boot" = { 34 + device = "/dev/disk/by-uuid/3AD6-FBA1"; 35 + fsType = "vfat"; 36 + options = [ 37 + "fmask=0077" 38 + "dmask=0077" 39 + ]; 40 + }; 41 + 42 + swapDevices = [ ]; 43 + 44 + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking 45 + # (the default) this is the recommended approach. When using systemd-networkd it's 46 + # still possible to use this option, but it's recommended to use it in conjunction 47 + # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`. 48 + networking.useDHCP = lib.mkDefault true; 49 + # networking.interfaces.wlp1s0.useDHCP = lib.mkDefault true; 50 + 51 + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 52 + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; 53 + }
+97
modules/apps.nix
··· 1 + { 2 + pkgs, 3 + packages, 4 + ... 5 + }: 6 + { 7 + services.flatpak.enable = true; 8 + 9 + xdg.portal = { 10 + enable = true; 11 + xdgOpenUsePortal = true; 12 + extraPortals = with pkgs; [ 13 + xdg-desktop-portal-gtk 14 + xdg-desktop-portal-gnome 15 + ]; 16 + }; 17 + 18 + # default handlers 19 + xdg.terminal-exec = { 20 + enable = true; 21 + settings = { 22 + default = [ "com.mitchellh.ghostty.desktop" ]; 23 + }; 24 + }; 25 + 26 + xdg.mime.defaultApplications = { 27 + "x-scheme-handler/http" = "org.mozilla.firefox.desktop"; 28 + "x-scheme-handler/https" = "org.mozilla.firefox.desktop"; 29 + "x-scheme-handler/ftp" = "org.mozilla.firefox.desktop"; 30 + "x-scheme-handler/mailto" = "org.mozilla.thunderbird.desktop"; 31 + "text/plain" = "Helix.desktop"; 32 + "application/pdf" = "org.mozilla.firefox.desktop"; 33 + # "image/png" = [ 34 + # "sxiv.desktop" 35 + # "gimp.desktop" 36 + # ]; 37 + }; 38 + 39 + # set up chromium config for pwas 40 + programs.chromium = { 41 + enable = true; 42 + extensions = [ 43 + "cjpalhdlnbpafiamejdnhcphjbkeiagm" # ublock origin 44 + ]; 45 + extraOpts = { 46 + "BrowserSignin" = 0; 47 + "SyncDisabled" = true; 48 + "PasswordManagerEnabled" = false; 49 + "SpellcheckEnabled" = true; 50 + "SpellcheckLanguage" = [ "en-US" ]; 51 + }; 52 + }; 53 + 54 + # Tell electron apps / chromium to use wayland 55 + environment.sessionVariables.NIXOS_OZONE_WL = "1"; 56 + 57 + # List packages installed in system profile. To search, run: 58 + # $ nix search wget 59 + users.users.asa.packages = with pkgs; [ 60 + packages.ghostty-wrapped 61 + packages.helix-wrapped 62 + packages.mako-wrapped 63 + packages.fuzzel-wrapped 64 + wl-clipboard 65 + # delete later 66 + xwayland-satellite 67 + 68 + yazi 69 + wget 70 + docker-compose 71 + ungoogled-chromium 72 + 73 + alacritty 74 + 75 + # utilities 76 + zip 77 + unzip 78 + zathura 79 + btop 80 + 81 + # vpn stuff 82 + networkmanager-openconnect 83 + globalprotect-openconnect 84 + 85 + # stuff that should be wrapped with helix 86 + nixd 87 + pyright 88 + rust-analyzer 89 + llvmPackages_19.clang-tools 90 + nixfmt-rfc-style 91 + 92 + # gnome core apps 93 + adwaita-icon-theme 94 + nautilus 95 + ]; 96 + 97 + }
+9
modules/audio.nix
··· 1 + { 2 + ... 3 + }: 4 + { 5 + services.pipewire = { 6 + enable = true; 7 + pulse.enable = true; 8 + }; 9 + }
+45
modules/desktop.nix
··· 1 + { 2 + lib, 3 + pkgs, 4 + packages, 5 + ... 6 + }: 7 + { 8 + systemd.user.services = { 9 + swayidle = { 10 + enable = true; 11 + unitConfig = { 12 + PartOf = "graphical-session.target"; 13 + After = "graphical-session.target"; 14 + Requisite = "graphical-session.target"; 15 + }; 16 + wantedBy = [ "graphical-session.target" ]; 17 + serviceConfig = { 18 + ExecStart = "${lib.getExe pkgs.swayidle} -w timeout 300 '${lib.getExe pkgs.niri} msg action power-off-monitors' timeout 300 '${lib.getExe packages.swaylock-wrapped} -f' before-sleep '${lib.getExe packages.swaylock-wrapped} -f'"; 19 + Restart = "on-failure"; 20 + }; 21 + }; 22 + }; 23 + 24 + programs.niri = { 25 + enable = true; 26 + package = packages.niri-wrapped; 27 + }; 28 + 29 + programs.waybar = { 30 + enable = true; 31 + package = packages.waybar-wrapped; 32 + }; 33 + 34 + services.greetd = { 35 + enable = true; 36 + settings = { 37 + default_session = { 38 + command = "${lib.getExe pkgs.tuigreet} --time --cmd 'bash -l -c ${lib.getExe' packages.niri-wrapped "niri-session"}'"; 39 + user = "greeter"; 40 + }; 41 + }; 42 + }; 43 + 44 + services.libinput.enable = true; 45 + }
+44
modules/dev.nix
··· 1 + { ... }: 2 + { 3 + programs.git = { 4 + enable = true; 5 + config = { 6 + init.defaultBranch = "main"; 7 + user.email = "asapaparo@gmail.com"; 8 + user.name = "Asa Paparo"; 9 + }; 10 + }; 11 + 12 + # programs.ssh.startAgent = true; 13 + programs.ssh.extraConfig = "SetEnv TERM=xterm-256color"; 14 + 15 + virtualisation.containers.enable = true; 16 + # virtualisation.docker.enable = true; 17 + 18 + virtualisation.podman = { 19 + enable = true; 20 + dockerCompat = true; 21 + dockerSocket.enable = true; 22 + }; 23 + 24 + # Enable direnv 25 + programs.direnv.enable = true; 26 + 27 + # Configure helix for editor and viewing functionality 28 + environment.variables = { 29 + EDITOR = "hx"; 30 + VISUAL = "hx"; 31 + }; 32 + 33 + # Set up local llms 34 + services.ollama = { 35 + enable = true; 36 + # radeon 780M igpu = gfx1103 37 + # gfx1100 is closest working target 38 + rocmOverrideGfx = "11.0.0"; 39 + acceleration = "rocm"; 40 + # loadModels = [ 41 + # "gemma3n:latest" 42 + # ]; 43 + }; 44 + }
+67
modules/fonts.nix
··· 1 + { 2 + pkgs, 3 + ... 4 + }: 5 + { 6 + fonts.packages = with pkgs; [ 7 + inter 8 + jetbrains-mono 9 + nerd-fonts.jetbrains-mono 10 + noto-fonts 11 + noto-fonts-cjk-sans 12 + noto-fonts-color-emoji 13 + noto-fonts-monochrome-emoji 14 + font-awesome 15 + ]; 16 + 17 + fonts.fontconfig = { 18 + enable = true; 19 + hinting.enable = true; 20 + defaultFonts = { 21 + sansSerif = [ 22 + "Inter" 23 + "Noto Sans" 24 + "Noto Sans CJK SC" 25 + "Noto Sans CJK TC" 26 + "Noto Sans CJK JP" 27 + "Noto Sans CJK KR" 28 + "Noto Sans Devanagari" 29 + "Noto Sans Arabic" 30 + "Liberation Sans" 31 + "DejaVu Sans" 32 + ]; 33 + 34 + serif = [ 35 + "Noto Serif" 36 + "Noto Serif CJK SC" 37 + "Noto Serif CJK TC" 38 + "Noto Serif CJK JP" 39 + "Noto Serif CJK KR" 40 + "Noto Serif Devanagari" 41 + "Noto Serif Arabic" 42 + "Liberation Serif" 43 + "DejaVu Serif" 44 + ]; 45 + 46 + monospace = [ 47 + "JetBrains Mono" 48 + "Noto Sans Mono" 49 + "Noto Sans Mono CJK SC" 50 + "Noto Sans Mono CJK TC" 51 + "Noto Sans Mono CJK JP" 52 + "Noto Sans Mono CJK KR" 53 + "Liberation Mono" 54 + "DejaVu Sans Mono" 55 + ]; 56 + 57 + emoji = [ 58 + "Noto Color Emoji" 59 + ]; 60 + }; 61 + }; 62 + 63 + console = { 64 + font = "Lat2-Terminus16"; 65 + useXkbConfig = true; # use xkb.options in tty. 66 + }; 67 + }
+27
modules/networking.nix
··· 1 + { 2 + ... 3 + }: 4 + { 5 + networking.hostName = "asa-fw"; # Define your hostname. 6 + # Pick only one of the below networking options. 7 + # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. 8 + networking.networkmanager = { 9 + enable = true; 10 + wifi.backend = "wpa_supplicant"; 11 + }; 12 + programs.nm-applet.enable = true; # cursed wifi widget 13 + 14 + # networking.firewall = { 15 + # enable = true; 16 + # allowedUDPPorts = [11311]; 17 + # }; 18 + 19 + # Enable CUPS to print documents. 20 + # services.printing.enable = true; 21 + 22 + # services.avahi = { 23 + # enable = true; 24 + # nssmdns4 = true; 25 + # openFirewall = true; 26 + # }; 27 + }
+80
modules/system.nix
··· 1 + { 2 + ... 3 + }: 4 + 5 + { 6 + nix.settings.trusted-users = [ 7 + "root" 8 + "@wheel" 9 + ]; 10 + 11 + # Use the systemd-boot EFI boot loader. 12 + boot.loader.systemd-boot = { 13 + enable = true; 14 + configurationLimit = 10; 15 + }; 16 + boot.loader.efi.canTouchEfiVariables = true; 17 + 18 + # Set your time zone. 19 + time.timeZone = "America/New_York"; 20 + 21 + # why is this needed again? 22 + systemd.user.extraConfig = '' 23 + DefaultEnvironment="PATH=/run/current-system/sw/bin" 24 + ''; 25 + 26 + # latest (not lts) kernel? 27 + # boot.kernelPackages = pkgs.linuxPackages_latest; 28 + 29 + programs.nh = { 30 + enable = true; 31 + clean.enable = true; 32 + clean.extraArgs = "--keep-since 4d --keep 3"; 33 + flake = "/home/asa/system"; 34 + }; 35 + 36 + # Select internationalisation properties. 37 + i18n.defaultLocale = "en_US.UTF-8"; 38 + 39 + nix.settings.experimental-features = [ 40 + "nix-command" 41 + "flakes" 42 + ]; 43 + 44 + # For framework 45 + services.fwupd.enable = true; 46 + 47 + # Define a user account. Don't forget to set a password with ‘passwd’. 48 + users.users.asa = { 49 + isNormalUser = true; 50 + extraGroups = [ 51 + "wheel" 52 + "networkmanager" 53 + "video" 54 + "kvm" 55 + "dialout" 56 + ]; 57 + }; 58 + 59 + hardware.graphics.enable = true; 60 + 61 + # This option defines the first version of NixOS you have installed on this particular machine, 62 + # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions. 63 + # 64 + # Most users should NEVER change this value after the initial install, for any reason, 65 + # even if you've upgraded your system to a new NixOS release. 66 + # 67 + # This value does NOT affect the Nixpkgs version your packages and OS are pulled from, 68 + # so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how 69 + # to actually do that. 70 + # 71 + # This value being lower than the current NixOS release does NOT mean your system is 72 + # out of date, out of support, or vulnerable. 73 + # 74 + # Do NOT change this value unless you have manually inspected all the changes it would make to your configuration, 75 + # and migrated your data accordingly. 76 + # 77 + # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion . 78 + system.stateVersion = "24.11"; # Did you read the comment? 79 + 80 + }
+24
programs/default.nix
··· 1 + { pkgs }: 2 + pkgs.lib.fix ( 3 + self: 4 + let 5 + callPackage = pkgs.lib.callPackageWith ( 6 + pkgs 7 + // self 8 + // { 9 + mkWrapper = import ../utils/wrap.nix pkgs; 10 + theme = import ../config/theme.nix; 11 + } 12 + ); 13 + allFiles = builtins.attrNames (builtins.readDir ./.); 14 + packageFiles = builtins.filter ( 15 + file: pkgs.lib.hasSuffix ".nix" file && file != "default.nix" 16 + ) allFiles; 17 + in 18 + builtins.listToAttrs ( 19 + map (file: { 20 + name = pkgs.lib.removeSuffix ".nix" file; 21 + value = callPackage ./${file} { }; 22 + }) packageFiles 23 + ) 24 + )
+48
programs/fuzzel-wrapped.nix
··· 1 + { 2 + pkgs, 3 + lib, 4 + mkWrapper, 5 + theme, 6 + fuzzel, 7 + ghostty-wrapped, 8 + }: 9 + let 10 + cfg = pkgs.writeText "config.ini" '' 11 + font=sans-serif:size=${theme.lengths.font-lg} 12 + use-bold=true 13 + dpi-aware=auto 14 + placeholder="system search..." 15 + prompt="" 16 + terminal="${lib.getExe ghostty-wrapped} -e" 17 + 18 + lines=12 19 + width=24 20 + 21 + horizontal-pad=16 22 + vertical-pad=${theme.lengths.padding} 23 + inner-pad=${theme.lengths.padding} 24 + 25 + [colors] 26 + background=${theme.colors.bg-darker}FF 27 + text=${theme.colors.fg-dim}FF 28 + prompt=${theme.colors.fg-dim}FF 29 + placeholder=${theme.colors.fg-dim}FF 30 + input=${theme.colors.fg-regular}FF 31 + match=${theme.colors.fg-regular}FF 32 + selection=${theme.colors.bg-regular}FF 33 + selection-text=${theme.colors.fg-regular}FF 34 + selection-match=${theme.colors.fg-regular}FF 35 + border=${theme.colors.cyan}FF 36 + 37 + [border] 38 + width=${theme.lengths.border-width} 39 + radius=${theme.lengths.border-radius} 40 + ''; 41 + in 42 + mkWrapper { 43 + pkg = fuzzel; 44 + fuzzel.prependFlags = [ 45 + "--config" 46 + "${cfg}" 47 + ]; 48 + }
+36
programs/ghostty-wrapped.nix
··· 1 + { 2 + pkgs, 3 + mkWrapper, 4 + theme, 5 + ghostty, 6 + }: 7 + let 8 + cfg = pkgs.writeText "config" '' 9 + # font-family = "Hack" 10 + font-size = ${theme.lengths.font-sm} 11 + 12 + theme = "OneHalfDark" 13 + 14 + window-padding-x = 8 15 + window-padding-y = 8 16 + window-padding-balance = false 17 + window-padding-color = extend 18 + 19 + window-decoration = none 20 + window-theme = auto 21 + 22 + confirm-close-surface = false 23 + 24 + shell-integration = detect 25 + shell-integration-features = true 26 + 27 + gtk-single-instance = true 28 + quit-after-last-window-closed = false 29 + ''; 30 + in 31 + mkWrapper { 32 + pkg = ghostty; 33 + ghostty.prependFlags = [ 34 + "--config-file=${cfg}" 35 + ]; 36 + }
+41
programs/helix-wrapped.nix
··· 1 + { 2 + pkgs, 3 + mkWrapper, 4 + helix, 5 + }: 6 + let 7 + cfg = pkgs.writeText "config.toml" '' 8 + theme = "zed_onedark" 9 + 10 + [keys.normal] 11 + tab = "goto_next_buffer" 12 + S-tab = "goto_previous_buffer" 13 + 14 + [editor] 15 + bufferline = "multiple" 16 + idle-timeout = 5 17 + completion-timeout = 5 18 + end-of-line-diagnostics = "hint" 19 + inline-diagnostics.cursor-line = "error" 20 + 21 + [editor.cursor-shape] 22 + insert = "bar" 23 + normal = "block" 24 + select = "underline" 25 + 26 + [editor.file-picker] 27 + hidden = true 28 + ignore = true 29 + # git-ignore = true 30 + 31 + [editor.soft-wrap] 32 + enable = true 33 + ''; 34 + in 35 + mkWrapper { 36 + pkg = helix; 37 + hx.prependFlags = [ 38 + "--config" 39 + "${cfg}" 40 + ]; 41 + }
+26
programs/mako-wrapped.nix
··· 1 + { 2 + pkgs, 3 + mkWrapper, 4 + theme, 5 + mako, 6 + }: 7 + let 8 + cfg = pkgs.writeText "config.ini" '' 9 + font=sans-serif medium ${theme.lengths.font-md}px 10 + padding=${theme.lengths.padding} 11 + background-color=#${theme.colors.bg-darker} 12 + text-color=#${theme.colors.fg-regular} 13 + border-size=${theme.lengths.border-width} 14 + border-radius=${theme.lengths.border-radius} 15 + border-color=#${theme.colors.purple} 16 + default-timeout=5000 17 + max-icon-size=32 18 + ''; 19 + in 20 + mkWrapper { 21 + pkg = mako; 22 + mako.prependFlags = [ 23 + "--config" 24 + "${cfg}" 25 + ]; 26 + }
+619
programs/niri-wrapped.nix
··· 1 + { 2 + pkgs, 3 + lib, 4 + mkWrapper, 5 + theme, 6 + niri, 7 + swaylock-wrapped, 8 + ghostty-wrapped, 9 + fuzzel-wrapped, 10 + wireplumber, 11 + brightnessctl, 12 + flatpak, 13 + }: 14 + let 15 + cfg = pkgs.writeText "config.kdl" '' 16 + // This config is in the KDL format: https://kdl.dev 17 + // "/-" comments out the following node. 18 + // Check the wiki for a full description of the configuration: 19 + // https://github.com/YaLTeR/niri/wiki/Configuration:-Overview 20 + 21 + spawn-at-startup "xwayland-satellite" 22 + 23 + // Temporary for xwayland 24 + environment { 25 + DISPLAY ":0" 26 + } 27 + 28 + // Input device configuration. 29 + // Find the full list of options on the wiki: 30 + // https://github.com/YaLTeR/niri/wiki/Configuration:-Input 31 + input { 32 + keyboard { 33 + xkb { 34 + // You can set rules, model, layout, variant and options. 35 + // For more information, see xkeyboard-config(7). 36 + 37 + // For example: 38 + // layout "us,ru" 39 + // options "grp:win_space_toggle,compose:ralt,ctrl:nocaps" 40 + options "caps:escape" 41 + } 42 + repeat-delay 300 43 + repeat-rate 32 44 + } 45 + 46 + // Next sections include libinput settings. 47 + // Omitting settings disables them, or leaves them at their default values. 48 + touchpad { 49 + // off 50 + // tap 51 + // dwt 52 + // dwtp 53 + // drag-lock 54 + natural-scroll 55 + // accel-speed 0.2 56 + accel-profile "adaptive" 57 + scroll-method "two-finger" 58 + click-method "clickfinger" 59 + scroll-factor 0.3 60 + // disabled-on-external-mouse 61 + } 62 + 63 + mouse { 64 + // off 65 + // natural-scroll 66 + // accel-speed 0.2 67 + // accel-profile "flat" 68 + // scroll-method "no-scroll" 69 + } 70 + 71 + trackpoint { 72 + // off 73 + // natural-scroll 74 + // accel-speed 0.2 75 + // accel-profile "flat" 76 + // scroll-method "on-button-down" 77 + // scroll-button 273 78 + // middle-emulation 79 + } 80 + 81 + // Uncomment this to make the mouse warp to the center of newly focused windows. 82 + // warp-mouse-to-focus 83 + 84 + // Focus windows and outputs automatically when moving the mouse into them. 85 + // Setting max-scroll-amount="0%" makes it work only on windows already fully on screen. 86 + // focus-follows-mouse max-scroll-amount="0%" 87 + } 88 + 89 + output "DP-3" { 90 + mode "3840x2160@30.000" 91 + } 92 + 93 + // You can configure outputs by their name, which you can find 94 + // by running `niri msg outputs` while inside a niri instance. 95 + // The built-in laptop monitor is usually called "eDP-1". 96 + // Find more information on the wiki: 97 + // https://github.com/YaLTeR/niri/wiki/Configuration:-Outputs 98 + // Remember to uncomment the node by removing "/-"! 99 + output "eDP-1" { 100 + // Uncomment this line to disable this output. 101 + // off 102 + 103 + // Resolution and, optionally, refresh rate of the output. 104 + // The format is "<width>x<height>" or "<width>x<height>@<refresh rate>". 105 + // If the refresh rate is omitted, niri will pick the highest refresh rate 106 + // for the resolution. 107 + // If the mode is omitted altogether or is invalid, niri will pick one automatically. 108 + // Run `niri msg outputs` while inside a niri instance to list all outputs and their modes. 109 + mode "2256x1504@60" 110 + 111 + // You can use integer or fractional scale, for example use 1.5 for 150% scale. 112 + scale 1.5 113 + 114 + // Transform allows to rotate the output counter-clockwise, valid values are: 115 + // normal, 90, 180, 270, flipped, flipped-90, flipped-180 and flipped-270. 116 + transform "normal" 117 + 118 + background-color "#${theme.colors.bg-darker}" 119 + 120 + // Position of the output in the global coordinate space. 121 + // This affects directional monitor actions like "focus-monitor-left", and cursor movement. 122 + // The cursor can only move between directly adjacent outputs. 123 + // Output scale and rotation has to be taken into account for positioning: 124 + // outputs are sized in logical, or scaled, pixels. 125 + // For example, a 3840×2160 output with scale 2.0 will have a logical size of 1920×1080, 126 + // so to put another output directly adjacent to it on the right, set its x to 1920. 127 + // If the position is unset or results in an overlap, the output is instead placed 128 + // automatically. 129 + // position x=1280 y=0 130 + } 131 + 132 + output "DP-3" { 133 + // Uncomment this line to disable this output. 134 + // off 135 + 136 + // Resolution and, optionally, refresh rate of the output. 137 + // The format is "<width>x<height>" or "<width>x<height>@<refresh rate>". 138 + // If the refresh rate is omitted, niri will pick the highest refresh rate 139 + // for the resolution. 140 + // If the mode is omitted altogether or is invalid, niri will pick one automatically. 141 + // Run `niri msg outputs` while inside a niri instance to list all outputs and their modes. 142 + mode "2560x1440@60" 143 + 144 + // You can use integer or fractional scale, for example use 1.5 for 150% scale. 145 + scale 1.25 146 + 147 + // Transform allows to rotate the output counter-clockwise, valid values are: 148 + // normal, 90, 180, 270, flipped, flipped-90, flipped-180 and flipped-270. 149 + transform "normal" 150 + 151 + background-color "#${theme.colors.bg-darker}" 152 + 153 + // Position of the output in the global coordinate space. 154 + // This affects directional monitor actions like "focus-monitor-left", and cursor movement. 155 + // The cursor can only move between directly adjacent outputs. 156 + // Output scale and rotation has to be taken into account for positioning: 157 + // outputs are sized in logical, or scaled, pixels. 158 + // For example, a 3840×2160 output with scale 2.0 will have a logical size of 1920×1080, 159 + // so to put another output directly adjacent to it on the right, set its x to 1920. 160 + // If the position is unset or results in an overlap, the output is instead placed 161 + // automatically. 162 + // position x=1280 y=0 163 + } 164 + 165 + // Settings that influence how windows are positioned and sized. 166 + // Find more information on the wiki: 167 + // https://github.com/YaLTeR/niri/wiki/Configuration:-Layout 168 + layout { 169 + // Set gaps around windows in logical pixels. 170 + gaps 4 171 + 172 + // When to center a column when changing focus, options are: 173 + // - "never", default behavior, focusing an off-screen column will keep at the left 174 + // or right edge of the screen. 175 + // - "always", the focused column will always be centered. 176 + // - "on-overflow", focusing a column will center it if it doesn't fit 177 + // together with the previously focused column. 178 + center-focused-column "never" 179 + 180 + // You can customize the widths that "switch-preset-column-width" (Mod+R) toggles between. 181 + preset-column-widths { 182 + // Proportion sets the width as a fraction of the output width, taking gaps into account. 183 + // For example, you can perfectly fit four windows sized "proportion 0.25" on an output. 184 + // The default preset widths are 1/3, 1/2 and 2/3 of the output. 185 + proportion 0.33333 186 + proportion 0.5 187 + proportion 0.66667 188 + 189 + // Fixed sets the width in logical pixels exactly. 190 + // fixed 1920 191 + } 192 + 193 + // You can also customize the heights that "switch-preset-window-height" (Mod+Shift+R) toggles between. 194 + // preset-window-heights { } 195 + 196 + // You can change the default width of the new windows. 197 + default-column-width { proportion 0.5; } 198 + // If you leave the brackets empty, the windows themselves will decide their initial width. 199 + // default-column-width {} 200 + 201 + // By default focus ring and border are rendered as a solid background rectangle 202 + // behind windows. That is, they will show up through semitransparent windows. 203 + // This is because windows using client-side decorations can have an arbitrary shape. 204 + // 205 + // If you don't like that, you should uncomment `prefer-no-csd` below. 206 + // Niri will draw focus ring and border *around* windows that agree to omit their 207 + // client-side decorations. 208 + // 209 + // Alternatively, you can override it with a window rule called 210 + // `draw-border-with-background`. 211 + 212 + focus-ring { 213 + off 214 + } 215 + 216 + // You can also add a border. It's similar to the focus ring, but always visible. 217 + border { 218 + width ${theme.lengths.border-width} 219 + active-color "#${theme.colors.cyan}" 220 + inactive-color "#${theme.colors.gray}" 221 + } 222 + 223 + // You can enable drop shadows for windows. 224 + shadow { 225 + // Uncomment the next line to enable shadows. 226 + // on 227 + 228 + // By default, the shadow draws only around its window, and not behind it. 229 + // Uncomment this setting to make the shadow draw behind its window. 230 + // 231 + // Note that niri has no way of knowing about the CSD window corner 232 + // radius. It has to assume that windows have square corners, leading to 233 + // shadow artifacts inside the CSD rounded corners. This setting fixes 234 + // those artifacts. 235 + // 236 + // However, instead you may want to set prefer-no-csd and/or 237 + // geometry-corner-radius. Then, niri will know the corner radius and 238 + // draw the shadow correctly, without having to draw it behind the 239 + // window. These will also remove client-side shadows if the window 240 + // draws any. 241 + // 242 + // draw-behind-window true 243 + 244 + // You can change how shadows look. The values below are in logical 245 + // pixels and match the CSS box-shadow properties. 246 + 247 + // Softness controls the shadow blur radius. 248 + softness 30 249 + 250 + // Spread expands the shadow. 251 + spread 5 252 + 253 + // Offset moves the shadow relative to the window. 254 + offset x=0 y=5 255 + 256 + // You can also change the shadow color and opacity. 257 + color "#0007" 258 + } 259 + 260 + // Struts shrink the area occupied by windows, similarly to layer-shell panels. 261 + // You can think of them as a kind of outer gaps. They are set in logical pixels. 262 + // Left and right struts will cause the next window to the side to always be visible. 263 + // Top and bottom struts will simply add outer gaps in addition to the area occupied by 264 + // layer-shell panels and regular gaps. 265 + struts { 266 + // left 64 267 + // right 64 268 + // top 64 269 + // bottom 64 270 + } 271 + } 272 + 273 + // Add lines like this to spawn processes at startup. 274 + // Note that running niri as a session supports xdg-desktop-autostart, 275 + // which may be more convenient to use. 276 + // See the binds section below for more spawn examples. 277 + // spawn-at-startup "alacritty" "-e" "bash" 278 + 279 + // Uncomment this line to ask the clients to omit their client-side decorations if possible. 280 + // If the client will specifically ask for CSD, the request will be honored. 281 + // Additionally, clients will be informed that they are tiled, removing some client-side rounded corners. 282 + // This option will also fix border/focus ring drawing behind some semitransparent windows. 283 + // After enabling or disabling this, you need to restart the apps for this to take effect. 284 + prefer-no-csd 285 + 286 + // You can change the path where screenshots are saved. 287 + // A ~ at the front will be expanded to the home directory. 288 + // The path is formatted with strftime(3) to give you the screenshot date and time. 289 + screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png" 290 + 291 + // You can also set this to null to disable saving screenshots to disk. 292 + // screenshot-path null 293 + 294 + // Animation settings. 295 + // The wiki explains how to configure individual animations: 296 + // https://github.com/YaLTeR/niri/wiki/Configuration:-Animations 297 + animations { 298 + // Uncomment to turn off all animations. 299 + // off 300 + 301 + // Slow down all animations by this factor. Values below 1 speed them up instead. 302 + slowdown 0.8 303 + } 304 + 305 + // Window rules let you adjust behavior for individual windows. 306 + // Find more information on the wiki: 307 + // https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules 308 + 309 + // Work around WezTerm's initial configure bug 310 + // by setting an empty default-column-width. 311 + window-rule { 312 + // This regular expression is intentionally made as specific as possible, 313 + // since this is the default config, and we want no false positives. 314 + // You can get away with just app-id="wezterm" if you want. 315 + match app-id=r#"^org\.wezfurlong\.wezterm$"# 316 + default-column-width {} 317 + } 318 + 319 + // Open the Firefox picture-in-picture player as floating by default. 320 + window-rule { 321 + // This app-id regular expression will work for both: 322 + // - host Firefox (app-id is "firefox") 323 + // - Flatpak Firefox (app-id is "org.mozilla.firefox") 324 + match app-id=r#"firefox$"# title="^Picture-in-Picture$" 325 + open-floating true 326 + } 327 + 328 + // Example: block out two password managers from screen capture. 329 + // (This example rule is commented out with a "/-" in front.) 330 + /-window-rule { 331 + match app-id=r#"^org\.keepassxc\.KeePassXC$"# 332 + match app-id=r#"^org\.gnome\.World\.Secrets$"# 333 + 334 + block-out-from "screen-capture" 335 + 336 + // Use this instead if you want them visible on third-party screenshot tools. 337 + // block-out-from "screencast" 338 + } 339 + 340 + // Example: enable rounded corners for all windows. 341 + // (This example rule is commented out with a "/-" in front.) 342 + window-rule { 343 + geometry-corner-radius ${theme.lengths.border-radius} 344 + clip-to-geometry true 345 + } 346 + 347 + // window-rule { 348 + // match is-floating=true 349 + // baba-is-float true 350 + // } 351 + 352 + binds { 353 + // Keys consist of modifiers separated by + signs, followed by an XKB key name 354 + // in the end. To find an XKB name for a particular key, you may use a program 355 + // like wev. 356 + // 357 + // "Mod" is a special modifier equal to Super when running on a TTY, and to Alt 358 + // when running as a winit window. 359 + // 360 + // Most actions that you can bind here can also be invoked programmatically with 361 + // `niri msg action do-something`. 362 + 363 + // Mod-Shift-/, which is usually the same as Mod-?, 364 + // shows a list of important hotkeys. 365 + Mod+Shift+Slash { show-hotkey-overlay; } 366 + 367 + // Suggested binds for running programs: terminal, app launcher, screen locker. 368 + Mod+Return { spawn "${lib.getExe ghostty-wrapped}"; } 369 + Mod+D { spawn "${lib.getExe fuzzel-wrapped}"; } 370 + Mod+Shift+Return { spawn "${lib.getExe flatpak}" "run" "org.mozilla.firefox"; } 371 + Super+Alt+L { spawn "${lib.getExe swaylock-wrapped}"; } 372 + 373 + // You can also use a shell. Do this if you need pipes, multiple commands, etc. 374 + // Note: the entire command goes as a single argument in the end. 375 + // Mod+T { spawn "bash" "-c" "notify-send hello && exec alacritty"; } 376 + 377 + // Example volume keys mappings for PipeWire & WirePlumber. 378 + // The allow-when-locked=true property makes them work even when the session is locked. 379 + XF86AudioRaiseVolume allow-when-locked=true { spawn "${lib.getExe' wireplumber "wpctl"}" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.08+"; } 380 + XF86AudioLowerVolume allow-when-locked=true { spawn "${lib.getExe' wireplumber "wpctl"}" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.08-"; } 381 + XF86AudioMute allow-when-locked=true { spawn "${lib.getExe' wireplumber "wpctl"}" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; } 382 + XF86AudioMicMute allow-when-locked=true { spawn "${lib.getExe' wireplumber "wpctl"}" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; } 383 + XF86MonBrightnessDown allow-when-locked=true {spawn "${lib.getExe' brightnessctl "brightnessctl"}" "-e" "set" "10%-"; } 384 + XF86MonBrightnessUp allow-when-locked=true {spawn "${lib.getExe brightnessctl}" "-e" "set" "+10%"; } 385 + 386 + Mod+Ctrl+Shift+Alt+L { spawn "${lib.getExe flatpak}" "run" "org.mozilla.firefox" "--new-window" "https://www.linkedin.com/"; } 387 + 388 + Mod+Q { close-window; } 389 + 390 + Mod+Left { focus-column-left; } 391 + Mod+Down { focus-window-down; } 392 + Mod+Up { focus-window-up; } 393 + Mod+Right { focus-column-right; } 394 + Mod+H { focus-column-left; } 395 + Mod+J { focus-window-down; } 396 + Mod+K { focus-window-up; } 397 + Mod+L { focus-column-right; } 398 + 399 + Mod+Shift+Left { move-column-left; } 400 + Mod+Shift+Down { move-window-down; } 401 + Mod+Shift+Up { move-window-up; } 402 + Mod+Shift+Right { move-column-right; } 403 + Mod+Shift+H { move-column-left; } 404 + Mod+Shift+J { move-window-down; } 405 + Mod+Shift+K { move-window-up; } 406 + Mod+Shift+L { move-column-right; } 407 + 408 + // Alternative commands that move across workspaces when reaching 409 + // the first or last window in a column. 410 + // Mod+J { focus-window-or-workspace-down; } 411 + // Mod+K { focus-window-or-workspace-up; } 412 + // Mod+Ctrl+J { move-window-down-or-to-workspace-down; } 413 + // Mod+Ctrl+K { move-window-up-or-to-workspace-up; } 414 + 415 + Mod+Home { focus-column-first; } 416 + Mod+End { focus-column-last; } 417 + Mod+Shift+Home { move-column-to-first; } 418 + Mod+Shift+End { move-column-to-last; } 419 + 420 + Mod+Control+Left { focus-monitor-left; } 421 + Mod+Control+Down { focus-monitor-down; } 422 + Mod+Control+Up { focus-monitor-up; } 423 + Mod+Control+Right { focus-monitor-right; } 424 + Mod+Control+H { focus-monitor-left; } 425 + Mod+Control+J { focus-monitor-down; } 426 + Mod+Control+K { focus-monitor-up; } 427 + Mod+Control+L { focus-monitor-right; } 428 + 429 + Mod+Shift+Ctrl+Left { move-column-to-monitor-left; } 430 + Mod+Shift+Ctrl+Down { move-column-to-monitor-down; } 431 + Mod+Shift+Ctrl+Up { move-column-to-monitor-up; } 432 + Mod+Shift+Ctrl+Right { move-column-to-monitor-right; } 433 + Mod+Shift+Ctrl+H { move-column-to-monitor-left; } 434 + Mod+Shift+Ctrl+J { move-column-to-monitor-down; } 435 + Mod+Shift+Ctrl+K { move-column-to-monitor-up; } 436 + Mod+Shift+Ctrl+L { move-column-to-monitor-right; } 437 + 438 + // Alternatively, there are commands to move just a single window: 439 + // Mod+Shift+Ctrl+Left { move-window-to-monitor-left; } 440 + // ... 441 + 442 + // And you can also move a whole workspace to another monitor: 443 + // Mod+Shift+Ctrl+Left { move-workspace-to-monitor-left; } 444 + // ... 445 + 446 + Mod+Page_Down { focus-workspace-down; } 447 + Mod+Page_Up { focus-workspace-up; } 448 + Mod+U { focus-workspace-down; } 449 + Mod+I { focus-workspace-up; } 450 + Mod+Shift+Page_Down { move-column-to-workspace-down; } 451 + Mod+Shift+Page_Up { move-column-to-workspace-up; } 452 + Mod+Shift+U { move-column-to-workspace-down; } 453 + Mod+Shift+I { move-column-to-workspace-up; } 454 + 455 + // Alternatively, there are commands to move just a single window: 456 + // Mod+Ctrl+Page_Down { move-window-to-workspace-down; } 457 + // ... 458 + 459 + Mod+Ctrl+Page_Down { move-workspace-down; } 460 + Mod+Ctrl+Page_Up { move-workspace-up; } 461 + Mod+Ctrl+U { move-workspace-down; } 462 + Mod+Ctrl+I { move-workspace-up; } 463 + 464 + Mod+S { toggle-overview; } 465 + 466 + // You can bind mouse wheel scroll ticks using the following syntax. 467 + // These binds will change direction based on the natural-scroll setting. 468 + // 469 + // To avoid scrolling through workspaces really fast, you can use 470 + // the cooldown-ms property. The bind will be rate-limited to this value. 471 + // You can set a cooldown on any bind, but it's most useful for the wheel. 472 + Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; } 473 + Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; } 474 + Mod+Shift+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; } 475 + Mod+Shift+WheelScrollUp cooldown-ms=150 { move-column-to-workspace-up; } 476 + 477 + Mod+WheelScrollRight { focus-column-right; } 478 + Mod+WheelScrollLeft { focus-column-left; } 479 + Mod+Ctrl+WheelScrollRight { move-column-right; } 480 + Mod+Ctrl+WheelScrollLeft { move-column-left; } 481 + 482 + // Usually scrolling up and down with Shift in applications results in 483 + // horizontal scrolling; these binds replicate that. 484 + Mod+Ctrl+WheelScrollDown { focus-column-right; } 485 + Mod+Ctrl+WheelScrollUp { focus-column-left; } 486 + Mod+Ctrl+Shift+WheelScrollDown { move-column-right; } 487 + Mod+Ctrl+Shift+WheelScrollUp { move-column-left; } 488 + 489 + // Similarly, you can bind touchpad scroll "ticks". 490 + // Touchpad scrolling is continuous, so for these binds it is split into 491 + // discrete intervals. 492 + // These binds are also affected by touchpad's natural-scroll, so these 493 + // example binds are "inverted", since we have natural-scroll enabled for 494 + // touchpads by default. 495 + // Mod+TouchpadScrollDown { spawn "${lib.getExe' wireplumber "wpctl"}" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.02+"; } 496 + // Mod+TouchpadScrollUp { spawn "${lib.getExe' wireplumber "wpctl"}" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.02-"; } 497 + 498 + // You can refer to workspaces by index. However, keep in mind that 499 + // niri is a dynamic workspace system, so these commands are kind of 500 + // "best effort". Trying to refer to a workspace index bigger than 501 + // the current workspace count will instead refer to the bottommost 502 + // (empty) workspace. 503 + // 504 + // For example, with 2 workspaces + 1 empty, indices 3, 4, 5 and so on 505 + // will all refer to the 3rd workspace. 506 + Mod+1 { focus-workspace 1; } 507 + Mod+2 { focus-workspace 2; } 508 + Mod+3 { focus-workspace 3; } 509 + Mod+4 { focus-workspace 4; } 510 + Mod+5 { focus-workspace 5; } 511 + Mod+6 { focus-workspace 6; } 512 + Mod+7 { focus-workspace 7; } 513 + Mod+8 { focus-workspace 8; } 514 + Mod+9 { focus-workspace 9; } 515 + Mod+Shift+1 { move-column-to-workspace 1; } 516 + Mod+Shift+2 { move-column-to-workspace 2; } 517 + Mod+Shift+3 { move-column-to-workspace 3; } 518 + Mod+Shift+4 { move-column-to-workspace 4; } 519 + Mod+Shift+5 { move-column-to-workspace 5; } 520 + Mod+Shift+6 { move-column-to-workspace 6; } 521 + Mod+Shift+7 { move-column-to-workspace 7; } 522 + Mod+Shift+8 { move-column-to-workspace 8; } 523 + Mod+Shift+9 { move-column-to-workspace 9; } 524 + 525 + // Alternatively, there are commands to move just a single window: 526 + // Mod+Ctrl+1 { move-window-to-workspace 1; } 527 + 528 + // Switches focus between the current and the previous workspace. 529 + // Mod+Tab { focus-workspace-previous; } 530 + 531 + // The following binds move the focused window in and out of a column. 532 + // If the window is alone, they will consume it into the nearby column to the side. 533 + // If the window is already in a column, they will expel it out. 534 + Mod+BracketLeft { consume-or-expel-window-left; } 535 + Mod+BracketRight { consume-or-expel-window-right; } 536 + 537 + // Consume one window from the right to the bottom of the focused column. 538 + Mod+Comma { consume-window-into-column; } 539 + // Expel the bottom window from the focused column to the right. 540 + Mod+Period { expel-window-from-column; } 541 + 542 + Mod+R { switch-preset-column-width; } 543 + Mod+Shift+R { switch-preset-window-height; } 544 + Mod+Ctrl+R { reset-window-height; } 545 + Mod+F { maximize-column; } 546 + Mod+Shift+F { fullscreen-window; } 547 + 548 + // Expand the focused column to space not taken up by other fully visible columns. 549 + // Makes the column "fill the rest of the space". 550 + Mod+Ctrl+F { expand-column-to-available-width; } 551 + 552 + Mod+C { center-column; } 553 + 554 + // Finer width adjustments. 555 + // This command can also: 556 + // * set width in pixels: "1000" 557 + // * adjust width in pixels: "-5" or "+5" 558 + // * set width as a percentage of screen width: "25%" 559 + // * adjust width as a percentage of screen width: "-10%" or "+10%" 560 + // Pixel sizes use logical, or scaled, pixels. I.e. on an output with scale 2.0, 561 + // set-column-width "100" will make the column occupy 200 physical screen pixels. 562 + Mod+Minus { set-column-width "-10%"; } 563 + Mod+Equal { set-column-width "+10%"; } 564 + 565 + // Finer height adjustments when in column with other windows. 566 + Mod+Shift+Minus { set-window-height "-10%"; } 567 + Mod+Shift+Equal { set-window-height "+10%"; } 568 + 569 + // Move the focused window between the floating and the tiling layout. 570 + Mod+V { toggle-window-floating; } 571 + Mod+Shift+V { switch-focus-between-floating-and-tiling; } 572 + 573 + // Toggle tabbed column display mode. 574 + // Windows in this column will appear as vertical tabs, 575 + // rather than stacked on top of each other. 576 + Mod+W { toggle-column-tabbed-display; } 577 + 578 + // Actions to switch layouts. 579 + // Note: if you uncomment these, make sure you do NOT have 580 + // a matching layout switch hotkey configured in xkb options above. 581 + // Having both at once on the same hotkey will break the switching, 582 + // since it will switch twice upon pressing the hotkey (once by xkb, once by niri). 583 + // Mod+Space { switch-layout "next"; } 584 + // Mod+Shift+Space { switch-layout "prev"; } 585 + 586 + Print { screenshot; } 587 + Ctrl+Print { screenshot-screen; } 588 + Alt+Print { screenshot-window; } 589 + Super+P { screenshot; } 590 + Ctrl+Super+P { screenshot-screen; } 591 + Alt+Super+P { screenshot-window; } 592 + 593 + // Applications such as remote-desktop clients and software KVM switches may 594 + // request that niri stops processing the keyboard shortcuts defined here 595 + // so they may, for example, forward the key presses as-is to a remote machine. 596 + // It's a good idea to bind an escape hatch to toggle the inhibitor, 597 + // so a buggy application can't hold your session hostage. 598 + // 599 + // The allow-inhibiting=false property can be applied to other binds as well, 600 + // which ensures niri always processes them, even when an inhibitor is active. 601 + Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; } 602 + 603 + // The quit action will show a confirmation dialog to avoid accidental exits. 604 + Mod+Shift+E { quit; } 605 + Ctrl+Alt+Delete { quit; } 606 + 607 + // Powers off the monitors. To turn them back on, do any input like 608 + // moving the mouse or pressing any other key. 609 + Mod+Shift+P { power-off-monitors; } 610 + } 611 + ''; 612 + in 613 + mkWrapper { 614 + pkg = niri; 615 + niri.prependFlags = [ 616 + "--config" 617 + "${cfg}" 618 + ]; 619 + }
+54
programs/swaylock-wrapped.nix
··· 1 + { 2 + pkgs, 3 + mkWrapper, 4 + theme, 5 + swaylock, 6 + }: 7 + let 8 + cfg = pkgs.writeText "config.ini" '' 9 + scaling=fill 10 + font="Inter Display" 11 + font-size=30 12 + indicator-radius=64px 13 + indicator-thickness=6px 14 + 15 + color=${theme.colors.bg-darker} 16 + text-color=${theme.colors.fg-regular} 17 + text-clear-color=${theme.colors.fg-regular} 18 + text-wrong-color=${theme.colors.fg-regular} 19 + text-ver-color=${theme.colors.fg-regular} 20 + text-caps-lock-color=${theme.colors.fg-regular} 21 + 22 + bs-hl-color=${theme.colors.red} 23 + caps-lock-bs-hl-color=${theme.colors.red} 24 + key-hl-color=${theme.colors.blue} 25 + caps-lock-key-hl-color=${theme.colors.blue} 26 + 27 + separator-color=${theme.colors.bg-darker} 28 + 29 + layout-bg-color=${theme.colors.bg-darker} 30 + layout-border-color=${theme.colors.bg-darker} 31 + layout-text-color=${theme.colors.fg-regular} 32 + 33 + inside-color=${theme.colors.bg-darker} 34 + inside-clear-color=${theme.colors.bg-darker} 35 + inside-caps-lock-color=${theme.colors.bg-darker} 36 + inside-ver-color=${theme.colors.bg-darker} 37 + inside-wrong-color=${theme.colors.bg-darker} 38 + 39 + ring-color=${theme.colors.bg-darker} 40 + ring-clear-color=${theme.colors.yellow} 41 + ring-caps-lock-color=${theme.colors.purple} 42 + ring-ver-color=${theme.colors.blue} 43 + ring-wrong-color=${theme.colors.red} 44 + 45 + line-uses-inside 46 + ''; 47 + in 48 + mkWrapper { 49 + pkg = swaylock; 50 + swaylock.prependFlags = [ 51 + "--config" 52 + "${cfg}" 53 + ]; 54 + }
+370
programs/waybar-wrapped.nix
··· 1 + # additional @args needed to forward systemdSupport to mkWrapper 2 + { 3 + pkgs, 4 + lib, 5 + mkWrapper, 6 + theme, 7 + waybar, 8 + pavucontrol, 9 + networkmanagerapplet, 10 + ... 11 + }@args: 12 + let 13 + cfg = pkgs.writeText "config.jsonc" '' 14 + { 15 + "layer": "top", // Waybar at top layer 16 + // "position": "bottom", // Waybar position (top|bottom|left|right) 17 + "height": 26, // Waybar height (to be removed for auto height) 18 + // "width": 1280, // Waybar width 19 + "spacing": 4, // Gaps between modules (4px) 20 + "margin-top": 4, 21 + "margin-left": 4, 22 + "margin-right": 4, 23 + 24 + "modules-left": [ 25 + "niri/workspaces" 26 + ], 27 + 28 + "modules-center": [ 29 + "niri/window" 30 + ], 31 + 32 + "modules-right": [ 33 + // "mpd", 34 + // "idle_inhibitor", 35 + // "power-profiles-daemon", 36 + // "cpu", 37 + // "memory", 38 + // "temperature", 39 + // "backlight", 40 + // "keyboard-state", 41 + // "sway/language", 42 + "pulseaudio", 43 + "network", 44 + "battery", 45 + // "battery#bat2", 46 + "clock", 47 + "tray" 48 + ], 49 + 50 + "sway/mode": { 51 + "format": "<span style=\"italic\">{}</span>" 52 + }, 53 + 54 + "sway/scratchpad": { 55 + "format": "{icon} {count}", 56 + "show-empty": false, 57 + "format-icons": ["", ""], 58 + "tooltip": true, 59 + "tooltip-format": "{app}: {title}" 60 + }, 61 + 62 + "mpd": { 63 + "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ {volume}% ", 64 + "format-disconnected": "Disconnected ", 65 + "format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ", 66 + "unknown-tag": "N/A", 67 + "interval": 5, 68 + "consume-icons": { 69 + "on": " " 70 + }, 71 + "random-icons": { 72 + "off": "<span color=\"#f53c3c\"></span> ", 73 + "on": " " 74 + }, 75 + "repeat-icons": { 76 + "on": " " 77 + }, 78 + "single-icons": { 79 + "on": "1 " 80 + }, 81 + "state-icons": { 82 + "paused": "", 83 + "playing": "" 84 + }, 85 + "tooltip-format": "MPD (connected)", 86 + "tooltip-format-disconnected": "MPD (disconnected)" 87 + }, 88 + "idle_inhibitor": { 89 + "format": "{icon}", 90 + "format-icons": { 91 + "activated": "", 92 + "deactivated": "" 93 + } 94 + }, 95 + "tray": { 96 + "icon-size": 16, 97 + "spacing": 10 98 + }, 99 + "clock": { 100 + "format": "{:%H:%M}", 101 + "format-alt": "{:%A, %B %d, %R}", 102 + "tooltip-format": "<tt><small>{calendar}</small></tt>", 103 + "calendar": { 104 + "mode" : "year", 105 + "mode-mon-col" : 3, 106 + "on-scroll" : 1, 107 + "format": { 108 + "months": "<span color='#ffead3'><b>{}</b></span>", 109 + "days": "<span color='#ecc6d9'><b>{}</b></span>", 110 + "weeks": "<span color='#99ffdd'><b>W{}</b></span>", 111 + "weekdays": "<span color='#ffcc66'><b>{}</b></span>", 112 + "today": "<span color='#ff6699'><b><u>{}</u></b></span>" 113 + } 114 + }, 115 + "actions": { 116 + "on-click-right": "mode", 117 + "on-scroll-up": "tz_up", 118 + "on-scroll-down": "tz_down", 119 + "on-scroll-up": "shift_up", 120 + "on-scroll-down": "shift_down" 121 + } 122 + }, 123 + "cpu": { 124 + "format": "{usage}% ", 125 + "tooltip": false 126 + }, 127 + "memory": { 128 + "format": "{}% " 129 + }, 130 + "temperature": { 131 + // "thermal-zone": 2, 132 + // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input", 133 + "critical-threshold": 80, 134 + // "format-critical": "{temperatureC}°C {icon}", 135 + "format": "{temperatureC}°C {icon}", 136 + "format-icons": ["", "", ""] 137 + }, 138 + "backlight": { 139 + // "device": "acpi_video1", 140 + "format": "{percent}% {icon}", 141 + "format-icons": ["", "", "", "", "", "", "", "", ""] 142 + }, 143 + "battery": { 144 + "states": { 145 + "good": 95, 146 + "warning": 30, 147 + "critical": 15 148 + }, 149 + "format": "{capacity}% {icon} ", 150 + "format-full": "{capacity}% {icon} ", 151 + "format-charging": "{capacity}% ", 152 + "format-plugged": "{capacity}% ", 153 + "format-alt": "{time} {icon}", 154 + "format-icons": ["", "", "", "", ""] 155 + }, 156 + "power-profiles-daemon": { 157 + "format": "{icon}", 158 + "tooltip-format": "Power profile: {profile}\nDriver: {driver}", 159 + "tooltip": true, 160 + "format-icons": { 161 + "default": "", 162 + "performance": "", 163 + "balanced": "", 164 + "power-saver": "" 165 + } 166 + }, 167 + "network": { 168 + // "interface": "wlp2*", // (Optional) To force the use of this interface 169 + // "format-wifi": "{essid} ({signalStrength}%)  ", 170 + "format-wifi": "{signalStrength}%  ", 171 + "format-ethernet": "{ipaddr}/{cidr} ", 172 + "tooltip-format": "{ifname} via {gwaddr} ", 173 + "format-linked": "{ifname} (No IP) ", 174 + "format-disconnected": "Disconnected ⚠", 175 + // "format-alt": "{ifname}: {ipaddr}/{cidr}", 176 + "on-click": "${lib.getExe' networkmanagerapplet "nm-connection-editor"}" 177 + }, 178 + "pulseaudio": { 179 + "scroll-step": 1, // %, can be a float 180 + "format": "{volume}% {icon} {format_source}", 181 + "format-bluetooth": "{volume}% {icon}  {format_source}", 182 + "format-bluetooth-muted": "{icon}  {format_source}", 183 + "format-muted": "{format_source}", 184 + "format-source": "{volume}% ", 185 + "format-source-muted": "", 186 + "format-icons": { 187 + "headphone": "", 188 + "hands-free": "", 189 + "headset": "", 190 + "phone": "", 191 + "portable": "", 192 + "car": "", 193 + "default": ["", "", ""] 194 + }, 195 + "on-click": "${lib.getExe pavucontrol}" 196 + }, 197 + "custom/media": { 198 + "format": "{icon} {}", 199 + "return-type": "json", 200 + "max-length": 40, 201 + "format-icons": { 202 + "spotify": "", 203 + "default": "🎜" 204 + }, 205 + "escape": true, 206 + "exec": "$HOME/.config/waybar/mediaplayer.py 2> /dev/null" // Script in resources folder 207 + // "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name 208 + }, 209 + "custom/power": { 210 + "format" : "⏻ ", 211 + "tooltip": false, 212 + "menu": "on-click", 213 + "menu-file": "$HOME/.config/waybar/power_menu.xml", // Menu file in resources folder 214 + "menu-actions": { 215 + "shutdown": "shutdown", 216 + "reboot": "reboot", 217 + "suspend": "systemctl suspend", 218 + "hibernate": "systemctl hibernate" 219 + } 220 + } 221 + } 222 + ''; 223 + style = pkgs.writeText "style.css" '' 224 + * { 225 + font-family: "sans-serif", "Noto Monochrome Emoji", "Font Awesome 5 Pro Solid", "Font Awesome 5 Brands"; 226 + font-size: ${theme.lengths.font-md}px; 227 + font-weight: 600; 228 + color: #${theme.colors.fg-regular}; 229 + } 230 + 231 + /* Reset styling for buttons */ 232 + button { 233 + all: unset; 234 + } 235 + 236 + window#waybar { 237 + background: transparent; 238 + } 239 + 240 + #workspaces button.focused, 241 + #workspaces button:hover { 242 + border-color: #${theme.colors.cyan}; 243 + } 244 + 245 + #workspaces button.urgent { 246 + border-color: #${theme.colors.red}; 247 + } 248 + 249 + #mode, 250 + #clock, 251 + #battery, 252 + #cpu, 253 + #memory, 254 + #disk, 255 + #temperature, 256 + #backlight, 257 + #network, 258 + #pulseaudio, 259 + #wireplumber, 260 + #custom-media, 261 + #tray, 262 + #mode, 263 + #idle_inhibitor, 264 + #scratchpad, 265 + #power-profiles-daemon, 266 + #mpd, 267 + #window, 268 + #workspaces button { 269 + padding: 4px 8px; 270 + background-color: #${theme.colors.bg-regular}; 271 + border-style: solid; 272 + border-width: ${theme.lengths.border-width}px; 273 + border-radius: ${theme.lengths.border-radius}px; 274 + border-color: #${theme.colors.gray}; 275 + } 276 + 277 + /* Add spacing in between each workspace button */ 278 + #workspaces button { 279 + margin-right: 4px; 280 + } 281 + 282 + /* Prevent duplicated margin from rightmost workspace button */ 283 + #workspaces button:last-child { 284 + margin-right: 0; 285 + } 286 + 287 + #waybar.empty #window { 288 + background-color: transparent; 289 + border-width: 0px 290 + } 291 + 292 + #clock { 293 + color: #${theme.colors.purple}; 294 + } 295 + 296 + #clock:hover { 297 + border-color: #${theme.colors.purple}; 298 + } 299 + 300 + #battery { 301 + color: #${theme.colors.green}; 302 + } 303 + 304 + #battery:hover { 305 + border-color: #${theme.colors.green}; 306 + } 307 + 308 + #battery.charging { 309 + border-color: #${theme.colors.green}; 310 + } 311 + 312 + @keyframes blink { 313 + to { 314 + border-color: #${theme.colors.red}; 315 + } 316 + } 317 + 318 + /* Using steps() instead of linear as a timing function to limit cpu usage */ 319 + #battery.critical:not(.charging) { 320 + color: #${theme.colors.red}; 321 + animation-name: blink; 322 + animation-duration: 0.5s; 323 + animation-timing-function: steps(12); 324 + animation-iteration-count: infinite; 325 + animation-direction: alternate; 326 + } 327 + 328 + #network { 329 + color: #${theme.colors.blue}; 330 + } 331 + 332 + #network:hover { 333 + border-color: #${theme.colors.blue}; 334 + } 335 + 336 + #network.disconnected { 337 + border-color: #${theme.colors.red}; 338 + } 339 + 340 + #pulseaudio { 341 + color: #${theme.colors.yellow}; 342 + } 343 + 344 + #pulseaudio:hover { 345 + border-color: #${theme.colors.yellow}; 346 + } 347 + 348 + #pulseaudio.muted { 349 + border-color: #${theme.colors.red}; 350 + } 351 + ''; 352 + passThruArgs = builtins.removeAttrs args [ 353 + "pkgs" 354 + "lib" 355 + "mkWrapper" 356 + "theme" 357 + "waybar" 358 + "pavucontrol" 359 + "networkmanagerapplet" 360 + ]; 361 + in 362 + mkWrapper { 363 + pkg = waybar.override passThruArgs; 364 + waybar.prependFlags = [ 365 + "--config" 366 + "${cfg}" 367 + "--style" 368 + "${style}" 369 + ]; 370 + }
+184
utils/wrap.nix
··· 1 + /** 2 + Wraps a package with flags and env vars. 3 + 4 + The wrapper derivation has each specified binary in the original package replaced by a wrapper with the specified flags and environment variables. 5 + Every other file from the original derivation is symlinked to by the new derivation, except any file referencing the original $pkg will be cloned and have that reference replaced by the wrapper derivation's $out. 6 + 7 + # Example 8 + 9 + ```nix 10 + mkWrapper = import ./wrap.nix; 11 + editor = mkWrapper pkgs { 12 + pkg = pkgs.helix; 13 + hx = { 14 + env.idk = "hi"; 15 + prependFlags = [ 16 + "--config" 17 + "./helix.toml" 18 + ]; 19 + }; 20 + }; 21 + ``` 22 + 23 + # Type 24 + 25 + ``` 26 + mkWrapper :: Pkgs -> { 27 + pkg : Derivation, 28 + pname? : String, 29 + additionalDependencies? : [Derivation], 30 + <bin> : { 31 + env? : AttrSet String, 32 + prependFlags? : [String] 33 + appendFlags? : [String] 34 + } 35 + } -> Derivation 36 + ``` 37 + 38 + # Arguments 39 + 40 + pkgs 41 + : An instance of nixpkgs 42 + 43 + pkg 44 + : The derivation to wrap 45 + 46 + pname 47 + : An optional name for the wrapped package, defaults to `pkg.name` 48 + 49 + additionalDependencies 50 + : An optional list of additional derivations to include in the wrapped environment 51 + 52 + <bin> 53 + : Variadic number of attrsets in the form bin = { env?, flags? }, where `bin` is the name of the binary that `pkg` outputs that you want to wrap. `env` and `flags` are detailed below. 54 + 55 + env 56 + : An optional attrset specifying environment variables to set inside the wrapped environment, defaults to `{}` 57 + 58 + prependFlags 59 + : An optional list specifying flags to append to the wrapped binary, defaults to `[]`. These flags go before your arguments, in the form `bin --prepended $@`. 60 + 61 + appendFlags 62 + : An optional list specifying flags to append to the wrapped binary, defaults to `[]` These flags go after your arguments, in the form `bin $@ --appended`. 63 + */ 64 + pkgs: 65 + let 66 + wrap = 67 + { 68 + pkg, 69 + pname ? pkg.pname, 70 + version ? pkg.version, 71 + additionalDependencies ? [ ], 72 + ... 73 + }@args: 74 + 75 + let 76 + lib = pkgs.lib; 77 + in 78 + 79 + assert lib.isDerivation pkg || throw "'pkg' must be a derivation (e.g. pkgs.hello)"; 80 + assert lib.isString pname || throw "'name' must be a string"; 81 + 82 + let 83 + # all keyword kwargs are bins 84 + binaries = builtins.removeAttrs args [ 85 + "pkg" 86 + "pname" 87 + "version" 88 + "additionalDependencies" 89 + ]; 90 + 91 + wrapCommands = lib.mapAttrsToList ( 92 + bin: 93 + { 94 + prependFlags ? [ ], 95 + appendFlags ? [ ], 96 + env ? { }, 97 + }: 98 + assert 99 + (lib.isList prependFlags && lib.all lib.isString prependFlags) 100 + || throw "'${bin}.addFlags' must be a list of strings"; 101 + assert 102 + (lib.isList appendFlags && lib.all lib.isString appendFlags) 103 + || throw "'${bin}.addFlags' must be a list of strings"; 104 + assert 105 + (lib.isAttrs env && lib.all lib.isString (lib.attrValues env)) 106 + || throw "'${bin}.env' must be an attribute set of strings"; 107 + let 108 + envArgs = lib.concatStringsSep " " (lib.mapAttrsToList (k: v: "--set '${k}' '${v}'") env); 109 + in 110 + '' 111 + wrapProgram $out/bin/${bin} \ 112 + --add-flags "${builtins.toString prependFlags}" \ 113 + --append-flags "${builtins.toString appendFlags}" \ 114 + ${envArgs} 115 + '' 116 + ) binaries; 117 + 118 + # nasty script to replace all text references to ${pkg} with ${out} 119 + # this is necessary because some files that get symlinked into the new 120 + # package directory ($out) will still reference ${pkg}, leading to 121 + # unexpected behavior 122 + fixReferences = '' 123 + # iterate through every file/symlink in $out 124 + find "$out" \( -type l -o -type f \) | while read -r item; do 125 + # if item is a symlink, we want to (recursively) find the file it 126 + # points to, read it, and if it contains $pkg, overwrite the symlink 127 + # with a regular file to replace $pkg with $out 128 + if [ -L "$item" ]; then 129 + # redirection is to ignore broken symlinks 130 + target=$(readlink -f "$item" 2>/dev/null || true) 131 + 132 + # if our target is a nonempty file containing pkg, replace item with target 133 + if [ -n "$target" ] && [ -f "$target" ] && grep -F -q "${pkg}" "$target"; then 134 + rm "$item" 135 + cp "$target" "$item" 136 + # otherwise, skip the file replacement code 137 + else 138 + continue 139 + fi 140 + fi 141 + 142 + # $item is guaranteed to be a regular file 143 + if grep -F -q "${pkg}" "$item"; then 144 + # ensure $item is writeable 145 + [ -w "$item" ] || chmod +w "$item" 146 + # replace all occurances of $pkg with $out 147 + sed -i "s|${pkg}|$out|g" "$item" || true 148 + fi 149 + done 150 + ''; 151 + in 152 + pkgs.symlinkJoin { 153 + inherit pname; 154 + inherit version; 155 + passthru = (pkg.passthru or { }) // { 156 + unwrapped = pkg; 157 + 158 + override = 159 + overrideArgs: 160 + let 161 + newPkg = pkg.override overrideArgs; 162 + newArgs = args // { 163 + pkg = newPkg; 164 + }; 165 + in 166 + wrap newArgs; 167 + 168 + overrideAttrs = 169 + f: 170 + let 171 + newPkg = pkg.overrideAttrs f; 172 + newArgs = args // { 173 + pkg = newPkg; 174 + }; 175 + in 176 + wrap newArgs; 177 + }; 178 + meta = pkg.meta or { }; 179 + paths = [ pkg ]; 180 + buildInputs = [ pkgs.makeWrapper ] ++ additionalDependencies; 181 + postBuild = lib.concatStringsSep "\n" (wrapCommands ++ [ fixReferences ]); 182 + }; 183 + in 184 + wrap