├── .editorconfig ├── .envrc ├── .github └── workflows │ └── check.yml ├── .gitignore ├── .vscode └── settings.json ├── README.md ├── flake.lock ├── flake.nix ├── home ├── default.nix ├── direnvrc └── modules │ ├── blueberry.nix │ ├── nushell.nix │ ├── swaybg.nix │ ├── xdg-desktop-portal-hyprland.nix │ ├── xdg-desktop-portal.nix │ └── xssproxy.nix ├── nixos-config.code-workspace ├── packages ├── aw-watcher-window-hyprland │ └── package.nix ├── coin │ └── package.nix ├── git-worktree-shell │ ├── git-worktree-shell.sh │ └── package.nix ├── git-xargs │ └── package.nix ├── hypr-open │ ├── hypr-open.py │ └── package.nix ├── lazy-desktop │ └── package.nix ├── sway-open │ ├── package.nix │ └── sway-open.py ├── swaylock-fprintd │ └── package.nix ├── wl-screenrecord │ ├── package.nix │ └── wl-screenrecord.sh └── wl-screenshot │ ├── package.nix │ └── wl-screenshot.sh └── system ├── configuration.nix ├── hardware-configuration.nix └── modules ├── default.nix ├── docker-config.nix ├── home-manager.nix ├── suites ├── nix-channels.nix └── single-user.nix └── v4l2loopback.nix /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | 3 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: nix flake check 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | check: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Install Nix 16 | uses: cachix/install-nix-action@v17 17 | - run: nix flake check --no-build 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | result 2 | result-* 3 | *-result 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[nix]": { 3 | "editor.formatOnSave": true 4 | }, 5 | "nix.enableLanguageServer": true, 6 | "nix.serverPath": "nixd", 7 | "nix.serverSettings": { 8 | "nixpkgs": { 9 | "expr": "import {}" 10 | }, 11 | "nixd": { 12 | "formatting": { 13 | "command": ["nixfmt"] 14 | }, 15 | "options": { 16 | "nixos": { 17 | "expr": "(builtins.getFlake \"/home/bob.vanderlinden/projects/nixos-config\").nixosConfigurations.nac44250.options" 18 | }, 19 | "home-manager": { 20 | "expr": "(builtins.getFlake \"/home/bob.vanderlinden/projects/nixos-config\").homeConfigurations.\"bob.vanderlinden@nac44250\".options" 21 | } 22 | } 23 | } 24 | }, 25 | "editor.defaultFormatter": "jnoortheen.nix-ide" 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nixos-config 2 | 3 | This repository includes the Nix configuration for my laptop. 4 | 5 | It includes the following: 6 | 7 | - A flake-based configuration (see [flake.nix](flake.nix)) 8 | - [NixOS](https://nixos.org/) configuration (see [configuration.nix](system/configuration.nix)) 9 | - [home-manager](https://github.com/nix-community/home-manager#home-manager-using-nix) configuration (see [home](home/default.nix)) 10 | - Custom NixOS modules (see [system/modules/](system/modules/)) 11 | - Custom home-manager modules (see [home/modules/](home/modules/)) 12 | - Custom packages (see [packages/](packages/)) 13 | - Do not expect this configuration to work for your system as-is 14 | 15 | ## Usage 16 | 17 | To switch to a new system+home configuration I usually run: 18 | 19 | ```sh 20 | nix run .#switch 21 | ``` 22 | 23 | Which does the following: 24 | 25 | - Switch to new configuration for home-manager 26 | - Switch to new configuration for NixOS 27 | - Builds configuration using [`nom`](https://github.com/maralorn/nix-output-monitor) for more insightful output. 28 | - Asks for `sudo` only when system configuration has actually changed. 29 | - Plings when actually switching system configuration. 30 | 31 | This is similar to using the `home-manager` and `nixos-rebuild` tools: 32 | 33 | ```console 34 | $ home-manager switch --flake . 35 | $ nixos-rebuild --flake . switch --use-remote-sudo 36 | ``` 37 | 38 | To update nixpkgs and others I usually do: 39 | 40 | ```sh 41 | nix flake update 42 | ``` 43 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "crane": { 4 | "locked": { 5 | "lastModified": 1746291859, 6 | "narHash": "sha256-DdWJLA+D5tcmrRSg5Y7tp/qWaD05ATI4Z7h22gd1h7Q=", 7 | "owner": "ipetkov", 8 | "repo": "crane", 9 | "rev": "dfd9a8dfd09db9aad544c4d3b6c47b12562544a5", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "ipetkov", 14 | "repo": "crane", 15 | "type": "github" 16 | } 17 | }, 18 | "flake-compat": { 19 | "flake": false, 20 | "locked": { 21 | "lastModified": 1733328505, 22 | "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", 23 | "owner": "edolstra", 24 | "repo": "flake-compat", 25 | "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", 26 | "type": "github" 27 | }, 28 | "original": { 29 | "owner": "edolstra", 30 | "repo": "flake-compat", 31 | "type": "github" 32 | } 33 | }, 34 | "flake-parts": { 35 | "inputs": { 36 | "nixpkgs-lib": [ 37 | "lanzaboote", 38 | "nixpkgs" 39 | ] 40 | }, 41 | "locked": { 42 | "lastModified": 1743550720, 43 | "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", 44 | "owner": "hercules-ci", 45 | "repo": "flake-parts", 46 | "rev": "c621e8422220273271f52058f618c94e405bb0f5", 47 | "type": "github" 48 | }, 49 | "original": { 50 | "owner": "hercules-ci", 51 | "repo": "flake-parts", 52 | "type": "github" 53 | } 54 | }, 55 | "flake-utils": { 56 | "inputs": { 57 | "systems": "systems" 58 | }, 59 | "locked": { 60 | "lastModified": 1731533236, 61 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 62 | "owner": "numtide", 63 | "repo": "flake-utils", 64 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 65 | "type": "github" 66 | }, 67 | "original": { 68 | "owner": "numtide", 69 | "repo": "flake-utils", 70 | "type": "github" 71 | } 72 | }, 73 | "gitignore": { 74 | "inputs": { 75 | "nixpkgs": [ 76 | "lanzaboote", 77 | "pre-commit-hooks-nix", 78 | "nixpkgs" 79 | ] 80 | }, 81 | "locked": { 82 | "lastModified": 1709087332, 83 | "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", 84 | "owner": "hercules-ci", 85 | "repo": "gitignore.nix", 86 | "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", 87 | "type": "github" 88 | }, 89 | "original": { 90 | "owner": "hercules-ci", 91 | "repo": "gitignore.nix", 92 | "type": "github" 93 | } 94 | }, 95 | "home-manager": { 96 | "inputs": { 97 | "nixpkgs": [ 98 | "nixpkgs" 99 | ] 100 | }, 101 | "locked": { 102 | "lastModified": 1748455938, 103 | "narHash": "sha256-mQ/iNzPra2WtDQ+x2r5IadcWNr0m3uHvLMzJkXKAG/8=", 104 | "owner": "nix-community", 105 | "repo": "home-manager", 106 | "rev": "02077149e2921014511dac2729ae6dadb4ec50e2", 107 | "type": "github" 108 | }, 109 | "original": { 110 | "owner": "nix-community", 111 | "repo": "home-manager", 112 | "type": "github" 113 | } 114 | }, 115 | "lanzaboote": { 116 | "inputs": { 117 | "crane": "crane", 118 | "flake-compat": "flake-compat", 119 | "flake-parts": "flake-parts", 120 | "nixpkgs": "nixpkgs", 121 | "pre-commit-hooks-nix": "pre-commit-hooks-nix", 122 | "rust-overlay": "rust-overlay" 123 | }, 124 | "locked": { 125 | "lastModified": 1747056319, 126 | "narHash": "sha256-qSKcBaISBozadtPq6BomnD+wIYTZIkiua3UuHLaD52c=", 127 | "owner": "nix-community", 128 | "repo": "lanzaboote", 129 | "rev": "2e425f3da6ce7f5b34fa6eaf7a2a7f78dbabcc85", 130 | "type": "github" 131 | }, 132 | "original": { 133 | "owner": "nix-community", 134 | "repo": "lanzaboote", 135 | "type": "github" 136 | } 137 | }, 138 | "nix-index-database": { 139 | "inputs": { 140 | "nixpkgs": "nixpkgs_2" 141 | }, 142 | "locked": { 143 | "lastModified": 1748145500, 144 | "narHash": "sha256-t9fx0l61WOxtWxXCqlXPWSuG/0XMF9DtE2T7KXgMqJw=", 145 | "owner": "nix-community", 146 | "repo": "nix-index-database", 147 | "rev": "a98adbf54d663395df0b9929f6481d4d80fc8927", 148 | "type": "github" 149 | }, 150 | "original": { 151 | "owner": "nix-community", 152 | "repo": "nix-index-database", 153 | "type": "github" 154 | } 155 | }, 156 | "nixpkgs": { 157 | "locked": { 158 | "lastModified": 1746916775, 159 | "narHash": "sha256-TGHTAbjauY7kK8tX8HdFh0A9eXhiRIbwZH6vBHLsofc=", 160 | "owner": "NixOS", 161 | "repo": "nixpkgs", 162 | "rev": "7fb53a7bf9a07f97c34910aa45388083c67751e9", 163 | "type": "github" 164 | }, 165 | "original": { 166 | "owner": "NixOS", 167 | "ref": "nixos-unstable-small", 168 | "repo": "nixpkgs", 169 | "type": "github" 170 | } 171 | }, 172 | "nixpkgs_2": { 173 | "locked": { 174 | "lastModified": 1748026106, 175 | "narHash": "sha256-6m1Y3/4pVw1RWTsrkAK2VMYSzG4MMIj7sqUy7o8th1o=", 176 | "owner": "NixOS", 177 | "repo": "nixpkgs", 178 | "rev": "063f43f2dbdef86376cc29ad646c45c46e93234c", 179 | "type": "github" 180 | }, 181 | "original": { 182 | "owner": "NixOS", 183 | "ref": "nixos-unstable", 184 | "repo": "nixpkgs", 185 | "type": "github" 186 | } 187 | }, 188 | "nixpkgs_3": { 189 | "locked": { 190 | "lastModified": 1748370509, 191 | "narHash": "sha256-QlL8slIgc16W5UaI3w7xHQEP+Qmv/6vSNTpoZrrSlbk=", 192 | "owner": "nixos", 193 | "repo": "nixpkgs", 194 | "rev": "4faa5f5321320e49a78ae7848582f684d64783e9", 195 | "type": "github" 196 | }, 197 | "original": { 198 | "owner": "nixos", 199 | "ref": "nixos-unstable", 200 | "repo": "nixpkgs", 201 | "type": "github" 202 | } 203 | }, 204 | "pre-commit-hooks-nix": { 205 | "inputs": { 206 | "flake-compat": [ 207 | "lanzaboote", 208 | "flake-compat" 209 | ], 210 | "gitignore": "gitignore", 211 | "nixpkgs": [ 212 | "lanzaboote", 213 | "nixpkgs" 214 | ] 215 | }, 216 | "locked": { 217 | "lastModified": 1746537231, 218 | "narHash": "sha256-Wb2xeSyOsCoTCTj7LOoD6cdKLEROyFAArnYoS+noCWo=", 219 | "owner": "cachix", 220 | "repo": "pre-commit-hooks.nix", 221 | "rev": "fa466640195d38ec97cf0493d6d6882bc4d14969", 222 | "type": "github" 223 | }, 224 | "original": { 225 | "owner": "cachix", 226 | "repo": "pre-commit-hooks.nix", 227 | "type": "github" 228 | } 229 | }, 230 | "root": { 231 | "inputs": { 232 | "flake-utils": "flake-utils", 233 | "home-manager": "home-manager", 234 | "lanzaboote": "lanzaboote", 235 | "nix-index-database": "nix-index-database", 236 | "nixpkgs": "nixpkgs_3" 237 | } 238 | }, 239 | "rust-overlay": { 240 | "inputs": { 241 | "nixpkgs": [ 242 | "lanzaboote", 243 | "nixpkgs" 244 | ] 245 | }, 246 | "locked": { 247 | "lastModified": 1747017456, 248 | "narHash": "sha256-C/U12fcO+HEF071b5mK65lt4XtAIZyJSSJAg9hdlvTk=", 249 | "owner": "oxalica", 250 | "repo": "rust-overlay", 251 | "rev": "5b07506ae89b025b14de91f697eba23b48654c52", 252 | "type": "github" 253 | }, 254 | "original": { 255 | "owner": "oxalica", 256 | "repo": "rust-overlay", 257 | "type": "github" 258 | } 259 | }, 260 | "systems": { 261 | "locked": { 262 | "lastModified": 1681028828, 263 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 264 | "owner": "nix-systems", 265 | "repo": "default", 266 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 267 | "type": "github" 268 | }, 269 | "original": { 270 | "owner": "nix-systems", 271 | "repo": "default", 272 | "type": "github" 273 | } 274 | } 275 | }, 276 | "root": "root", 277 | "version": 7 278 | } 279 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 4 | # nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-24.11"; 5 | home-manager = { 6 | url = "github:nix-community/home-manager"; 7 | inputs.nixpkgs.follows = "nixpkgs"; 8 | }; 9 | flake-utils.url = "github:numtide/flake-utils"; 10 | lanzaboote.url = "github:nix-community/lanzaboote"; 11 | nix-index-database.url = "github:nix-community/nix-index-database"; 12 | }; 13 | 14 | outputs = 15 | { 16 | self, 17 | flake-utils, 18 | lanzaboote, 19 | nix-index-database, 20 | ... 21 | }@inputs: 22 | let 23 | system = "x86_64-linux"; 24 | 25 | # We'd like to be able to add patches on top of nixpkgs, like pending pull requests. 26 | # Source: https://github.com/NixOS/nixpkgs/pull/142273#issuecomment-948225922 27 | patchedNixpkgs = 28 | let 29 | pkgs = inputs.nixpkgs.legacyPackages.${system}; 30 | in 31 | pkgs.applyPatches { 32 | name = "nixpkgs-patched"; 33 | src = inputs.nixpkgs; 34 | patches = [ 35 | # (pkgs.fetchurl { 36 | # url = "https://github.com/NixOS/nixpkgs/pull/402547.patch"; 37 | # hash = "sha256-6y6l8AvbxnoPuDpFM4+HcROfqXRQVo1K3Q3YHWhDGmw="; 38 | # }) 39 | ]; 40 | }; 41 | username = "bob.vanderlinden"; 42 | defaultOverlays = with self.overlays; [ 43 | default 44 | workarounds 45 | ]; 46 | mkPkgs = 47 | { 48 | system ? system, 49 | nixpkgs ? patchedNixpkgs, 50 | config ? { 51 | allowUnfree = true; 52 | }, 53 | overlays ? defaultOverlays, 54 | ... 55 | }@options: 56 | import nixpkgs (options // { inherit system config overlays; }); 57 | nixosSystem = import (patchedNixpkgs + "/nixos/lib/eval-config.nix"); 58 | in 59 | { 60 | overlays.default = 61 | final: prev: 62 | prev.lib.packagesFromDirectoryRecursive { 63 | inherit (final) callPackage; 64 | directory = ./packages; 65 | }; 66 | overlays.workarounds = 67 | final: prev: 68 | # let 69 | # pkgsStable = import inputs.nixpkgs-stable { 70 | # system = prev.system; 71 | # config.allowUnfree = true; 72 | # }; 73 | # in 74 | { 75 | # Downgrade 1password-gui to 8.10.40, as 8.10.44+ has a problem with the CLI. 76 | # See: https://github.com/NixOS/nixpkgs/issues/373415 77 | _1password-gui = 78 | let 79 | version = "8.10.40"; 80 | in 81 | prev._1password-gui.overrideAttrs (prevAttrs: { 82 | inherit version; 83 | src = final.fetchurl { 84 | url = "https://downloads.1password.com/linux/tar/stable/x86_64/1password-${version}.x64.tar.gz"; 85 | hash = "sha256-viY0SOUhrOvmue6Nolau356rIqwDo2nLzMilFFmNb9g="; 86 | }; 87 | }); 88 | # _1password = _1passwordPkgs._1password; 89 | 90 | # Pin zoom-us to avoid continuous breaking changes. 91 | # Latest one: https://github.com/NixOS/nixpkgs/issues/371488 92 | # zoom-us = 93 | # let 94 | # version = "6.3.5.6065"; 95 | # in 96 | # prev.zoom-us.overrideAttrs (prevAttrs: { 97 | # inherit version; 98 | # src = final.fetchurl { 99 | # url = "https://zoom.us/client/${version}/zoom_x86_64.pkg.tar.xz"; 100 | # hash = "sha256-JOkQsiYWcVq3LoMI2LyMZ1YXBtiAf612T2bdbduqry8="; 101 | # }; 102 | # }); 103 | }; 104 | 105 | nixosModules = import ./system/modules // { 106 | overlays = { 107 | nixpkgs.overlays = defaultOverlays; 108 | }; 109 | hardware-configuration = import ./system/hardware-configuration.nix; 110 | system-configuration = import ./system/configuration.nix; 111 | single-user = { 112 | suites.single-user.user = username; 113 | }; 114 | inherit (lanzaboote.nixosModules) lanzaboote; 115 | # inherit (nix-index-database.nixosModules) nix-index; 116 | nix-index-database-home-manager = { 117 | home-manager.sharedModules = [ nix-index-database.hmModules.nix-index ]; 118 | }; 119 | }; 120 | 121 | # System configuration for laptop. 122 | nixosConfigurations.nac44250 = nixosSystem { 123 | inherit system; 124 | specialArgs = { 125 | inherit inputs; 126 | }; 127 | modules = builtins.attrValues self.nixosModules; 128 | }; 129 | 130 | homeConfigurations."${username}@nac44250" = 131 | self.nixosConfigurations.nac44250.config.home-manager.users.${username}.home; 132 | } 133 | # Define outputs that allow multiple systems with for all default systems. 134 | # This is to support OSX and RPI. 135 | // flake-utils.lib.eachDefaultSystem ( 136 | system: 137 | let 138 | pkgs = mkPkgs { inherit system; }; 139 | in 140 | { 141 | packages = 142 | let 143 | inherit (builtins) attrNames; 144 | inherit (pkgs.lib) genAttrs filterAttrs; 145 | # We're going to use overlays.default to create an attrbute set of my packages. 146 | packageOverlay = self.overlays.default; 147 | # We extract the package names from the overlay without actually applying it (which would result in _all_ packages) 148 | # We'll use these names to extract the custom packages from pkgs 149 | packageNames = 150 | let 151 | fakePrev = { inherit (pkgs) callPackage; }; 152 | fakeFinal = { inherit (pkgs) lib; }; 153 | in 154 | attrNames (packageOverlay fakePrev fakeFinal); 155 | # finalPkgs contain _all_ packages (those from packageOverlay as well as all of nixpkgs), we need to pick those defined in packageOverlay. 156 | finalPackages = genAttrs packageNames (packageName: pkgs.${packageName}); 157 | # Filter packages that are not compatible with the current system 158 | compatiblePackages = filterAttrs ( 159 | name: package: 160 | (package ? meta) -> (package.meta ? platforms) -> builtins.elem system package.meta.platforms 161 | ) finalPackages; 162 | in 163 | compatiblePackages; 164 | 165 | apps.switch-home = { 166 | type = "app"; 167 | program = 168 | let 169 | switch-home = pkgs.writeShellApplication { 170 | name = "switch-home"; 171 | text = '' 172 | nom build --keep-going --out-link home-result ${self}#nixosConfigurations."$(hostname)".config.home-manager.users.\""$USER"\".home.activationPackage 173 | ./home-result/activate 174 | ''; 175 | runtimeInputs = [ pkgs.nix-output-monitor ]; 176 | }; 177 | in 178 | "${switch-home}/bin/switch-home"; 179 | }; 180 | 181 | apps.switch = { 182 | type = "app"; 183 | program = 184 | let 185 | switch = pkgs.writeShellApplication { 186 | name = "switch"; 187 | text = '' 188 | nom build --impure --keep-going --out-link system-result ${self}#nixosConfigurations."$(hostname)".config.system.build.toplevel 189 | nom build --keep-going --out-link home-result ${self}#nixosConfigurations."$(hostname)".config.home-manager.users.\""$USER"\".home.activationPackage 190 | if [[ "$(readlink --canonicalize system-result)" != "$(readlink --canonicalize /nix/var/nix/profiles/system)" ]] 191 | then 192 | ${pkgs.coin}/bin/coin 193 | sudo nix-env -p /nix/var/nix/profiles/system --set "$(readlink system-result)" 194 | sudo system-result/bin/switch-to-configuration switch 195 | fi 196 | ./home-result/activate 197 | ''; 198 | runtimeInputs = [ pkgs.nix-output-monitor ]; 199 | }; 200 | in 201 | "${switch}/bin/switch"; 202 | }; 203 | 204 | formatter = pkgs.nixfmt-tree; 205 | 206 | devShells.default = pkgs.mkShell { 207 | packages = with pkgs; [ 208 | nixfmt-rfc-style 209 | nixd 210 | ]; 211 | }; 212 | } 213 | ); 214 | } 215 | -------------------------------------------------------------------------------- /home/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | config, 4 | lib, 5 | ... 6 | }: 7 | let 8 | inherit (lib) mapAttrsToList; 9 | 10 | backgroundColor = "1a1b26"; 11 | wallpaperSvg = pkgs.fetchurl { 12 | url = "https://raw.githubusercontent.com/NixOS/nixos-artwork/4ad062cee62116f6055e2876e9638e7bb399d219/logo/nix-snowflake-colours.svg"; 13 | hash = "sha256-43taHBHoFJbp1GrwSQiVGtprq6pBbWcKquSTTM6RLrI="; 14 | }; 15 | wallpaperPng = pkgs.runCommand "nix-snowflake.png" { } '' 16 | ${pkgs.resvg}/bin/resvg ${wallpaperSvg} $out 17 | ''; 18 | 19 | cursor-alias = pkgs.writeShellScriptBin "code" '' 20 | exec cursor "$@" 21 | ''; 22 | cursor-wrapper = pkgs.writeShellScriptBin "cursor" '' 23 | # Disable any custom node options that some projects might have. 24 | # These conflict with node inside cursor/vscode. 25 | export NODE_OPTIONS="" 26 | 27 | # Open files in the cursor instance on the current workspace. 28 | # Otherwise, open a new instance on the current workspace. 29 | exec ${lib.getExe pkgs.hypr-open} \ 30 | --window-class cursor \ 31 | --new-window-argument="--new-window" \ 32 | -- \ 33 | ${lib.getExe pkgs.code-cursor} \ 34 | "$@" > /dev/null 2>&1 35 | ''; 36 | 37 | # Open URLs in the chromium instance on the current workspace. 38 | # Otherwise, open a new instance on the current workspace. 39 | chromium-wrapper = pkgs.writeShellScriptBin "chromium" '' 40 | exec ${lib.getExe pkgs.hypr-open} \ 41 | --window-class chromium-browser \ 42 | --new-window-argument="--new-window" \ 43 | -- \ 44 | ${lib.getExe config.programs.chromium.package} \ 45 | "$@" 46 | ''; 47 | in 48 | { 49 | imports = [ 50 | ./modules/blueberry.nix 51 | ./modules/xssproxy.nix 52 | ./modules/nushell.nix 53 | ./modules/swaybg.nix 54 | ./modules/xdg-desktop-portal.nix 55 | ./modules/xdg-desktop-portal-hyprland.nix 56 | ]; 57 | config = { 58 | home.packages = with pkgs; [ 59 | darkman 60 | gnome-keyring 61 | grim 62 | # Development Tools 63 | nixfmt-rfc-style 64 | gdb 65 | nodejs 66 | clang 67 | jdk 68 | maven 69 | deno 70 | devenv 71 | watchman 72 | strace 73 | ltrace 74 | kubectl 75 | k9s 76 | postgresql 77 | 78 | # Version Control 79 | hub 80 | gh 81 | git-cola 82 | git-absorb 83 | git-revise 84 | git-worktree-shell 85 | git-xargs 86 | tig 87 | mergiraf 88 | 89 | # Text Processing & Search 90 | ripgrep 91 | fd 92 | sd 93 | q-text-as-data 94 | delta 95 | ast-grep 96 | 97 | # System Tools 98 | socat 99 | file 100 | qemu 101 | darkhttpd 102 | lsof 103 | bottom 104 | procs 105 | dua 106 | nix-output-monitor 107 | 108 | # Network Tools 109 | nmap 110 | httpie 111 | insomnia 112 | docker-compose 113 | 114 | # File Management 115 | xfce.thunar 116 | xfce.xfconf 117 | xfce.tumbler 118 | xfce.exo 119 | file-roller 120 | meld 121 | 122 | # Media & Graphics 123 | imagemagick 124 | vlc 125 | gimp 126 | feh 127 | ffmpeg-full 128 | ffmpegthumbnailer 129 | audacity 130 | inkscape 131 | peek 132 | 133 | # Desktop Environment 134 | pavucontrol 135 | volumeicon 136 | lxappearance 137 | networkmanagerapplet 138 | dconf 139 | wl-clipboard-rs 140 | wl-screenrecord 141 | wl-screenshot 142 | 143 | # Security & Privacy 144 | bitwarden-desktop 145 | bitwarden-cli 146 | keepassxc 147 | gnupg 148 | seahorse 149 | 150 | # Communication & Collaboration 151 | slack 152 | zoom-us 153 | thunderbird 154 | signal-desktop 155 | 156 | # Text Editors & IDEs 157 | helix 158 | (lib.hiPrio cursor-wrapper) 159 | (lib.hiPrio chromium-wrapper) 160 | cursor-alias 161 | 162 | # Productivity 163 | pomodoro 164 | libreoffice 165 | speedcrunch 166 | chatgpt-cli 167 | 168 | # CLI Utilities 169 | entr 170 | xclip 171 | jq 172 | graphviz 173 | screen 174 | yq-go 175 | watchexec 176 | difftastic 177 | du-dust 178 | fx 179 | cachix 180 | ijq 181 | nodePackages.zx 182 | xdg-utils 183 | nixpkgs-review 184 | tabiew 185 | 186 | # Fonts 187 | liberation_ttf 188 | ttf_bitstream_vera 189 | 190 | # Misc 191 | coin 192 | patchelf 193 | home-manager 194 | ]; 195 | 196 | i18n.inputMethod = { 197 | enable = true; 198 | type = "fcitx5"; 199 | fcitx5.waylandFrontend = true; 200 | }; 201 | 202 | programs.rofi = { 203 | enable = true; 204 | package = pkgs.rofi-wayland; 205 | plugins = [ 206 | pkgs.rofi-calc 207 | pkgs.rofi-emoji 208 | pkgs.rofi-file-browser 209 | pkgs.rofi-rbw 210 | pkgs.rofi-bluetooth 211 | pkgs.rofi-power-menu 212 | pkgs.rofi-screenshot 213 | ]; 214 | theme = 215 | let 216 | rofi-themes-collection = pkgs.fetchFromGitHub { 217 | owner = "newmanls"; 218 | repo = "rofi-themes-collection"; 219 | rev = "ec731cef79d39fc7ae12ef2a70a2a0dd384f9730"; 220 | hash = "sha256-96wSyOp++1nXomnl8rbX5vMzaqRhTi/N7FUq6y0ukS8="; 221 | }; 222 | in 223 | "${rofi-themes-collection}/themes/rounded-blue-dark.rasi"; 224 | }; 225 | 226 | wayland.windowManager.hyprland = { 227 | enable = true; 228 | systemd.variables = [ "--all" ]; 229 | settings = { 230 | "$mod" = "SUPER"; 231 | 232 | general = { 233 | gaps_in = 0; 234 | gaps_out = 0; 235 | }; 236 | 237 | # 238 | env = 239 | let 240 | envkv = { 241 | BROWSER = "chromium"; 242 | EDITOR = "code --wait"; 243 | 244 | # Source: https://github.com/NixOS/nixpkgs/issues/271461#issuecomment-1934829672 245 | ELECTRON_OZONE_PLATFORM_HINT = "wayland"; 246 | 247 | # Source: https://github.com/NixOS/nixpkgs/blob/45004c6f6330b1ff6f3d6c3a0ea8019f6c18a930/nixos/modules/programs/sway.nix#L47-L53 248 | SDL_VIDEODRIVER = "wayland"; 249 | QT_QPA_PLATFORM = "wayland"; 250 | QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; 251 | _JAVA_AWT_WM_NONREPARENTING = "1"; 252 | 253 | # Source: https://wiki.archlinux.org/title/Wayland#Clutter 254 | CLUTTER_BACKEND = "wayland"; 255 | 256 | MOZ_DISABLE_RDD_SANDBOX = "1"; 257 | EGL_PLATFORM = "wayland"; 258 | 259 | # Make Chromium and Electron use Ozone Wayland support 260 | NIXOS_OZONE_WL = "1"; 261 | }; 262 | in 263 | mapAttrsToList (k: v: "${k},${v}") envkv; 264 | 265 | bind = 266 | let 267 | swayosd_client = "${config.services.swayosd.package}/bin/swayosd-client"; 268 | in 269 | [ 270 | "$mod, T, exec, ghostty" 271 | "$mod, W, exec, chromium" 272 | "$mod, E, exec, thunar" 273 | "$mod, Q, exec, ${config.programs.rofi.finalPackage}/bin/rofi -show combi -modes combi -combi-modes run,emoji -combi-hide-mode-prefix" 274 | "$mod, Delete, exec, loginctl lock-session" 275 | "$mod, Print, exec, flameshot gui" 276 | "$mod SHIFT, Print, exec, wl-screenrecord" 277 | "$mod, C, killactive" 278 | 279 | # Focus movement 280 | "$mod, H, movefocus, l" 281 | "$mod, J, movefocus, u" 282 | "$mod, K, movefocus, d" 283 | "$mod, L, movefocus, r" 284 | "$mod, Left, movefocus, l" 285 | "$mod, Up, movefocus, u" 286 | "$mod, Down, movefocus, d" 287 | "$mod, Right, movefocus, r" 288 | "$mod, Tab, changegroupactive, f" 289 | "$mod SHIFT, Tab, changegroupactive, b" 290 | 291 | # Move window 292 | "$mod SHIFT, H, movewindow, l" 293 | "$mod SHIFT, K, movewindow, u" 294 | "$mod SHIFT, J, movewindow, d" 295 | "$mod SHIFT, L, movewindow, r" 296 | "$mod SHIFT, Left, movewindow, l" 297 | "$mod SHIFT, Up, movewindow, u" 298 | "$mod SHIFT, Down, movewindow, d" 299 | "$mod SHIFT, Right, movewindow, r" 300 | 301 | # Resize window 302 | "$mod CTRL, Left, resizeactive, -20 0" 303 | "$mod CTRL, Down, resizeactive, 0 20" 304 | "$mod CTRL, Up, resizeactive, 0 -20" 305 | "$mod CTRL, Right, resizeactive, 20 0" 306 | 307 | # Split/Fullscreen/Layout 308 | "$mod, G, togglegroup" 309 | "$mod, F, fullscreen, 1" 310 | "$mod SHIFT, F, togglefloating" 311 | 312 | # Workspaces 313 | "$mod, 1, workspace, 1" 314 | "$mod, 2, workspace, 2" 315 | "$mod, 3, workspace, 3" 316 | "$mod, 4, workspace, 4" 317 | "$mod, 5, workspace, 5" 318 | "$mod, 6, workspace, 6" 319 | "$mod, 7, workspace, 7" 320 | "$mod, 8, workspace, 8" 321 | "$mod, 9, workspace, 9" 322 | "$mod, 0, workspace, 10" 323 | "$mod SHIFT, 1, movetoworkspace, 1" 324 | "$mod SHIFT, 2, movetoworkspace, 2" 325 | "$mod SHIFT, 3, movetoworkspace, 3" 326 | "$mod SHIFT, 4, movetoworkspace, 4" 327 | "$mod SHIFT, 5, movetoworkspace, 5" 328 | "$mod SHIFT, 6, movetoworkspace, 6" 329 | "$mod SHIFT, 7, movetoworkspace, 7" 330 | "$mod SHIFT, 8, movetoworkspace, 8" 331 | "$mod SHIFT, 9, movetoworkspace, 9" 332 | "$mod SHIFT, 0, movetoworkspace, 10" 333 | 334 | # Restart Hyprland 335 | "$mod SHIFT, R, exec, hyprctl reload" 336 | 337 | # Media keys 338 | " , XF86AudioRaiseVolume, exec, ${swayosd_client} --output-volume raise" 339 | " , XF86AudioLowerVolume, exec, ${swayosd_client} --output-volume lower" 340 | " , XF86AudioMute, exec, ${swayosd_client} --output-volume mute-toggle" 341 | " , XF86AudioPlay, exec, ${pkgs.playerctl}/bin/playerctl play" 342 | " , XF86AudioPause, exec, ${pkgs.playerctl}/bin/playerctl pause" 343 | " , XF86AudioNext, exec, ${pkgs.playerctl}/bin/playerctl next" 344 | " , XF86AudioPrev, exec, ${pkgs.playerctl}/bin/playerctl previous" 345 | 346 | # Brightness 347 | " , XF86MonBrightnessUp, exec, ${swayosd_client} --brightness raise" 348 | " , XF86MonBrightnessDown, exec, ${swayosd_client} --brightness lower" 349 | ]; 350 | 351 | bindm = [ 352 | "$mod, mouse:272, movewindow" # Drag window with SUPER + Left Mouse Button 353 | "$mod, mouse:273, resizewindow" # Resize window with SUPER + Right Mouse Button 354 | ]; 355 | 356 | bindl = [ 357 | "$mod, switch:[Lid Switch], exec, hyprlock" 358 | ]; 359 | 360 | # Disable all Hyprland animations (see https://wiki.hyprland.org/Configuring/Animations/) 361 | animation = [ 362 | "global, 0" 363 | "fade, 0" 364 | "windows, 0" 365 | "workspaces, 0" 366 | ]; 367 | 368 | misc = { 369 | disable_hyprland_logo = true; 370 | disable_splash_rendering = true; 371 | background_color = "rgb(${backgroundColor})"; 372 | }; 373 | }; 374 | }; 375 | 376 | programs.hyprlock = { 377 | enable = true; 378 | settings = { 379 | background = { 380 | color = "rgba(${backgroundColor})"; 381 | }; 382 | image = { 383 | path = "${wallpaperPng}"; 384 | size = 535; 385 | rounding = 0; 386 | border_size = 0; 387 | }; 388 | input-field = { 389 | size = "500, 64"; 390 | position = "0, -300"; 391 | font_size = 24; 392 | font_color = "rgba(255, 255, 255, 0.8)"; 393 | inner_color = "rgba(0, 0, 0, 0)"; 394 | outer_color = "rgba(255, 255, 255, 0.1)"; 395 | outline_thickness = 1; 396 | }; 397 | auth.fingerprint.enabled = true; 398 | animations.enabled = false; 399 | }; 400 | }; 401 | 402 | services.hypridle = { 403 | enable = true; 404 | settings = { 405 | general = { 406 | lock_cmd = "pidof hyprlock || hyprlock"; 407 | before_sleep_cmd = "loginctl lock-session"; 408 | after_sleep_cmd = "hyprctl dispatch dpms on"; 409 | }; 410 | 411 | listener = { 412 | timeout = 150; 413 | on-timeout = "brightnessctl -s set 10"; 414 | on-resume = "brightnessctl -r"; 415 | }; 416 | }; 417 | }; 418 | services.swayosd.enable = true; 419 | services.swaync = { 420 | enable = true; 421 | settings = { 422 | positionX = "right"; 423 | positionY = "bottom"; 424 | layer = "overlay"; 425 | }; 426 | }; 427 | 428 | programs.swaybg = { 429 | enable = true; 430 | outputs."*" = { 431 | mode = "center"; 432 | color = "#${backgroundColor}"; 433 | image = "${wallpaperPng}"; 434 | }; 435 | }; 436 | 437 | systemd.user.services.waybar.Unit.Requisite = config.programs.waybar.systemd.target; 438 | programs.waybar = { 439 | enable = true; 440 | systemd.enable = true; 441 | style = '' 442 | @import "${config.programs.waybar.package}/etc/xdg/waybar/style.css"; 443 | 444 | #workspaces button.active { 445 | background-color: #64727D; 446 | box-shadow: inset 0 -3px #ffffff; 447 | } 448 | 449 | #privacy-item.screenshare { 450 | background-color: #cf5700; 451 | } 452 | 453 | #privacy-item.audio-in { 454 | background-color: #cf5700; 455 | } 456 | 457 | #privacy-item.audio-out { 458 | background-color: #cf5700; 459 | } 460 | 461 | #custom-docker { 462 | padding: 0 10px; 463 | background-color: #1D63ED; 464 | } 465 | ''; 466 | settings = { 467 | mainBar = { 468 | position = "bottom"; 469 | modules-left = [ 470 | "hyprland/workspaces" 471 | ]; 472 | modules-center = [ ]; 473 | modules-right = [ 474 | "systemd_failed_units" 475 | "privacy" 476 | "custom/docker" 477 | "network" 478 | "battery" 479 | # "cpu" 480 | "clock" 481 | "tray" 482 | ]; 483 | "hyprland/workspaces" = { 484 | format = "{icon}"; 485 | on-scroll-up = "hyprctl dispatch workspace e+1"; 486 | on-scroll-down = "hyprctl dispatch workspace e-1"; 487 | }; 488 | "hyprland/window" = { 489 | separate-outputs = true; 490 | }; 491 | systemd_failed_units = { }; 492 | privacy = { 493 | icon-size = 12; 494 | }; 495 | "custom/docker" = { 496 | format = "{}  "; 497 | interval = 10; 498 | tooltip-format = "{} containers running"; 499 | exec = 500 | let 501 | docker-count = pkgs.writeShellApplication { 502 | name = "docker-count"; 503 | text = '' 504 | docker ps --format json | jq --slurp 'length' 505 | ''; 506 | runtimeInputs = [ 507 | pkgs.docker 508 | pkgs.jq 509 | ]; 510 | }; 511 | in 512 | "${docker-count}/bin/docker-count"; 513 | }; 514 | network = { 515 | format = ""; 516 | format-wired = ""; 517 | format-linked = ""; 518 | format-wifi = "{essid}  "; 519 | format-disconnected = ""; 520 | tooltip-format = "{ifname}\n{ipaddr}\n{essid} ({signalStrength}%)"; 521 | }; 522 | cpu = { 523 | interval = 10; 524 | format = "{}% "; 525 | max-length = 10; 526 | }; 527 | battery = { 528 | format = "{capacity}% {icon}"; 529 | format-icons = [ 530 | "" 531 | "" 532 | "" 533 | "" 534 | "" 535 | ]; 536 | format-charging = " {capacity}% - {time} {icon}"; 537 | format-full = " Charged {icon}"; 538 | }; 539 | clock = { 540 | format = "{:%a, %d. %b %H:%M}"; 541 | }; 542 | }; 543 | }; 544 | }; 545 | 546 | services.xdg-desktop-portal = { 547 | enable = true; 548 | verbose = true; 549 | portals = with pkgs; [ 550 | darkman 551 | xdg-desktop-portal-gtk 552 | gnome-keyring 553 | ]; 554 | }; 555 | 556 | services.xdg-desktop-portal-hyprland = { 557 | enable = true; 558 | settings = { 559 | # Skip the interactive screencopy picker and pick the current monitor non-interactively. 560 | screencopy.custom_picker_binary = 561 | let 562 | screencopy-picker = pkgs.writeShellApplication { 563 | name = "screencopy-picker"; 564 | runtimeInputs = [ 565 | config.wayland.windowManager.hyprland.finalPackage 566 | pkgs.jq 567 | ]; 568 | text = '' 569 | echo "[SELECTION]/screen:$(hyprctl activeworkspace -j | jq --raw-output .monitor)" 570 | ''; 571 | }; 572 | in 573 | "${screencopy-picker}/bin/screencopy-picker"; 574 | }; 575 | }; 576 | 577 | dconf = { 578 | enable = true; 579 | settings = { 580 | "org/gnome/desktop/sound" = { 581 | event-sounds = false; 582 | input-feedback-sounds = false; 583 | }; 584 | }; 585 | }; 586 | 587 | programs.chromium.enable = true; 588 | 589 | programs.lazygit = { 590 | enable = true; 591 | settings = { 592 | git.overrideGpg = true; 593 | customCommands = [ 594 | { 595 | key = "N"; 596 | context = "global"; 597 | command = "git fetch upstream HEAD && git checkout FETCH_HEAD"; 598 | } 599 | { 600 | key = "U"; 601 | context = "global"; 602 | command = "git pull upstream HEAD"; 603 | } 604 | ]; 605 | os.copyToClipboardCmd = '' 606 | ${pkgs.wl-clipboard-rs}/bin/wl-copy '{{text}}' 607 | ''; 608 | os.readFromClipboardCmd = '' 609 | ${pkgs.wl-clipboard-rs}/bin/wl-paste 610 | ''; 611 | }; 612 | }; 613 | 614 | fonts.fontconfig.enable = true; 615 | gtk = { 616 | enable = true; 617 | font = { 618 | name = "Noto Sans 10"; 619 | package = pkgs.noto-fonts; 620 | }; 621 | iconTheme = { 622 | name = "Adwaita"; 623 | package = pkgs.adwaita-icon-theme; 624 | }; 625 | theme = { 626 | name = "Adwaita-dark"; 627 | package = pkgs.gnome-themes-extra; 628 | }; 629 | gtk2.extraConfig = '' 630 | gtk-error-bell = 0 631 | ''; 632 | 633 | gtk3.extraConfig = { 634 | gtk-error-bell = 0; 635 | }; 636 | gtk4.extraConfig = { 637 | gtk-application-prefer-dark-theme = true; 638 | }; 639 | }; 640 | programs.ssh = { 641 | enable = true; 642 | forwardAgent = false; 643 | serverAliveInterval = 180; 644 | matchBlocks = { 645 | "beheer1.ioservice.net beheer1.stpst.nl beheer2.ioservice.net beheer2.stpst.nl" = { 646 | user = "bob.vanderlinden"; 647 | forwardAgent = false; 648 | identityFile = "~/.ssh/nedap_rsa"; 649 | }; 650 | 651 | "nl12* nl14* nl22* nl24* vm* nvs* nas* *.healthcare.nedap.local *.consul" = { 652 | user = "bob.vanderlinden"; 653 | forwardAgent = false; 654 | identityFile = "~/.ssh/nedap_rsa"; 655 | extraOptions = { 656 | VerifyHostKeyDNS = "no"; 657 | ProxyJump = "beheer1.ioservice.net"; 658 | }; 659 | }; 660 | 661 | "127.0.0.1" = { 662 | user = "bob.vanderlinden"; 663 | forwardAgent = false; 664 | identityFile = "~/.ssh/nedap_rsa"; 665 | extraOptions.VerifyHostKeyDNS = "no"; 666 | }; 667 | 668 | "github.com gist.github.com" = { 669 | user = "git"; 670 | identityFile = "~/.ssh/github_ed25519"; 671 | }; 672 | }; 673 | }; 674 | programs.fzf.enable = true; 675 | programs.bat.enable = true; 676 | programs.fish = { 677 | enable = true; 678 | interactiveShellInit = '' 679 | set fish_greeting 680 | ''; 681 | }; 682 | 683 | programs.starship = { 684 | enable = true; 685 | settings = { 686 | character = { 687 | success_symbol = "[\\$](bold blue)"; 688 | error_symbol = "[\\$](bold red)"; 689 | }; 690 | }; 691 | }; 692 | 693 | programs.atuin = { 694 | enable = true; 695 | flags = [ "--disable-up-arrow" ]; 696 | settings = { 697 | auto_sync = false; 698 | update_check = false; 699 | style = "compact"; 700 | }; 701 | }; 702 | 703 | programs.zoxide.enable = true; 704 | 705 | qt = { 706 | enable = true; 707 | platformTheme.name = "adwaita"; 708 | style = { 709 | name = "adwaita-dark"; 710 | package = pkgs.adwaita-qt; 711 | }; 712 | }; 713 | 714 | services.gpg-agent.enable = true; 715 | 716 | services.network-manager-applet.enable = true; 717 | services.blueberry.enable = true; 718 | services.mpris-proxy.enable = true; 719 | services.flameshot = { 720 | enable = true; 721 | package = pkgs.flameshot.overrideAttrs (old: { 722 | src = pkgs.fetchFromGitHub { 723 | owner = "flameshot-org"; 724 | repo = "flameshot"; 725 | rev = "f7a049ee78531b7dfa36ead4945ce9c721d90bfe"; 726 | hash = "sha256-teAvx50AvMjKcW44pdWxThTuJvUBeK4YI5fUmBQD9lI="; 727 | }; 728 | patches = [ ]; 729 | postFixup = '' 730 | wrapProgram $out/bin/flameshot \ 731 | --prefix PATH : ${pkgs.lib.makeBinPath [ pkgs.grim ]} \ 732 | ''${qtWrapperArgs[@]} 733 | ''; 734 | }); 735 | settings = { 736 | General = { 737 | showDesktopNotification = false; 738 | showStartupLaunchMessage = false; 739 | # useGrimAdapter = true; 740 | # disabledGrimWarning = true; 741 | }; 742 | }; 743 | }; 744 | 745 | services.darkman = { 746 | enable = true; 747 | settings = { 748 | lat = 51.974882858758626; 749 | lng = 5.9115896491034565; 750 | }; 751 | }; 752 | 753 | services.gammastep = { 754 | enable = true; 755 | latitude = 51.974882858758626; 756 | longitude = 5.9115896491034565; 757 | temperature.day = 5500; 758 | temperature.night = 3700; 759 | tray = true; 760 | }; 761 | 762 | services.xsettingsd = { 763 | enable = true; 764 | settings = { 765 | "Net/ThemeName" = "Adwaita-dark"; 766 | "Xft/Antialias" = true; 767 | "Xft/Hinting" = true; 768 | "Xft/RGBA" = "rgb"; 769 | }; 770 | }; 771 | 772 | programs.ghostty = { 773 | enable = true; 774 | settings = { 775 | window-decoration = false; 776 | resize-overlay = "never"; 777 | theme = "dark:Adwaita Dark,light:Adwaita"; 778 | scrollback-limit = 10000; 779 | keybind = [ 780 | "shift+enter=text:\\n" 781 | ]; 782 | }; 783 | }; 784 | 785 | services.xssproxy.enable = false; 786 | services.lxqt-policykit-agent.enable = false; 787 | services.polkit-gnome.enable = true; 788 | services.hyprpolkitagent.enable = false; 789 | 790 | services.pasystray.enable = true; 791 | 792 | xdg.enable = true; 793 | # news.display = "silent"; 794 | 795 | home.pointerCursor = { 796 | x11.enable = true; 797 | gtk.enable = true; 798 | hyprcursor.enable = true; 799 | name = "Vanilla-DMZ"; 800 | package = pkgs.vanilla-dmz; 801 | size = 128; 802 | }; 803 | 804 | home.shellAliases = { 805 | g = "git"; 806 | }; 807 | 808 | programs.git = { 809 | enable = true; 810 | package = pkgs.gitFull; 811 | userName = "Bob van der Linden"; 812 | userEmail = "bobvanderlinden@gmail.com"; 813 | 814 | # Use specific configuration for work projects. 815 | includes = 816 | let 817 | nedap-config = { 818 | user.name = "Bob van der Linden"; 819 | user.email = "bob.vanderlinden@nedap.com"; 820 | }; 821 | in 822 | [ 823 | { 824 | condition = "gitdir:~/projects/nedap/**"; 825 | contents = nedap-config; 826 | } 827 | { 828 | condition = "gitdir:~/projects/meditools/**"; 829 | contents = nedap-config; 830 | } 831 | ]; 832 | 833 | signing = { 834 | key = "~/.ssh/github_ed25519.pub"; 835 | signByDefault = true; 836 | format = "ssh"; 837 | }; 838 | 839 | difftastic.enable = true; 840 | aliases = { 841 | unstage = "reset HEAD --"; 842 | sw = "switch"; 843 | co = "checkout"; 844 | c = "commit"; 845 | b = "branch"; 846 | p = "push"; 847 | pf = "push --force-with-lease --force"; 848 | d = "diff"; 849 | a = "add"; 850 | s = "status"; 851 | f = "fetch"; 852 | t = "tag"; 853 | bl = "blame -w -C -C -C"; 854 | l = "log --graph --pretty='%Cred%h%Creset - %C(bold blue)<%an>%Creset %s%C(yellow)%d%Creset %Cgreen(%cr)' --abbrev-commit --date=relative"; 855 | fixup = "commit --fixup"; 856 | pr-init = '' 857 | !git fetch upstream HEAD && git checkout upstream/HEAD -b $1 858 | ''; 859 | pr-diff = "diff upstream/HEAD...HEAD"; 860 | pr-log = "l upstream/HEAD.."; 861 | pr-edit = "rebase --interactive --autosquash --rerere-autoupdate --rebase-merges --fork-point upstream/HEAD"; 862 | pr-clean = "rebase --autosquash --rerere-autoupdate --empty drop --no-keep-empty --fork-point upstream/HEAD"; 863 | pr-update = "pull --rebase=merges upstream HEAD"; 864 | pr-bisect = "!git bisect start && git bisect bad HEAD; git bisect good $(git merge-base --fork-point upstream/HEAD HEAD)"; 865 | }; 866 | ignores = [ 867 | "vendor" 868 | "workspace.code-workspace" 869 | 870 | # Always ignore devenv.sh temporary files. 871 | ".devenv" 872 | ".devenv.flake.nix" 873 | ]; 874 | extraConfig = { 875 | init.defaultBranch = "main"; 876 | 877 | column.ui = "auto"; 878 | 879 | core.editor = "code --wait"; 880 | 881 | # Show diff in commit message editor. 882 | commit.verbose = true; 883 | 884 | # Use more descriptive diff prefixes than a/ and b/. 885 | # See https://git-scm.com/docs/diff-config#Documentation/diff-config.txt-diffmnemonicPrefix 886 | diff.mnemonicPrefix = true; 887 | 888 | diff.algorithm = "patience"; 889 | 890 | # Show moved lines in diff. 891 | diff.colorMoved = "zebra"; 892 | 893 | diff.renames = true; 894 | 895 | push.default = "current"; 896 | push.autoSetupRemote = true; 897 | pull.rebase = true; 898 | 899 | rebase.autoSquash = true; 900 | rebase.autoStash = true; 901 | rebase.updateRefs = true; 902 | 903 | # Show original in-between ours and theirs. 904 | merge.conflictstyle = "zdiff3"; 905 | 906 | # Record and replay conflict resolutions. 907 | rerere.enabled = true; 908 | rerere.autoupdate = true; 909 | 910 | # Sort last committed branches to top. 911 | branch.sort = "-committerdate"; 912 | 913 | # Sort highest version to top. 914 | tag.sort = "-v:refname"; 915 | 916 | credential.helper = "${config.programs.git.package}/bin/git-credential-libsecret"; 917 | 918 | # Avoid hint: use --reapply-cherry-picks to include skipped commits 919 | advice.skippedCherryPicks = false; 920 | 921 | # Avoid hint: use git switch -c to retain commits 922 | advice.detachedHead = false; 923 | 924 | help.autocorrect = "prompt"; 925 | 926 | url."git@github.com:".insteadOf = [ 927 | # Normalize GitHub URLs to SSH to avoid authentication issues with HTTPS. 928 | "https://github.com/" 929 | 930 | # Allows typing `git clone github:owner/repo`. 931 | "github:" 932 | ]; 933 | 934 | # Source: https://github.com/rust-lang/cargo/issues/3381#issuecomment-1193730972 935 | # avoid issues where the cargo-edit tool tries to clone from a repo you do not have WRITE access to. 936 | # we already use SSH for every github repo, and so this puts the clone back to using HTTPS. 937 | url."https://github.com/rust-lang/crates.io-index".insteadOf = 938 | "https://github.com/rust-lang/crates.io-index"; 939 | 940 | # avoid issues where the `cargo audit` command tries to clone from a repo you do not have WRITE access to. 941 | # we already use SSH for every github repo, and so this puts the clone back to using HTTPS. 942 | url."https://github.com/RustSec/advisory-db".insteadOf = "https://github.com/RustSec/advisory-db"; 943 | 944 | # Let git absorb look at 100 parents. 945 | absorb.maxStack = 100; 946 | }; 947 | }; 948 | programs.mergiraf.enable = true; 949 | programs.gh = { 950 | enable = true; 951 | settings = { 952 | # See https://github.com/nix-community/home-manager/issues/4744 953 | version = "1"; 954 | editor = "code --wait"; 955 | }; 956 | }; 957 | programs.jq.enable = true; 958 | programs.neovim.enable = true; 959 | programs.nix-index.enable = true; 960 | 961 | # Source: https://discourse.nixos.org/t/atril-is-blurry-engrampa-is-not-sway-scale-2/2865/2 962 | xresources.properties."Xft.dpi" = "96"; 963 | 964 | programs.direnv = { 965 | enable = true; 966 | nix-direnv.enable = true; 967 | 968 | # Store .envrc files outside of project directories. 969 | # Source: https://github.com/nix-community/nix-direnv#storing-direnv-outside-the-project-directory 970 | stdlib = builtins.readFile ./direnvrc; 971 | }; 972 | programs.htop.enable = true; 973 | 974 | services.activitywatch = { 975 | enable = true; 976 | watchers = { 977 | aw-watcher-afk = { 978 | package = pkgs.activitywatch; 979 | }; 980 | }; 981 | }; 982 | 983 | systemd.user.services.activitywatch-watcher-window-hyprland = { 984 | Unit = { 985 | Description = "ActivityWatch watcher 'aw-watcher-window-hyprland'"; 986 | After = [ 987 | "graphical-session.target" 988 | "activitywatch.service" 989 | ]; 990 | BindsTo = [ "activitywatch.target" ]; 991 | ConditionEnvironment = "WAYLAND_DISPLAY"; 992 | }; 993 | Service = { 994 | ExecStart = lib.getExe pkgs.aw-watcher-window-hyprland; 995 | }; 996 | Install = { 997 | WantedBy = [ "activitywatch.target" ]; 998 | }; 999 | }; 1000 | 1001 | home.stateVersion = "21.03"; 1002 | }; 1003 | } 1004 | -------------------------------------------------------------------------------- /home/direnvrc: -------------------------------------------------------------------------------- 1 | : ${XDG_CACHE_HOME:=$HOME/.cache} 2 | declare -A direnv_layout_dirs 3 | direnv_layout_dir() { 4 | echo "${direnv_layout_dirs[$PWD]:="$XDG_CACHE_HOME"/direnv/layouts${PWD#"$HOME"}}" 5 | } 6 | -------------------------------------------------------------------------------- /home/modules/blueberry.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | with lib; 8 | { 9 | options = { 10 | services.blueberry = { 11 | enable = mkEnableOption "" // { 12 | description = '' 13 | Whether to enable the blueberry applet. 14 | 15 | Note, for the applet to work, the 'blueberry' service should 16 | be enabled system-wide. You can enable it in the system 17 | configuration using 18 | 19 | services.blueberry.enable = true; 20 | 21 | ''; 22 | }; 23 | }; 24 | }; 25 | 26 | config = mkIf config.services.blueberry.enable { 27 | home.packages = [ pkgs.blueberry ]; 28 | systemd.user.services.blueberry = { 29 | Unit = { 30 | Description = "blueberry applet"; 31 | # Avoid depencency cycle, resulting in waybar not starting. 32 | # Requires = [ "tray.target" ]; 33 | After = [ "tray.target" ]; 34 | ConditionEnvironment = "WAYLAND_DISPLAY"; 35 | }; 36 | 37 | Install = { 38 | WantedBy = [ "graphical-session.target" ]; 39 | }; 40 | 41 | Service = { 42 | ExecStart = "${pkgs.blueberry}/bin/blueberry"; 43 | }; 44 | }; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /home/modules/nushell.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | with lib; 8 | let 9 | cfg = config.programs.nushell; 10 | formatNuValue = 11 | let 12 | indentation = " "; 13 | indent = 14 | str: 15 | let 16 | lines = splitString "\n" str; 17 | indentedLines = map (line: "${indentation}${line}") lines; 18 | in 19 | lib.concatStringsSep "\n" indentedLines; 20 | in 21 | value: 22 | { 23 | bool = v: if v then "true" else "false"; 24 | int = toString; 25 | float = toString; 26 | string = builtins.toJSON; 27 | null = v: "null"; 28 | path = v: formatNuValue (toString v); 29 | list = v: "[${lib.concatStrings (map (v: "\n${indent (formatNuValue v)}") v)}\n]"; 30 | set = 31 | v: 32 | if nuExpressionType.check v then 33 | v.__nu 34 | else 35 | "{${lib.concatStrings (mapAttrsToList (k: v: "\n${indent "${k}: ${formatNuValue v}"}") v)}\n}"; 36 | } 37 | .${builtins.typeOf value} 38 | value; 39 | 40 | nuExpressionType = mkOptionType { 41 | name = "nu"; 42 | description = "Nu expression"; 43 | check = x: isAttrs x && x ? __nu && isString x.__nu; 44 | merge = mergeEqualOption; 45 | }; 46 | in 47 | { 48 | meta.maintainers = [ maintainers.bobvanderlinden ]; 49 | 50 | options = { 51 | programs.nushell = { 52 | settingss = mkOption { 53 | type = 54 | with lib.types; 55 | let 56 | valueType = 57 | nullOr (oneOf [ 58 | nuExpressionType 59 | bool 60 | int 61 | float 62 | str 63 | path 64 | (attrsOf valueType) 65 | (listOf valueType) 66 | ]) 67 | // { 68 | description = "Nu value type"; 69 | }; 70 | in 71 | valueType; 72 | default = { }; 73 | example = literalExpression '' 74 | { 75 | show_banner = false; 76 | } 77 | ''; 78 | description = '' 79 | Configuration written to 80 | $XDG_CONFIG_HOME/pueue/pueue.yml. 81 | ''; 82 | }; 83 | }; 84 | }; 85 | 86 | config = mkIf cfg.enable (mkMerge [ 87 | { 88 | programs.nushell.extraConfig = mkBefore '' 89 | let-env config = ${formatNuValue cfg.settingss} 90 | ''; 91 | } 92 | { 93 | programs.nushell.extraConfig = mkAfter ( 94 | lib.concatLines ( 95 | mapAttrsToList (k: v: '' 96 | alias ${k} = ${v} 97 | '') cfg.shellAliases 98 | ) 99 | ); 100 | } 101 | ]); 102 | } 103 | -------------------------------------------------------------------------------- /home/modules/swaybg.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | 8 | let 9 | inherit (lib) 10 | mkIf 11 | escapeShellArgs 12 | mapAttrsToList 13 | flatten 14 | optionals 15 | types 16 | ; 17 | cfg = config.programs.swaybg; 18 | outputModule = types.submodule { 19 | options = { 20 | mode = lib.mkOption { 21 | type = types.nullOr ( 22 | types.enum [ 23 | "stretch" 24 | "fill" 25 | "fit" 26 | "center" 27 | "tile" 28 | "solid_color" 29 | ] 30 | ); 31 | default = null; 32 | example = "center"; 33 | description = "Scaling mode for images: stretch, fill, fit, center, or tile. Use the additional mode solid_color to display only the background color, even if a background image is specified."; 34 | }; 35 | color = lib.mkOption { 36 | type = types.nullOr types.str; 37 | default = null; 38 | example = "#000000"; 39 | description = "Set the background color."; 40 | }; 41 | image = lib.mkOption { 42 | type = types.nullOr types.str; 43 | default = null; 44 | example = "/path/to/image.png"; 45 | description = "Path to the image file to use as the background."; 46 | }; 47 | }; 48 | }; 49 | in 50 | { 51 | options = { 52 | programs.swaybg = { 53 | enable = lib.mkEnableOption "swaybg"; 54 | target = lib.mkOption { 55 | type = types.str; 56 | default = "graphical-session.target"; 57 | example = "sway-session.target"; 58 | }; 59 | package = lib.mkOption { 60 | type = types.package; 61 | default = pkgs.swaybg; 62 | }; 63 | 64 | outputs = lib.mkOption { 65 | type = types.attrsOf outputModule; 66 | default = { }; 67 | example = { 68 | "*" = { 69 | mode = "center"; 70 | color = "#000000"; 71 | image = "/path/to/image.png"; 72 | }; 73 | "DP-1" = { 74 | mode = "center"; 75 | color = "#000000"; 76 | image = "/path/to/image.png"; 77 | }; 78 | }; 79 | }; 80 | 81 | }; 82 | }; 83 | 84 | config = mkIf cfg.enable { 85 | systemd.user.services.swaybg = { 86 | Unit = { 87 | Description = "swaybg"; 88 | PartOf = [ cfg.target ]; 89 | After = [ cfg.target ]; 90 | ConditionEnvironment = "WAYLAND_DISPLAY"; 91 | }; 92 | Service = { 93 | ExecStart = "${cfg.package}/bin/swaybg ${ 94 | escapeShellArgs ( 95 | flatten ( 96 | mapAttrsToList ( 97 | output: 98 | { 99 | mode, 100 | color, 101 | image, 102 | }: 103 | ( 104 | [ 105 | "--output" 106 | output 107 | ] 108 | ++ (optionals (mode != null) [ 109 | "--mode" 110 | mode 111 | ]) 112 | ++ (optionals (color != null) [ 113 | "--color" 114 | color 115 | ]) 116 | ++ (optionals (image != null) [ 117 | "--image" 118 | image 119 | ]) 120 | ) 121 | ) cfg.outputs 122 | ) 123 | ) 124 | }"; 125 | }; 126 | Install = { 127 | WantedBy = [ cfg.target ]; 128 | }; 129 | }; 130 | }; 131 | } 132 | -------------------------------------------------------------------------------- /home/modules/xdg-desktop-portal-hyprland.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | 8 | let 9 | cfg = config.services.xdg-desktop-portal-hyprland; 10 | in 11 | { 12 | options.services.xdg-desktop-portal-hyprland = { 13 | enable = lib.mkEnableOption "XDG Desktop Portal Hyprland implementation"; 14 | package = lib.mkOption { 15 | type = lib.types.package; 16 | default = pkgs.xdg-desktop-portal-hyprland; 17 | description = "The xdg-desktop-portal-hyprland package to use."; 18 | }; 19 | target = lib.mkOption { 20 | type = lib.types.str; 21 | default = "graphical-session.target"; 22 | example = "hyprland-session.target"; 23 | description = "The systemd target to bind to."; 24 | }; 25 | settings = lib.mkOption { 26 | type = 27 | with lib.types; 28 | let 29 | valueType = 30 | nullOr (oneOf [ 31 | bool 32 | int 33 | float 34 | str 35 | path 36 | (attrsOf valueType) 37 | (listOf valueType) 38 | ]) 39 | // { 40 | description = "Hyprland configuration value"; 41 | }; 42 | in 43 | valueType; 44 | default = { }; 45 | description = "Settings for the hyprland portal."; 46 | }; 47 | }; 48 | 49 | config = lib.mkIf cfg.enable { 50 | services.xdg-desktop-portal.portals = [ cfg.package ]; 51 | 52 | xdg.configFile."hypr/xdph.conf".text = lib.hm.generators.toHyprconf { 53 | attrs = cfg.settings; 54 | }; 55 | 56 | systemd.user.services.xdg-desktop-portal-hyprland = { 57 | Unit = { 58 | Description = "Portal service (Hyprland implementation)"; 59 | PartOf = [ cfg.target ]; 60 | After = [ cfg.target ]; 61 | ConditionEnvironment = "WAYLAND_DISPLAY"; 62 | }; 63 | Service = { 64 | Type = "dbus"; 65 | BusName = "org.freedesktop.impl.portal.desktop.hyprland"; 66 | ExecStart = "${cfg.package}/libexec/xdg-desktop-portal-hyprland"; 67 | Restart = "on-failure"; 68 | Slice = "session.slice"; 69 | }; 70 | }; 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /home/modules/xdg-desktop-portal.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | # This is a replacement implementation for the xdg.portal module in home-manager. 8 | # It automatically generates default portals based on the packages provided. 9 | # It allows running xdg-desktop-portal in verbose mode, so it is possible to see any errors that might occur. 10 | # It creates a portals directory in Nix, instead of in the user's profile directory. This avoids issues with packages in 11 | # the profile, but not in the xdg-portal option being used as usable portals. 12 | # It avoids using `home.sessionVariables`, which only applies to applications run from the shell and not those run from the compositor. 13 | let 14 | cfg = config.services.xdg-desktop-portal; 15 | portalDir = 16 | pkgs.runCommand "xdg-desktop-portal-dir" 17 | { 18 | nativeBuildInputs = [ pkgs.makeWrapper ]; 19 | env.PACKAGES = lib.escapeShellArgs cfg.portals; 20 | } 21 | '' 22 | function join_by { local IFS="$1"; shift; echo "$*"; } 23 | 24 | mkdir -p $out 25 | DEFAULT_PORTALS=() 26 | for package in $PACKAGES 27 | do 28 | for portal in "$package"/share/xdg-desktop-portal/portals/*.portal 29 | do 30 | DEFAULT_PORTALS+=("$(basename "$portal" .portal)") 31 | if [ ! -f $out/"$portal" ]; then 32 | ln -s "$portal" $out/ 33 | fi 34 | done 35 | done 36 | echo "[preferred]" >> $out/portals.conf 37 | echo "default=$(join_by ';' "''${DEFAULT_PORTALS[@]}")" >> $out/portals.conf 38 | ''; 39 | in 40 | { 41 | options.services.xdg-desktop-portal = { 42 | enable = lib.mkEnableOption "XDG Desktop Portal"; 43 | 44 | package = lib.mkOption { 45 | type = lib.types.package; 46 | default = pkgs.xdg-desktop-portal; 47 | description = "The XDG Desktop Portal package to use."; 48 | }; 49 | 50 | target = lib.mkOption { 51 | type = lib.types.str; 52 | default = "graphical-session.target"; 53 | example = "hyprland-session.target"; 54 | description = "The systemd target to bind to."; 55 | }; 56 | 57 | verbose = lib.mkOption { 58 | type = lib.types.bool; 59 | default = false; 60 | description = "Whether to run the XDG Desktop Portal in verbose mode."; 61 | }; 62 | 63 | portals = lib.mkOption { 64 | type = lib.types.listOf lib.types.package; 65 | default = [ ]; 66 | description = "Packages that contain xdg portals. These packages should contain `/share/xdg-desktop-portal/portals/*.portal` files."; 67 | }; 68 | }; 69 | 70 | config = lib.mkIf cfg.enable { 71 | systemd.user.services.xdg-desktop-portal = { 72 | Unit = { 73 | Description = "XDG Desktop Portal"; 74 | PartOf = [ cfg.target ]; 75 | }; 76 | 77 | Service = { 78 | Type = "dbus"; 79 | BusName = "org.freedesktop.portal.Desktop"; 80 | ExecStart = "${cfg.package}/libexec/xdg-desktop-portal ${lib.optionalString cfg.verbose "--verbose"}"; 81 | Slice = "session.slice"; 82 | Restart = "on-failure"; 83 | Environment = [ 84 | "XDG_DESKTOP_PORTAL_DIR=${portalDir}" 85 | ]; 86 | }; 87 | }; 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /home/modules/xssproxy.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | with lib; 8 | { 9 | options = { 10 | services.xssproxy = { 11 | enable = mkEnableOption "XSSProxy"; 12 | package = mkOption { 13 | type = types.package; 14 | default = pkgs.xssproxy; 15 | defaultText = literalExample "pkgs.xssproxy"; 16 | description = '' 17 | XSSProxy package to use 18 | ''; 19 | }; 20 | }; 21 | }; 22 | 23 | config = mkIf config.services.xssproxy.enable { 24 | systemd.user.services.xssproxy = { 25 | Unit = { 26 | Description = "forward freedesktop.org Idle Inhibition Service calls to Xss"; 27 | After = [ "graphical-session-pre.target" ]; 28 | PartOf = [ "graphical-session.target" ]; 29 | }; 30 | 31 | Install = { 32 | WantedBy = [ "graphical-session.target" ]; 33 | }; 34 | 35 | Service = { 36 | ExecStart = "${config.services.xssproxy.package}/bin/xssproxy"; 37 | }; 38 | }; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /nixos-config.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | }, 6 | { 7 | "path": "../nixpkgs" 8 | }, 9 | { 10 | "path": "../lanzaboote" 11 | } 12 | ], 13 | "settings": { 14 | "nix.enableLanguageServer": true, 15 | "nix.serverPath": "nixd", 16 | "nix.serverSettings": { 17 | "nixpkgs": { 18 | "expr": "import {}" 19 | }, 20 | "nixd": { 21 | "formatting": { 22 | "command": ["nixfmt"] 23 | }, 24 | "options": { 25 | "nixos": { 26 | "expr": "(builtins.getFlake \"/home/bob.vanderlinden/projects/nixos-config\").nixosConfigurations.nac44250.options" 27 | }, 28 | "home-manager": { 29 | "expr": "(builtins.getFlake \"/home/bob.vanderlinden/projects/nixos-config\").homeConfigurations.\"bob.vanderlinden@nac44250\".options" 30 | } 31 | } 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/aw-watcher-window-hyprland/package.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | rustPlatform, 4 | fetchFromGitHub, 5 | pkg-config, 6 | openssl, 7 | hyprland, 8 | }: 9 | 10 | rustPlatform.buildRustPackage rec { 11 | pname = "aw-watcher-window-hyprland"; 12 | version = "0.1.0"; 13 | 14 | src = fetchFromGitHub { 15 | owner = "bobvanderlinden"; 16 | repo = pname; 17 | rev = "v${version}"; 18 | hash = "sha256-7dfEwaub0wVy8QxMfxAmVd3htJ4S+9WOnru/LtnlzQg="; 19 | }; 20 | 21 | cargoHash = "sha256-eA5MzNgTEtNNIHKGj3QG1TUhp1esBIU8Qou0SdoJczs="; 22 | 23 | nativeBuildInputs = [ 24 | pkg-config 25 | ]; 26 | 27 | buildInputs = [ 28 | openssl 29 | ]; 30 | 31 | runtimeDependencies = [ 32 | hyprland 33 | ]; 34 | 35 | meta = with lib; { 36 | description = "ActivityWatch watcher for tracking active window and workspace in Hyprland"; 37 | homepage = "https://github.com/bobvanderlinden/aw-watcher-window-hyprland"; 38 | license = licenses.mit; 39 | maintainers = with maintainers; [ ]; 40 | mainProgram = "aw-watcher-window-hyprland"; 41 | platforms = platforms.linux; 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /packages/coin/package.nix: -------------------------------------------------------------------------------- 1 | { pkgs }: 2 | let 3 | coinSound = pkgs.fetchurl { 4 | url = "https://themushroomkingdom.net/sounds/wav/smw/smw_coin.wav"; 5 | sha256 = "18c7dfhkaz9ybp3m52n1is9nmmkq18b1i82g6vgzy7cbr2y07h93"; 6 | }; 7 | in 8 | pkgs.writeShellScriptBin "coin" '' 9 | ${pkgs.sox}/bin/play --no-show-progress ${coinSound} 10 | '' 11 | -------------------------------------------------------------------------------- /packages/git-worktree-shell/git-worktree-shell.sh: -------------------------------------------------------------------------------- 1 | fail() 2 | { 3 | echo "$@" >&2 4 | exit 1 5 | } 6 | 7 | OPTION_INDEX="1" 8 | ARGS=() 9 | 10 | while [[ $# -gt 0 ]]; do 11 | case $1 in 12 | --) 13 | ARGS+=("$@") 14 | break 15 | ;; 16 | --no-index) 17 | OPTION_INDEX="0" 18 | shift 19 | ;; 20 | --index) 21 | OPTION_INDEX="1" 22 | shift 23 | ;; 24 | -*) 25 | fail "Unknown option $1" 26 | ;; 27 | *) 28 | ARGS+=("$1") 29 | shift 30 | ;; 31 | esac 32 | done 33 | 34 | REVISION="HEAD" 35 | 36 | if [ "${#ARGS[@]}" -eq 1 ] 37 | then 38 | REVISION="${ARGS[0]}" 39 | elif [ "${#ARGS[@]}" -gt 1 ] 40 | then 41 | fail "Too many arguments" 42 | fi 43 | 44 | [ -d .git ] || fail "Not a git directory" 45 | 46 | REPO_NAME="$(basename "$PWD")" 47 | WORKTREE_DIR="$(mktemp --directory --suffix "$REPO_NAME-worktree")" 48 | git worktree add "$WORKTREE_DIR" "$REVISION" 49 | 50 | if [ "$OPTION_INDEX" = 1 ] 51 | then 52 | # Apply the index of the original worktree to the new one. 53 | git diff-index -p --cached HEAD | (cd "$WORKTREE_DIR" && git apply --index --allow-empty) 54 | fi 55 | 56 | (cd "$WORKTREE_DIR" 57 | $SHELL || true 58 | git worktree remove --force "$WORKTREE_DIR" || true 59 | rm -rf "$WORKTREE_DIR" || true 60 | ) 61 | -------------------------------------------------------------------------------- /packages/git-worktree-shell/package.nix: -------------------------------------------------------------------------------- 1 | { writeShellApplication, git }: 2 | writeShellApplication { 3 | name = "git-worktree-shell"; 4 | text = builtins.readFile ./git-worktree-shell.sh; 5 | runtimeInputs = [ git ]; 6 | } 7 | -------------------------------------------------------------------------------- /packages/git-xargs/package.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | buildGoModule, 4 | fetchFromGitHub, 5 | # git, 6 | }: 7 | let 8 | pname = "git-xargs"; 9 | version = "0.1.16"; 10 | in 11 | buildGoModule { 12 | inherit pname version; 13 | src = fetchFromGitHub { 14 | owner = "gruntwork-io"; 15 | repo = pname; 16 | rev = "v${version}"; 17 | hash = "sha256-43HXWV5qrzZL0exsNtpYhJj/wvEWIw8tX5JGdHIZYhY="; 18 | }; 19 | vendorHash = "sha256-PsNCZRz+iUuZN5YUhkitItvx2SQMvTX/t8L/XthwaQs="; 20 | doCheck = false; 21 | # nativeCheckInputs = [ git ]; 22 | # preCheck = '' 23 | # export HOME=$TMPDIR 24 | # git config --global user.email "test@example.com" 25 | # git config --global user.name "Test User" 26 | # ''; 27 | meta = with lib; { 28 | description = "A command-line tool for making updates across multiple Github repositories"; 29 | homepage = "https://github.com/gruntwork-io/git-xargs"; 30 | license = licenses.asl20; 31 | maintainers = [ maintainers.bobvanderlinden ]; 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /packages/hypr-open/hypr-open.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | import subprocess 5 | 6 | parser = argparse.ArgumentParser( 7 | prog="hypr-open", 8 | description="Open application in current workspace", 9 | ) 10 | 11 | parser.add_argument("--window-class", 12 | required=True, 13 | type=str, 14 | help="Window class", 15 | ) 16 | parser.add_argument( 17 | "--new-window-argument", 18 | type=str, 19 | help="Argument to add when opening a new window" 20 | ) 21 | parser.add_argument( 22 | "command", 23 | nargs="*", 24 | type=str, 25 | help="Command to run" 26 | ) 27 | args = parser.parse_args() 28 | 29 | 30 | def exec(cmd): 31 | stdout, stderr = subprocess.Popen( 32 | cmd, stdout=subprocess.PIPE, shell=True 33 | ).communicate() 34 | return stdout.decode("utf-8").strip() 35 | 36 | 37 | active_workspace = json.loads(exec("hyprctl -j activeworkspace")) 38 | 39 | active_workspace_id = active_workspace["id"] 40 | 41 | clients = json.loads(exec("hyprctl -j clients")) 42 | 43 | matching_clients = sorted(( 44 | client 45 | for client in clients 46 | if "class" in client 47 | if client["workspace"]["id"] == active_workspace_id 48 | if client["class"] == args.window_class 49 | ), key=lambda client: client["focusHistoryID"]) 50 | 51 | command_program = args.command[0] 52 | command_args = args.command[1:] 53 | 54 | match matching_clients: 55 | case []: 56 | os.execvp(command_program, [ 57 | command_program, 58 | args.new_window_argument, 59 | *command_args 60 | ]) 61 | case [{"address": str(address)}, *_]: 62 | exec(f'hyprctl dispatch focuswindow address:{address}') 63 | os.execvp(command_program, [command_program, *command_args]) 64 | case _: 65 | raise Exception("unreachable") 66 | -------------------------------------------------------------------------------- /packages/hypr-open/package.nix: -------------------------------------------------------------------------------- 1 | { writers }: 2 | let 3 | content = builtins.readFile ./hypr-open.py; 4 | in 5 | writers.writePython3Bin "hypr-open" { } content 6 | -------------------------------------------------------------------------------- /packages/lazy-desktop/package.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | stdenv, 4 | runCommand, 5 | nix-index, 6 | desktop-file-utils, 7 | }: 8 | let 9 | nix-index-database = builtins.fetchurl { 10 | url = "https://github.com/nix-community/nix-index-database/releases/download/2024-02-11-030837/index-x86_64-linux"; 11 | sha256 = "1zc6wraxrjrm93z71244ly07jbdg2fq2g3dqzf02n55z410cblgv"; 12 | }; 13 | in 14 | stdenv.mkDerivation { 15 | name = "lazy-desktop"; 16 | buildInputs = [ 17 | nix-index 18 | desktop-file-utils 19 | ]; 20 | dontUnpack = true; 21 | dontBuild = true; 22 | installPhase = '' 23 | mkdir -p $out/share/applications 24 | ln -s ${nix-index-database} files 25 | nix-locate \ 26 | --db . \ 27 | --top-level \ 28 | --minimal \ 29 | --regex \ 30 | '/share/applications/.*\.desktop$' \ 31 | | while read -r package 32 | do 33 | cat > $out/share/applications/"$package.desktop" << EOF 34 | [Desktop Entry] 35 | Version=1.0 36 | Name=$package 37 | Type=Application 38 | Exec=nix run "nixpkgs#$package" -- %F 39 | EOF 40 | desktop-file-validate $out/share/applications/"$package.desktop" 41 | done 42 | ''; 43 | meta = { 44 | description = '' 45 | A package with desktop files for all packages in the nix-index database. 46 | When a .desktop is executed it will run the package using `nix run nixpkgs#package`. 47 | 48 | See https://discourse.nixos.org/t/nixpkgs-desktop/39781 49 | ''; 50 | platforms = lib.platforms.all; 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /packages/sway-open/package.nix: -------------------------------------------------------------------------------- 1 | { writers, lib }: 2 | let 3 | content = builtins.readFile ./sway-open.py; 4 | in 5 | writers.writePython3Bin "sway-open" { } content 6 | -------------------------------------------------------------------------------- /packages/sway-open/sway-open.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import json 3 | import os 4 | import argparse 5 | 6 | parser = argparse.ArgumentParser( 7 | prog="sway-open", 8 | description="Open application in current workspace", 9 | ) 10 | 11 | parser.add_argument("--app_id", 12 | required=True, 13 | type=str, 14 | help="Sway Application ID" 15 | ) 16 | parser.add_argument( 17 | "--new-window-argument", 18 | type=str, 19 | help="Argument to add when opening a new window" 20 | ) 21 | parser.add_argument( 22 | "command", 23 | nargs="*", 24 | type=str, 25 | help="Command to run" 26 | ) 27 | args = parser.parse_args() 28 | 29 | 30 | def exec(cmd): 31 | stdout, stderr = subprocess.Popen( 32 | cmd, stdout=subprocess.PIPE, shell=True 33 | ).communicate() 34 | return stdout.decode("utf-8").strip() 35 | 36 | 37 | tree = json.loads(exec("swaymsg -t get_tree --raw")) 38 | 39 | 40 | def get_nodes(type, parent): 41 | def get_focus_index(node): 42 | return ( 43 | parent["focus"].index( 44 | node["id"]) if node["id"] in parent["focus"] else 9999 45 | ) 46 | 47 | return sorted( 48 | [ 49 | node 50 | for node in parent["nodes"] 51 | if node["type"] == type 52 | ], 53 | key=get_focus_index 54 | ) 55 | 56 | 57 | def get_nodes_recursive(type, parent): 58 | for node in get_nodes(type, parent): 59 | yield node 60 | yield from get_nodes_recursive(type, node) 61 | 62 | 63 | apps = [ 64 | app 65 | for output in get_nodes("output", tree) 66 | for workspace in get_nodes("workspace", output)[:1] 67 | for app in get_nodes_recursive("con", workspace) 68 | if "app_id" in app 69 | if app["app_id"] == args.app_id 70 | ] 71 | 72 | command_program = args.command[0] 73 | command_args = args.command[1:] 74 | 75 | match apps: 76 | case [app, *_]: 77 | exec(f'swaymsg [con_id={app["id"]}] focus') 78 | os.execvp(command_program, [command_program, *command_args]) 79 | case []: 80 | os.execvp(command_program, [ 81 | command_program, 82 | args.new_window_argument, 83 | *command_args 84 | ]) 85 | case _: 86 | raise Exception("unreachable") 87 | -------------------------------------------------------------------------------- /packages/swaylock-fprintd/package.nix: -------------------------------------------------------------------------------- 1 | { 2 | swaylock, 3 | fetchFromGitHub, 4 | dbus, 5 | fprintd, 6 | glib, 7 | }: 8 | swaylock.overrideAttrs (oldAttrs: { 9 | src = fetchFromGitHub { 10 | owner = "SL-RU"; 11 | repo = "swaylock-fprintd"; 12 | rev = "ffd639a785df0b9f39e9a4d77b7c0d7ba0b8ef79"; 13 | hash = "sha256-2VklrbolUV00djPt+ngUyU+YMnJLAHhD+CLZD1wH4ww="; 14 | }; 15 | postPatch = '' 16 | substituteInPlace fingerprint/meson.build \ 17 | --replace /usr/share/dbus-1/interfaces/ ${fprintd}/share/dbus-1/interfaces/ 18 | ''; 19 | buildInputs = oldAttrs.buildInputs ++ [ 20 | dbus 21 | fprintd 22 | glib 23 | ]; 24 | }) 25 | -------------------------------------------------------------------------------- /packages/wl-screenrecord/package.nix: -------------------------------------------------------------------------------- 1 | { 2 | writeShellApplication, 3 | parallel, 4 | libnotify, 5 | slurp, 6 | wf-recorder, 7 | wl-clipboard-rs, 8 | xdg-utils, 9 | }: 10 | writeShellApplication { 11 | name = "wl-screenrecord"; 12 | text = builtins.readFile ./wl-screenrecord.sh; 13 | runtimeInputs = [ 14 | parallel 15 | libnotify 16 | slurp 17 | wf-recorder 18 | wl-clipboard-rs 19 | xdg-utils 20 | ]; 21 | } 22 | -------------------------------------------------------------------------------- /packages/wl-screenrecord/wl-screenrecord.sh: -------------------------------------------------------------------------------- 1 | set -o errexit 2 | title="Screen recording" 3 | video_file="$HOME"/recording.mp4 4 | notification_id="$(notify-send --transient --urgency critical --print-id "$title" "Initializing...")" 5 | geometry="$(slurp)" 6 | parallel -j 0 --halt now,done=1 <