dot

NixOS dotfiles
git clone https://git.echoz.io/dot.git
Log | Files | Refs

commit f9a1f35dfe91ffe8aef1849b256a725cb959b185
Author: Chris <chris@echoz.io>
Date:   Tue,  7 Oct 2025 04:30:57 +0200

feat: init

Diffstat:
A.envrc | 1+
A.gitignore | 3+++
Aflake.lock | 307+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aflake.nix | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahosts/rc/default.nix | 31+++++++++++++++++++++++++++++++
Ahosts/vm/default.nix | 22++++++++++++++++++++++
Ahosts/ws/default.nix | 30++++++++++++++++++++++++++++++
Ahosts/ws/pipewire.nix | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/aerc/binds.nix | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/aerc/default.nix | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/aerc/filters.nix | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/boot/default.nix | 15+++++++++++++++
Amodules/dark-mode/default.nix | 32++++++++++++++++++++++++++++++++
Amodules/default.nix | 31+++++++++++++++++++++++++++++++
Amodules/disko/default.nix | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/ephemeral-root/default.nix | 30++++++++++++++++++++++++++++++
Amodules/ephemeral-root/prune-roots.sh | 174+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/firefox/default.nix | 47+++++++++++++++++++++++++++++++++++++++++++++++
Amodules/firefox/policies.nix | 273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/firefox/user.js | 24++++++++++++++++++++++++
Amodules/firefox/userChrome.css | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/flatpak/default.nix | 12++++++++++++
Amodules/git/default.nix | 14++++++++++++++
Amodules/greetd/default.nix | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/hypr/default.nix | 6++++++
Amodules/hypr/hyprland.nix | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/hypr/hyprpaper.nix | 19+++++++++++++++++++
Amodules/hypr/wallpaper.jpg | 0
Amodules/imsh-clients/default.nix | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/kitty/default.nix | 29+++++++++++++++++++++++++++++
Amodules/mutable-files/default.nix | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/neovim/default.nix | 13+++++++++++++
Amodules/neovim/neovim.lua | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/nixpak/default.nix | 11+++++++++++
Amodules/persistence/default.nix | 26++++++++++++++++++++++++++
Amodules/pipewire/default.nix | 11+++++++++++
Amodules/plymouth/default.nix | 10++++++++++
Amodules/rbw/default.nix | 37+++++++++++++++++++++++++++++++++++++
Amodules/secrets/default.nix | 18++++++++++++++++++
Amodules/ssh/default.nix | 5+++++
Amodules/user/default.nix | 40++++++++++++++++++++++++++++++++++++++++
Amodules/walker/default.nix | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/waybar/default.nix | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/waybar/style.css | 40++++++++++++++++++++++++++++++++++++++++
Amodules/wayprompt/default.nix | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/xdg-user-dirs/default.nix | 43+++++++++++++++++++++++++++++++++++++++++++
Amodules/zsh/default.nix | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/zsh/liquidpromptrc | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
48 files changed, 2841 insertions(+), 0 deletions(-)

diff --git a/.envrc b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore @@ -0,0 +1,3 @@ +.direnv/ +/result +/tmp diff --git a/flake.lock b/flake.lock @@ -0,0 +1,307 @@ +{ + "nodes": { + "disko": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1746728054, + "narHash": "sha256-eDoSOhxGEm2PykZFa/x9QG5eTH0MJdiJ9aR00VAofXE=", + "owner": "nix-community", + "repo": "disko", + "rev": "ff442f5d1425feb86344c028298548024f21256d", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "v1.12.0", + "repo": "disko", + "type": "github" + } + }, + "elephant": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "systems": "systems" + }, + "locked": { + "lastModified": 1759390862, + "narHash": "sha256-j1OY4dXsaoE1Pmeo6tn88JNUJdSLnuTSGuNxmWAkYyo=", + "owner": "abenz1267", + "repo": "elephant", + "rev": "2e5c793d2e14dc44318f19149147134db9e0c332", + "type": "github" + }, + "original": { + "owner": "abenz1267", + "repo": "elephant", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "nixpak", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1759362264, + "narHash": "sha256-wfG0S7pltlYyZTM+qqlhJ7GMw2fTF4mLKCIVhLii/4M=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "758cf7296bee11f1706a574c77d072b8a7baa881", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "hercules-ci-effects": { + "inputs": { + "flake-parts": [ + "nixpak", + "flake-parts" + ], + "nixpkgs": [ + "nixpak", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1758022363, + "narHash": "sha256-ENUhCRWgSX4ni751HieNuQoq06dJvApV/Nm89kh+/A0=", + "owner": "hercules-ci", + "repo": "hercules-ci-effects", + "rev": "1a3667d33e247ad35ca250698d63f49a5453d824", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "hercules-ci-effects", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1759337100, + "narHash": "sha256-CcT3QvZ74NGfM+lSOILcCEeU+SnqXRvl1XCRHenZ0Us=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "004753ae6b04c4b18aa07192c1106800aaacf6c3", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "impermanence": { + "locked": { + "lastModified": 1737831083, + "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "impermanence", + "type": "github" + } + }, + "imsh-clients": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1759497641, + "narHash": "sha256-VbzrwiqWhwGQLQiV6TOYiCGMF9+W0RRhHotLZWNiOU4=", + "owner": "echozio", + "repo": "imsh-clients", + "rev": "9fb726805a2f9ae34d80ccf8332a09343dbef709", + "type": "github" + }, + "original": { + "owner": "echozio", + "repo": "imsh-clients", + "type": "github" + } + }, + "nixpak": { + "inputs": { + "flake-parts": "flake-parts", + "hercules-ci-effects": "hercules-ci-effects", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1759373157, + "narHash": "sha256-AdQmn5AASt6nUYxIAo+/+we312zqA0moB/Cuj7TsIC4=", + "owner": "nixpak", + "repo": "nixpak", + "rev": "7cd8f919d173deeb1f6d01cfda22a84eeedba1ae", + "type": "github" + }, + "original": { + "owner": "nixpak", + "repo": "nixpak", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-unstable", + "type": "indirect" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-unstable", + "type": "indirect" + } + }, + "root": { + "inputs": { + "disko": "disko", + "elephant": "elephant", + "home-manager": "home-manager", + "impermanence": "impermanence", + "imsh-clients": "imsh-clients", + "nixpak": "nixpak", + "nixpkgs": "nixpkgs", + "sec": "sec", + "sops-nix": "sops-nix", + "walker": "walker" + } + }, + "sec": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1759804042, + "narHash": "sha256-dvi/FFa9RVS3peH820gXUgl1MIIB73zSYOTg+j/45eo=", + "owner": "echozio", + "repo": "sec", + "rev": "51df35202dc657f66323c0411b18680daa5b7ff1", + "type": "github" + }, + "original": { + "owner": "echozio", + "repo": "sec", + "type": "github" + } + }, + "sops-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1759188042, + "narHash": "sha256-f9QC2KKiNReZDG2yyKAtDZh0rSK2Xp1wkPzKbHeQVRU=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "9fcfabe085281dd793589bdc770a2e577a3caa5d", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1689347949, + "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", + "owner": "nix-systems", + "repo": "default-linux", + "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default-linux", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1689347949, + "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", + "owner": "nix-systems", + "repo": "default-linux", + "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default-linux", + "type": "github" + } + }, + "walker": { + "inputs": { + "elephant": [ + "elephant" + ], + "nixpkgs": [ + "nixpkgs" + ], + "systems": "systems_2" + }, + "locked": { + "lastModified": 1759389183, + "narHash": "sha256-IltPEM2ljbLjEt3XYR7oWNf52JV9Z3M2WkGnz5ELceY=", + "owner": "abenz1267", + "repo": "walker", + "rev": "0709813b758757eaec6628a2ed4180c2f7885c7a", + "type": "github" + }, + "original": { + "owner": "abenz1267", + "repo": "walker", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix @@ -0,0 +1,126 @@ +{ + inputs = { + nixpkgs.url = "nixpkgs/nixos-unstable"; + + disko.url = "github:nix-community/disko/v1.12.0"; + home-manager.url = "github:nix-community/home-manager"; + impermanence.url = "github:nix-community/impermanence"; + sops-nix.url = "github:Mic92/sops-nix"; + walker.url = "github:abenz1267/walker"; + elephant.url = "github:abenz1267/elephant"; + nixpak.url = "github:nixpak/nixpak"; + imsh-clients.url = "github:echozio/imsh-clients"; + + disko.inputs.nixpkgs.follows = "nixpkgs"; + home-manager.inputs.nixpkgs.follows = "nixpkgs"; + sops-nix.inputs.nixpkgs.follows = "nixpkgs"; + walker.inputs.nixpkgs.follows = "nixpkgs"; + walker.inputs.elephant.follows = "elephant"; + elephant.inputs.nixpkgs.follows = "nixpkgs"; + nixpak.inputs.nixpkgs.follows = "nixpkgs"; + imsh-clients.inputs.nixpkgs.follows = "nixpkgs"; + + sec.url = "github:echozio/sec"; + }; + + outputs = + inputs@{ + self, + disko, + nixpkgs, + ... + }: + let + lib = nixpkgs.lib; + systems = [ + "x86_64-linux" + "aarch64-linux" + ]; + argsFor = system: { + pkgs = nixpkgs.legacyPackages.${system}; + disko = disko.packages.${system}.disko; + }; + forAllSystems = f: lib.genAttrs systems (system: f (argsFor system)); + in + { + devShells = forAllSystems ( + { pkgs, disko, ... }: + { + default = pkgs.mkShell { + packages = with pkgs; [ + nixos-rebuild + nixos-anywhere + ssh-to-age + disko + (writeShellScriptBin "generate-host-keys" '' + host="$1" + out="tmp/fix" + if [ -z "$host" ]; then + echo "Usage: $(basename "$0") hostname" >&2 + exit 1 + fi + umask 0022 + mkdir -p "$out/etc/ssh" + ${openssh}/bin/ssh-keygen -t ed25519 -f "$out/etc/ssh/ssh_host_ed25519_key" -C "root@$host" -q -N "" + cat <<EOF + age public key: + $(${ssh-to-age}/bin/ssh-to-age < "$out/etc/ssh/ssh_host_ed25519_key.pub") + + install with: + nixos-anywhere --flake .#$host --extra-files tmp --target-host root@$host + + remember to: + rm -rf tmp + EOF + + '') + ]; + }; + } + ); + packages = forAllSystems ( + { pkgs, ... }: + { + neovim-minimal = pkgs.wrapNeovimUnstable pkgs.neovim-unwrapped { + vimAlias = true; + viAlias = true; + luaRcContent = builtins.readFile ./neovim.lua; + withRuby = false; + }; + } + ); + + nixosModules.default = ./modules; + + nixosConfigurations = + let + mkHost = + module: + lib.nixosSystem { + specialArgs = inputs; + modules = [ + self.nixosModules.default + module + ]; + }; + in + { + vm = mkHost ./hosts/vm; + rc = mkHost ./hosts/rc; + ws = mkHost ./hosts/ws; + }; + + formatter = forAllSystems ( + { pkgs, ... }: + pkgs.treefmt.withConfig { + settings = { + on-unmatched = "info"; + formatter.nixfmt = { + command = lib.getExe pkgs.nixfmt-rfc-style; + includes = [ "*.nix" ]; + }; + }; + } + ); + }; +} diff --git a/hosts/rc/default.nix b/hosts/rc/default.nix @@ -0,0 +1,31 @@ +{ modulesPath, sec, ... }: +{ + imports = [ + sec.nixosModules.dot + + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + disko.devices.disk.system.device = "/dev/disk/by-id/wwn-0x5002538e410ec0c1"; + + networking = { + hostName = "rc"; + hostId = "9cb48259"; + }; + + boot = { + initrd.availableKernelModules = [ + "xhci_pci" + "ehci_pci" + "ahci" + "sd_mod" + "sdhci_pci" + ]; + kernelModules = [ "kvm-intel" ]; + }; + + hardware = { + enableAllFirmware = true; + cpu.intel.updateMicrocode = true; + }; +} diff --git a/hosts/vm/default.nix b/hosts/vm/default.nix @@ -0,0 +1,22 @@ +{ + modulesPath, + sec, + user, + ... +}: +{ + imports = [ + sec.nixosModules.dot + + (modulesPath + "/profiles/qemu-guest.nix") + ]; + + disko.devices.disk.system.device = "/dev/vda"; + + networking = { + hostName = "vm"; + hostId = "522ef8a2"; + }; + + home-manager.users.${user}.wayland.windowManager.hyprland.settings."$mod" = "ALT"; +} diff --git a/hosts/ws/default.nix b/hosts/ws/default.nix @@ -0,0 +1,30 @@ +{ modulesPath, sec, ... }: +{ + imports = [ + sec.nixosModules.dot + ./pipewire.nix + + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + disko.devices.disk.system.device = "/dev/disk/by-id/nvme-eui.00000000000000000026b7282f657265"; + + networking = { + hostName = "ws"; + hostId = "e27df163"; + }; + + boot = { + initrd.availableKernelModules = [ + "nvme" + "thunderbolt" + "usbhid" + ]; + kernelModules = [ "kvm-amd" ]; + }; + + hardware = { + enableAllFirmware = true; + cpu.amd.updateMicrocode = true; + }; +} diff --git a/hosts/ws/pipewire.nix b/hosts/ws/pipewire.nix @@ -0,0 +1,115 @@ +{ + lib, + ... +}: +{ + services.pipewire.wireplumber.extraConfig = { + devices."monitor.alsa.rules" = [ + { + matches = lib.singleton { + "alsa.card_name" = "Xonar STX"; + "media.class" = "Audio/Sink"; + }; + actions.update-props = { + "node.description" = "Desktop"; + "node.nick" = "Desktop"; + }; + } + { + matches = lib.singleton { + "alsa.card_name" = "Xonar STX"; + "media.class" = "Audio/Source"; + }; + actions.update-props."node.disabled" = true; + } + { + matches = lib.singleton { + "alsa.card_name" = "RODE NT-USB"; + "media.class" = "Audio/Source"; + }; + actions.update-props = { + "node.description" = "Voice"; + "node.nick" = "Voice"; + }; + } + { + matches = lib.singleton { + "alsa.card_name" = "RODE NT-USB"; + "media.class" = "Audio/Sink"; + }; + action.update-props."node.disabled" = true; + } + { + matches = lib.singleton { + "alsa.card_name" = "HDA ATI HDMI"; + }; + actions.update-props."node.disabled" = true; + } + { + matches = lib.singleton { + "alsa.card_name" = "HD 720P webcam"; + }; + actions.update-props."node.disabled" = true; + } + { + matches = lib.singleton { + "alsa.card_name" = "HD-Audio Generic"; + }; + actions.update-props."node.disabled" = true; + } + ]; + loopback = { + "wireplumber.components" = lib.singleton { + name = "loopback.lua"; + type = "script/lua"; + provides = "custom.loopback"; + }; + "wireplumber.profiles".main."custom.loopback" = "required"; + }; + }; + services.pipewire.wireplumber.extraScripts."loopback.lua" = '' + masterOutput = LocalModule("libpipewire-module-loopback", [[ + audio.position = [ FL FR ] + capture.props = { + media.class = Audio/Sink + node.name = "capture.master" + node.description = "Master" + node.latency = 1024/44100 + audio.rate = 44100 + audio.channels = 2 + audio.position = [ FL FR ] + } + playback.props = { + node.name = "playback.master" + node.description = "Master" + node.latency = 1024/44100 + audio.rate = 44100 + audio.channels = 2 + audio.position = [ AUX0 AUX1 ] + target.object = "alsa_output.usb-DENON_DJ_DENON_DJ_MC7000_201603-00.pro-output-0" + } + ]]) + + headphoneOutput = LocalModule("libpipewire-module-loopback", [[ + audio.position = [ FL FR ] + capture.props = { + media.class = Audio/Sink + node.name = "capture.headphones" + node.description = "Headphones" + node.latency = 1024/44100 + audio.rate = 44100 + audio.channels = 2 + audio.position = [ FL FR ] + } + playback.props = { + node.name = "playback.headphones" + node.description = "Headphones" + node.latency = 1024/44100 + audio.rate = 44100 + audio.channels = 2 + audio.position = [ AUX2 AUX3 ] + target.object = "alsa_output.usb-DENON_DJ_DENON_DJ_MC7000_201603-00.pro-output-0" + } + ]]) + ''; +} diff --git a/modules/aerc/binds.nix b/modules/aerc/binds.nix @@ -0,0 +1,192 @@ +{ + lib, + config, + pkgs, + + user, + ... +}: +{ + home-manager.users.${user}.programs.aerc.extraBinds = lib.mkMerge [ + { + global = { + "<C-p>" = ":prev-tab<Enter>"; + "<C-n>" = ":next-tab<Enter>"; + "?" = ":help keys<Enter>"; + "<C-t>" = ":term<Enter>"; + }; + + # Actions + messages = { + d = ":read<Enter>,:move Trash<Enter>"; + a = ":archive flat<Enter>"; + f = ":move Important<Enter>"; + + m = ":compose<Enter>"; + b = ":bounce<space>"; + rr = ":reply -a<Enter>"; + rq = ":reply -aq<Enter>"; + Rr = ":reply<Enter>"; + Rq = ":reply -q<Enter>"; + F = ":forward<Enter>"; + + v = ":mark -t<Enter>"; + V = ":mark -v<Enter>"; + }; + + # Navigation + messages = { + "<Enter>" = ":view<Enter>"; + + j = ":next<Enter>"; + k = ":prev<Enter>"; + + "<C-d>" = ":next 50%<Enter>"; + "<C-u>" = ":prev 50%<Enter>"; + + "<C-f>" = ":next 100%<Enter>"; + "<C-b>" = ":prev 100%<Enter>"; + + G = ":select -1<Enter>"; + gg = ":select 0<Enter>"; + + J = ":next-folder<Enter>"; + K = ":prev-folder<Enter>"; + H = ":collapse-folder<Enter>"; + L = ":expand-folder<Enter>"; + + zz = ":align center<Enter>"; + zt = ":align top<Enter>"; + zb = ":align bottom<Enter>"; + }; + + # Search + messages = { + "/" = ":search<space>"; + "\\" = ":filter<space>"; + n = ":next-result<Enter>"; + N = ":prev-result<Enter>"; + + "<Esc>" = ":clear<Enter>:unmark -a<Enter>"; + }; + + messages = { + T = ":toggle-threads<Enter>"; + zc = ":fold<Enter>"; + zo = ":unfold<Enter>"; + za = ":fold -t<Enter>"; + zM = ":fold -a<Enter>"; + zR = ":unfold -a<Enter>"; + + c = ":cf<space>"; + + s = ":split<Enter>"; + S = ":vsplit<Enter>"; + + "|" = ":pipe<space>"; + pl = ":patch list<Enter>"; + pa = ":patch apply <Tab>"; + pd = ":patch drop <Tab>"; + pb = ":patch rebase <Tab>"; + pt = ":patch term<Enter>"; + ps = ":patch switch <Tab>"; + }; + + "messages:folder=Drafts" = { + "<Enter>" = ":recall<Enter>"; + }; + + view = { + "/" = ":toggle-key-passthrough<Enter>/"; + q = ":close<Enter>"; + O = ":open<Enter>"; + o = ":open<Enter>"; + S = ":save<space>"; + "|" = ":pipe<space>"; + D = ":delete<Enter>"; + A = ":archive flat<Enter>"; + + "<C-y>" = ":copy-link <space>"; + "<C-l>" = ":open-link <space>"; + + r = ":reply -a<Enter>"; + rq = ":reply -aq<Enter>"; + Rr = ":reply<Enter>"; + Rq = ":reply -q<Enter>"; + F = ":forward<Enter>"; + + H = ":toggle-headers<Enter>"; + "<C-k>" = ":prev-part<Enter>"; + "<C-j>" = ":next-part<Enter>"; + J = ":next<Enter>"; + K = ":prev<Enter>"; + }; + + "view::passthrough" = { + "$noinherit" = true; + "$ex" = "<C-x>"; + "<Esc>" = ":toggle-key-passthrough<Enter>"; + }; + + compose = { + "$noinherit" = true; + "$ex" = "<C-x>"; + "$complete" = "<C-o>"; + + "<C-k>" = ":prev-field<Enter>"; + "<C-j>" = ":next-field<Enter>"; + "<A-p>" = ":switch-account -p<Enter>"; + "<A-n>" = ":switch-account -n<Enter>"; + "<C-p>" = ":prev-tab<Enter>"; + "<C-n>" = ":next-tab<Enter>"; + }; + + "compose::editor" = { + "$noinherit" = true; + "$ex" = "<C-x>"; + "<C-k>" = ":prev-field<Enter>"; + "<C-j>" = ":next-field<Enter>"; + "<C-p>" = ":prev-tab<Enter>"; + "<C-n>" = ":next-tab<Enter>"; + }; + + "compose::review" = { + y = ":send<Enter>"; + s = ":sign<Enter>"; + x = ":encrypt<Enter>"; + v = ":preview<Enter>"; + p = ":postpone<Enter>"; + q = ":choose -o d discard abort -o p postpone postpone<Enter>"; + Q = ":abort<Enter>"; + e = ":edit<Enter>"; + a = ":attach<space>"; + d = ":detach<space>"; + }; + + terminal = { + "$noinherit" = true; + "$ex" = "<C-x>"; + + "<C-p>" = ":prev-tab<Enter>"; + "<C-n>" = ":next-tab<Enter>"; + }; + } + + (lib.mapAttrs' ( + name: account: + let + passwordCmd = lib.concatStringsSep " " account.passwordCommand; + in + lib.nameValuePair "messages:account=${name}" { + "gs" = ":term ${pkgs.writeShellScript "sieve-edit" '' + printf "\033]0;%s\a" "Sieve: ${name}" + "${lib.getExe pkgs.sieve-connect}" \ + --user "${account.userName}" \ + --server "${account.imap.host}" \ + --passwordfd 5 \ + 5<<<"$(${passwordCmd})" + ''}<Enter>"; + } + ) config.home-manager.users.${user}.accounts.email.accounts) + ]; +} diff --git a/modules/aerc/default.nix b/modules/aerc/default.nix @@ -0,0 +1,53 @@ +{ + lib, + config, + pkgs, + + user, + ... +}: +{ + imports = [ + ./filters.nix + ./binds.nix + ]; + + config = { + environment.persistence."/fix".users.${user}.directories = [ + { + directory = ".cache/aerc"; + mode = "0700"; + } + ]; + + home-manager.users.${user} = { + options.accounts.email.accounts = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule { + config.aerc.enable = true; + } + ); + }; + + config = { + programs.aerc = { + enable = true; + extraConfig = { + general.unsafe-accounts-conf = true; + + ui = { + mouse-enabled = true; + dirlist-tree = true; + fuzzy-complete = true; + }; + + openers = { + "text/html" = "firefox"; + "application/pdf" = "firefox"; + }; + }; + }; + }; + }; + }; +} diff --git a/modules/aerc/filters.nix b/modules/aerc/filters.nix @@ -0,0 +1,56 @@ +{ + lib, + pkgs, + + user, + + mkNixPak, + ... +}: +{ + home-manager.users.${user}.programs.aerc.extraConfig.filters = { + "text/plain" = "colorize"; + "text/calendar" = "calendar"; + "message/rfc822" = "colorize"; + "message/delivery-status" = "colorize"; + ".headers" = "colorize"; + + "text/html" = builtins.toString ( + lib.getExe + (mkNixPak { + config.app.package = pkgs.writeShellScriptBin "w3m-html-filter" '' + export LC_ALL=C.UTF-8 + exec ${lib.getExe pkgs.w3m} -dump -T text/html -no-cookie -config /dev/null \ + | ${lib.getExe pkgs.perl} -CS -ne 's/[\p{M}\p{Cf}]//g; s/[ \t\p{Zs}]+$//; print unless /^[ \t\p{Zs}]*$/ && $p; $p=/^[ \t\p{Zs}]*$/;' + ''; + config.dbus.enable = false; + config.bubblewrap.network = false; + config.bubblewrap.bindEntireStore = false; + }).config.script + ); + + "application/pdf" = builtins.toString ( + lib.getExe + (mkNixPak { + config.app.package = pkgs.writeShellScriptBin "w3m-pdf-filter" '' + exec ${pkgs.poppler-utils}/bin/pdftotext -layout - - + ''; + config.dbus.enable = false; + config.bubblewrap.network = false; + config.bubblewrap.bindEntireStore = false; + }).config.script + ); + + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" = builtins.toString ( + lib.getExe + (mkNixPak { + config.app.package = pkgs.writeShellScriptBin "w3m-pdf-filter" '' + exec ${lib.getExe pkgs.pandoc} -f docx -t plain + ''; + config.dbus.enable = false; + config.bubblewrap.network = false; + config.bubblewrap.bindEntireStore = false; + }).config.script + ); + }; +} diff --git a/modules/boot/default.nix b/modules/boot/default.nix @@ -0,0 +1,15 @@ +{ config, ... }: +{ + config = { + boot = { + initrd.systemd.enable = true; + + zfs.devNodes = builtins.dirOf config.disko.devices.disk.system.device; + + loader = { + systemd-boot.enable = true; + efi.canTouchEfiVariables = true; + }; + }; + }; +} diff --git a/modules/dark-mode/default.nix b/modules/dark-mode/default.nix @@ -0,0 +1,32 @@ +{ + pkgs, + + user, + ... +}: +{ + programs.dconf.enable = true; + home-manager.users.${user} = { + dconf.settings = { + "org/gnome/desktop/interface".color-scheme = "prefer-dark"; + }; + + gtk = { + enable = true; + + gtk3.extraConfig.gtk-application-prefer-dark-theme = true; + + theme = { + name = "Adwaita:Dark"; + package = pkgs.gnome-themes-extra; + }; + }; + + qt = { + enable = true; + + platformTheme.name = "adwaita"; + style.name = "adwaita-dark"; + }; + }; +} diff --git a/modules/default.nix b/modules/default.nix @@ -0,0 +1,31 @@ +{ + lib, + config, + ... +}: +{ + imports = lib.mapAttrsToList (path: _: ./. + "/${path}") ( + lib.filterAttrs (path: type: type == "directory") (builtins.readDir ./.) + ); + + system.stateVersion = lib.mkDefault "25.05"; + + nixpkgs = { + hostPlatform = lib.mkDefault "x86_64-linux"; + config.allowUnfree = true; + }; + + nix.settings = { + experimental-features = [ + "nix-command" + "flakes" + ]; + trusted-users = [ "@wheel" ]; + }; + + users.mutableUsers = false; + + security.sudo.extraConfig = '' + Defaults lecture = never + ''; +} diff --git a/modules/disko/default.nix b/modules/disko/default.nix @@ -0,0 +1,73 @@ +{ config, disko, ... }: +{ + imports = [ disko.nixosModules.disko ]; + + disko.devices = { + disk.system = { + type = "disk"; + + content = { + type = "gpt"; + partitions = { + boot = { + size = "1G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "nofail" ]; + }; + }; + + zfs = { + size = "100%"; + content = { + type = "zfs"; + pool = "system"; + }; + }; + }; + }; + }; + + zpool.system = { + type = "zpool"; + rootFsOptions = { + mountpoint = "none"; + compression = "zstd"; + acltype = "posixacl"; + xattr = "sa"; + encryption = "aes-256-gcm"; + keyformat = "passphrase"; + keylocation = "prompt"; + }; + options.ashift = "12"; + + datasets = { + root = { + type = "zfs_fs"; + options.mountpoint = "none"; + }; + + "root/current" = { + type = "zfs_fs"; + options.mountpoint = "legacy"; + mountpoint = "/"; + }; + + nix = { + type = "zfs_fs"; + options.mountpoint = "legacy"; + mountpoint = "/nix"; + }; + + fix = { + type = "zfs_fs"; + options.mountpoint = "legacy"; + mountpoint = "/fix"; + }; + }; + }; + }; +} diff --git a/modules/ephemeral-root/default.nix b/modules/ephemeral-root/default.nix @@ -0,0 +1,30 @@ +{ + lib, + config, + pkgs, + ... +}: +{ + config = { + boot.initrd.systemd.services.zfs-rotate-root = { + wantedBy = [ "initrd.target" ]; + after = [ "zfs-import-system.service" ]; + before = [ "sysroot.mount" ]; + path = with pkgs; [ + zfs + coreutils + ]; + description = "rotate root dataset"; + unitConfig.DefaultDependencies = "no"; + serviceConfig.Type = "oneshot"; + script = '' + zfs rename "system/root/current" "system/root/$(date -ud "@$(zfs get -Hpo value creation system/root/current)" +%Y-%m-%dT%H-%M-%SZ)" + zfs create -o mountpoint=legacy "system/root/current" + ''; + }; + + environment.systemPackages = [ + (pkgs.writeShellScriptBin "prune-roots" (builtins.readFile ./prune-roots.sh)) + ]; + }; +} diff --git a/modules/ephemeral-root/prune-roots.sh b/modules/ephemeral-root/prune-roots.sh @@ -0,0 +1,174 @@ +argv0="prune-roots" + +help() { +cat <<EOF +$argv0 - Prune non-current root filesystems. + +Usage: $argv0 [options...] + +Options: + -k, --keep=n + Keep the latest n roots. + + -b, --before=date + Prune roots from before date. + + -y, --yes + Delete filesystems without confirmation. + + -n, --dry-run + List filesystems that would be deleted. + + -l, --list + List all non-current filesystems along with their creation times. + + -h, --help + Display this message. + +If both --keep and --before are specified, a filesystem will need to +match both filters to be selected for deletion. +EOF +} + +list() { + local filesystems=("$@") + ( + printf "AGE NAME CREATION\n" + for i in "${!filesystems[@]}"; do + age="$((${#filesystems[@]}-$i))" + fs="${filesystems[$i]}" + creation="$(date -d "@$(zfs get -Hpo value creation "$fs")")" + printf "%d %s %s\n" "$age" "$fs" "$creation" + done + ) | column -tl3 +} + +keep="" +before="" +confirmation="" +dry="" + +readarray -t filesystems < <(\ + zfs list -t filesystem -Ho name -s creation \ + | awk '/^system\/root\// && !/^system\/root\/current$/' \ +) + +while [ $# -gt 0 ]; do + case "$1" in + -k*|--keep|--keep=*) + keep="${1/#-k/}" + if [ -z "$keep" ] || [ "$keep" = "$1" ]; then + keep="${1#*=}" + fi + if [ -z "$keep" ] || [ "$keep" = "$1" ]; then + if [ $# -lt 2 ]; then + printf "Error: Flag needs an argument: %s\n" "$1" >&2 + exit 1 + fi + keep="$2" + shift + fi + shift + if [ ! "$keep" -ge 0 ]; then + printf "Error: Invalid value for --keep: Must be an unsigned integer.\n" >&2 + exit 1 + fi + ;; + -b*|--before*) + before="${1/#-b/}" + if [ -z "$before" ] || [ "$before" = "$1" ]; then + before="${1#*=}" + fi + if [ -z "$before" ] || [ "$before" = "$1" ]; then + if [ $# -lt 2 ]; then + printf "Error: Flag needs an argument: %s\n" "$1" >&2 + exit 1 + fi + before="$2" + shift + fi + shift + if date -d "$before" 2>/dev/null >/dev/null; then + before="$(date -d "$before" +%s)" + else + error="$(date -d "$before" 2>&1)" + error="${error#date: }" + printf "Error: Invalid value for --before: %s\n" "${error^}" >&2 + exit 1 + fi + ;; + -h*|--help*) + help + exit 0 + ;; + -y|--yes) + confirmation="Yes" + shift + ;; + -n|--dry-run) + dry=1 + shift + ;; + -l|--list) + list "${filesystems[@]}" + exit 0 + ;; + *) + printf "Error: Unknown argument: %s\n" "$1" >&2 + exit 1 + esac +done + +delete=() +for i in "${!filesystems[@]}"; do + fs="${filesystems[$i]}" + creation="$(zfs get -Hpo value creation "$fs")" + if [ ! -z "$before" ] && [ "$creation" -ge "$before" ]; then + continue + fi + if [ ! -z "$keep" ] && [ $((${#filesystems[@]}-$i)) -le "$keep" ]; then + continue + fi + delete+=($fs) +done + +if [ ${#delete[@]} -le 0 ]; then + printf "Nothing to do: No matching filesystems.\n" >&2 + exit 0 +fi + +list "${delete[@]}" + +if [ ! -z "$dry" ]; then + exit 0 +fi + +if [ "$confirmation" != "Yes" ]; then + printf "\n" + read -r -p "Type 'Yes' to confirm deletion of ${#delete[@]} volumes: " confirmation >&2 + if [ "$confirmation" != "Yes" ]; then + printf "Abort.\n" >&2 + exit 1 + fi +fi + +for i in "${!delete[@]}"; do + fs="${filesystems[$i]}" + elevate="" + if [ $UID -ne 0 ]; then + elevate="sudo" + error=$($elevate true 2>&1) + if [ $? -ne 0 ]; then + error="${error#sudo: }" + printf "Error: Could not elevate privileges: %s\n" "${error^}" >&2 + exit 1 + fi + fi + printf "Deleting filesystem '%s'...\n" "$fs" >&2 + error="$($elevate zfs destroy "$fs" 2>&1)" + if [ $? -ne 0 ]; then + printf "Error: Could not delete filesystem: %s\n" "${error^}" >&2 + exit 1 + fi +done + diff --git a/modules/firefox/default.nix b/modules/firefox/default.nix @@ -0,0 +1,47 @@ +{ + lib, + + user, + ... +}: +{ + imports = [ + ./policies.nix + ]; + + environment.persistence."/fix".users.${user}.directories = [ + { + directory = ".mozilla"; + mode = "0700"; + } + ]; + + home-manager.users.${user} = { + wayland.windowManager.hyprland.settings.bind = [ "$mod, W, exec, uwsm app -- firefox" ]; + + programs.firefox = { + enable = true; + + profiles.default = { + id = 0; + isDefault = true; + containersForce = true; + containers = { + personal = { + id = 0; + color = "green"; + icon = "fingerprint"; + }; + work = { + id = 1; + color = "blue"; + icon = "briefcase"; + }; + }; + + extraConfig = builtins.readFile ./user.js; + userChrome = builtins.readFile ./userChrome.css; + }; + }; + }; +} diff --git a/modules/firefox/policies.nix b/modules/firefox/policies.nix @@ -0,0 +1,273 @@ +{ + name, + lib, + + user, + ... +}: +let + installExtension = id: attrs: { + ${id} = { + installation_mode = "normal_installed"; + install_url = "https://addons.mozilla.org/firefox/downloads/latest/${id}/latest.xpi"; + } + // attrs; + }; +in +{ + # Read: https://mozilla.github.io/policy-templates/ + + home-manager.users.${user}.programs.firefox.policies = { + ExtensionUpdate = true; + ExtensionSettings = lib.mkMerge [ + (installExtension "{446900e4-71c2-419f-a6a7-df9c091e268b}" { + # Bitwarden + private_browsing = true; + default_area = "navbar"; + }) + (installExtension "uBlock0@raymondhill.net" { + private_browsing = true; + default_area = "navbar"; + }) + (installExtension "addon@darkreader.org" { + private_browsing = true; + default_area = "navbar"; + }) + (installExtension "firefox@tampermonkey.net" { + private_browsing = true; + default_area = "navbar"; + }) + (installExtension "soundfixer@unrelenting.technology" { + private_browsing = true; + default_area = "navbar"; + }) + (installExtension "{d7742d87-e61d-4b78-b8a1-b469842139fa}" { + # Vimium + private_browsing = true; + default_area = "menupanel"; + }) + (installExtension "{0c3ab5c8-57ac-4ad8-9dd1-ee331517884d}" { + # Proxy Toggle + private_browsing = true; + default_area = "menupanel"; + }) + + # YouTube repairs + (installExtension "sponsorBlocker@ajay.app" { + private_browsing = true; + default_area = "menupanel"; + }) + (installExtension "{0d7cafdd-501c-49ca-8ebb-e3341caaa55e}" { + # YouTube NonStop + private_browsing = true; + default_area = "menupanel"; + }) + (installExtension "{7b1bf0b6-a1b9-42b0-b75d-252036438bdc}" { + # YouTube High Definition + private_browsing = true; + default_area = "menupanel"; + }) + (installExtension "{762f9885-5a13-4abd-9c77-433dcd38b8fd}" { + # Return YouTube Dislike + private_browsing = true; + default_area = "menupanel"; + }) + ]; + + AutofillAddressEnabled = false; + AutofillCreditCardEnabled = false; + Cookies = { + Behavior = "reject-tracker-and-partition-foreign"; + BehaviorPrivateBrowsing = "reject-tracker-and-partition-foreign"; + }; + DefaultDownloadDirectory = "\${home}/dls"; + DownloadDirectory = "\${home}/dls"; + DisableFeedbackCommands = true; + DisableFirefoxAccounts = true; + DisableFirefoxScreenshots = true; + DisableFirefoxStudies = true; + DisableFormHistory = true; + DisableMasterPasswordCreation = true; + DisablePasswordReveal = true; + DisablePocket = true; + DisableProfileImport = true; + DisableProfileRefresh = true; + DisableSetDesktopBackground = true; + DisableSystemAddonUpdate = true; + DisableTelemetry = true; + DisplayBookmarksToolbar = "never"; + DisplayMenuBar = "never"; + DNSOverHTTPS.Enabled = false; + DontCheckDefaultBrowser = true; + EnableTrackingProtection = { + Value = true; + Cryptomining = true; + Fingerprinting = true; + EmailTracking = true; + SuspectedFingerprinting = true; + }; + EncryptedMediaExtensions = { + Enabled = false; + Locked = true; + }; + ExemptDomainFileTypePairsFromFileTypeDownloadWarnings = [ + { + file_extension = "*"; + domains = [ "*" ]; + } + ]; + FirefoxHome = { + Search = false; + TopSites = false; + SponsoredTopSites = false; + Highlights = false; + Pocket = false; + Stories = false; + SponsoredPocket = false; + SponsoredStories = false; + Snippets = false; + Locked = true; + }; + FirefoxSuggest = { + WebSuggestions = false; + SponsoredSuggestions = false; + ImproveSuggest = false; + Locked = true; + }; + GoToIntranetSiteForSingleWordEntryInAddressBar = false; + HardwareAcceleration = true; + HttpsOnlyMode = "disabled"; + LegacyProfiles = false; + LegacySameSiteCookieBehaviorEnabled = false; + ManualAppUpdateOnly = true; + NetworkPrediction = false; + NewTabPage = false; + NoDefaultBookmarks = true; + OfferToSaveLogins = false; + OverrideFirstRunPage = ""; + OverridePostUpdatePage = ""; + PasswordManagerEnabled = false; + Preferences = { + "accessibility.browsewithcaret" = { + Value = false; + Status = "locked"; + }; + "accessibility.browsewithcaret_shortcut.enabled" = { + Value = false; + Status = "locked"; + }; + "browser.aboutConfig.showWarning" = { + Value = false; + Status = "locked"; + }; + "browser.compactmode.show" = { + Value = true; + Status = "locked"; + }; + "browser.link.open_newwindow.restriction" = { + Value = 0; + Status = "locked"; + Type = "number"; + }; + "browser.ml.enable" = { + Value = false; + Status = "locked"; + }; + "browser.search.region" = { + Value = "US"; + Status = "locked"; + }; + "browser.uidensity" = { + Value = 1; + Status = "locked"; + Type = "number"; + }; + "dom.block_download_insecure" = { + Value = false; + Status = "locked"; + }; + "extensions.ui.dictionary.hidden" = { + Value = true; + Status = "locked"; + }; + "network.IDN_show_punycode" = { + Value = true; + Status = "locked"; + }; + "print.more-settings.open" = { + Value = true; + Status = "locked"; + }; + "print.print_bgcolor" = { + Value = false; + Status = "locked"; + }; + "print.print_bgimages" = { + Value = false; + Status = "locked"; + }; + "ui.context_menus.after_mouseup" = { + Value = true; + Status = "locked"; + }; + "ui.key.menuAccessKeyFocuses" = { + Value = false; + Status = "locked"; + }; + "browser.theme.content-theme" = { + Value = 2; + Status = "locked"; + }; + "browser.theme.dark-private-windows" = { + Value = true; + Status = "locked"; + }; + "browser.theme.dark-toolbar-theme" = { + Value = true; + Status = "locked"; + }; + "browser.theme.native-theme" = { + Value = true; + Status = "locked"; + }; + "browser.theme.toolbar-theme" = { + Value = 0; + Status = "locked"; + Type = "number"; + }; + "browser.newtabpage.activity-stream.system.showWeather" = { + Value = false; + Status = "locked"; + }; + "browser.newtabpage.activity-stream.telemetry" = { + Value = false; + Status = "locked"; + }; + }; + PrimaryPassword = false; + PopupBlocking.Default = false; + RequestedLocales = [ "en-US" ]; + SanitizeOnShutdown = { + Cache = true; + FormData = true; + }; + SearchBar = "unified"; + SearchEngines = { + PreventInstalls = true; + }; + SearchSuggestEnabled = false; + ShowHomeButton = false; + SkipTermsOfUse = true; + StartDownloadsInTempDirectory = true; + UserMessaging = { + WhatsNew = false; + ExtensionRecommendations = false; + FeatureRecommendations = false; + UrlbarInterventions = false; + SkipOnboarding = true; + MoreFromMozilla = false; + FirefoxLabs = false; + Locked = true; + }; + }; +} diff --git a/modules/firefox/user.js b/modules/firefox/user.js @@ -0,0 +1,24 @@ +// Restore previous session +user_pref("browser.startup.page", 3); + +// Middle mouse scroll +user_pref("general.autoScroll", true); + +// Don't navigate history with alt+scroll +user_pref("mousewheel.with_alt.action", 1); + +// Enable browser toolbox +user_pref("devtools.chrome.enabled", true); +user_pref("devtools.debugger.remote-enabled", true); + +user_pref("browser.sessionstore.resume_from_crash", true); +user_pref("general.aboutConfig.enable", true); +user_pref("middlemouse.paste", false); +user_pref("sidebar.animation.enabled", false); +user_pref("image.jxl.enabled", true); +user_pref("devtools.cache.disabled", true); +user_pref("devtools.netmonitor.persistlog", true); +user_pref("devtools.webconsole.persistlog", true); +user_pref("devtools.webconsole.timestampMessages", true); + +user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true); diff --git a/modules/firefox/userChrome.css b/modules/firefox/userChrome.css @@ -0,0 +1,64 @@ +@-moz-document url(chrome://browser/content/browser.xul), url(chrome://browser/content/browser.xhtml) { + #browser { + background-color: rgba(0,0,0,0.2) !important; + } + + #navigator-toolbox { + background-color: rgba(0,0,0,0.2) !important; + border-bottom: none !important; + } + + #firefox-view-button, + toolbarbutton.titlebar-close, + toolbarspring, + toolbarbutton#alltabs-button, + #urlbar-searchmode-switcher, + /* #unified-extensions-button, */ + image.tab-close-button.close-icon, + toolbarbutton#tabs-newtab-button, + splitter#sidebar-tools-and-extensions-splitter, + .titlebar-spacer { + display: none !important; + } + + #urlbar hbox.urlbar-background, + toolbar#nav-bar { + background: transparent !important; + } + + #urlbar[breakout-extend] hbox.urlbar-background { + background: #222 !important; + } + + tabs#tabbrowser-tabs { + border-inline-start: none !important; + } + + + .tabbrowser-tab:hover > .tab-stack > &:not([selected], [multiselected]) { + background-color: rgba(0,0,0,0.2) !important; + } + + .tab-background { + &:is([selected], [multiselected]) { + background-color: rgba(255,255,255,0.1) !important; + } + } + + toolbaritem#ublock0_raymondhill_net-browser-action image.toolbarbutton-icon { + filter: grayscale(100%) invert(100%); + } + + toolbaritem#_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action image.toolbarbutton-icon, + button[title="Bitwarden"] img { + filter: grayscale(100%) invert(100%) contrast(200%); + } + + toolbaritem#addon_darkreader_org-browser-action image.toolbarbutton-icon { + filter: grayscale(100%) invert(100%) contrast(250%); + } + + stack.tab-icon-stack { + display: none; + } +} diff --git a/modules/flatpak/default.nix b/modules/flatpak/default.nix @@ -0,0 +1,12 @@ +{ user, ... }: +{ + services.flatpak.enable = true; + + environment.persistence."/fix" = { + directories = [ "/var/lib/flatpak" ]; + users.${user}.directories = [ + ".local/share/flatpak" + ".var/app" + ]; + }; +} diff --git a/modules/git/default.nix b/modules/git/default.nix @@ -0,0 +1,14 @@ +{ + lib, + + user, + email, + ... +}: +{ + home-manager.users.${user}.programs.git = { + enable = true; + userName = lib.toSentenceCase user; + userEmail = email; + }; +} diff --git a/modules/greetd/default.nix b/modules/greetd/default.nix @@ -0,0 +1,58 @@ +{ + lib, + config, + pkgs, + + user, + + home-manager, + ... +}: +{ + config = { + services.greetd = { + enable = true; + settings = { + default_session.command = "Hyprland --config ${ + pkgs.writeText "greetd-hyprland.conf" ( + home-manager.lib.hm.generators.toHyprconf { + attrs = { + inherit (config.home-manager.users.${user}.wayland.windowManager.hyprland.settings) + input + general + misc + decoration + animations + ; + windowrule = "float, class:gtkgreet"; + exec-once = [ + "${lib.getExe pkgs.gtkgreet} -s ${pkgs.writeText "greetd-style.css" '' + window, button, entry { + background: rgba(0,0,0,0.2); + border: none; + box-shadow: none; + text-shadow: none; + } + label, window, button, entry { + color: #eee; + } + #command-selector arrow { + opacity: 0; + } + ''} -c 'uwsm start hyprland-uwsm.desktop'; hyprctl dispatch exit" + "${lib.getExe pkgs.hyprpaper} -c ${ + pkgs.writeText "greetd-hyprpaper.conf" ( + home-manager.lib.hm.generators.toHyprconf { + attrs = config.home-manager.users.${user}.services.hyprpaper.settings; + } + ) + }" + ]; + }; + } + ) + }"; + }; + }; + }; +} diff --git a/modules/hypr/default.nix b/modules/hypr/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./hyprland.nix + ./hyprpaper.nix + ]; +} diff --git a/modules/hypr/hyprland.nix b/modules/hypr/hyprland.nix @@ -0,0 +1,145 @@ +{ + lib, + pkgs, + + user, + ... +}: +{ + programs.hyprland = { + enable = true; + xwayland.enable = true; + withUWSM = true; + }; + + home-manager.users.${user} = + { config, ... }: + { + wayland.windowManager.hyprland = { + enable = true; + package = null; + portalPackage = null; + settings = { + input = { + kb_layout = "us"; + kb_options = "compose:ralt,caps:escape"; + follow_mouse = 1; + touchpad.natural_scroll = false; + sensitivity = 0; + accel_profile = "flat"; + }; + + general = { + gaps_in = 5; + gaps_out = 20; + border_size = 2; + "col.active_border" = "rgba(00000000) rgba(ffffffff) rgba(00000000) 45deg"; + "col.inactive_border" = "rgba(00000000)"; + layout = "dwindle"; + }; + + decoration = { + rounding = 10; + shadow = { + enabled = false; + range = 4; + render_power = 3; + color = "rgba(1a1a1aee)"; + }; + blur = { + enabled = true; + size = 9; + passes = 3; + new_optimizations = true; + }; + }; + + animations = { + enabled = lib.mkDefault true; + bezier = [ + "linear, 0, 0, 1, 1" + "easeOut, 0.42, 0, 0.58, 1" + "easeInOut, 0, 0, 0.58, 1" + "easeIn, 0.42, 0, 1, 1" + ]; + animation = [ + "windows, 1, 2, linear" + "windowsOut, 1, 2, linear" + "border, 1, 2, linear" + "borderangle, 1, 100, linear, loop" + "fade, 1, 2, linear" + "workspaces, 1, 2, linear" + ]; + }; + + misc = { + disable_hyprland_logo = true; + disable_splash_rendering = true; + disable_hyprland_qtutils_check = true; + }; + + ecosystem = { + no_donation_nag = true; + no_update_news = true; + }; + + "$mod" = lib.mkDefault "SUPER"; + + bind = [ + "$mod, Q, killactive," + "$mod SHIFT, Delete, exit," + "$mod, F, fullscreen, 0" + "$mod SHIFT, F, togglefloating," + "$mod, G, togglegroup," + "$mod SHIFT, G, lockgroups," + "$mod, Tab, changegroupactive," + "$mod, P, pseudo," + "$mod, O, togglesplit," + + "$mod, 1, workspace, 1" + "$mod, 2, workspace, 2" + "$mod, 3, workspace, 3" + "$mod, 4, workspace, 4" + "$mod, 5, workspace, 5" + "$mod, 6, workspace, 6" + "$mod, 7, workspace, 7" + "$mod, 8, workspace, 8" + "$mod, 9, workspace, 9" + "$mod, 0, workspace, 10" + + "$mod SHIFT, 1, movetoworkspace, 1" + "$mod SHIFT, 2, movetoworkspace, 2" + "$mod SHIFT, 3, movetoworkspace, 3" + "$mod SHIFT, 4, movetoworkspace, 4" + "$mod SHIFT, 5, movetoworkspace, 5" + "$mod SHIFT, 6, movetoworkspace, 6" + "$mod SHIFT, 7, movetoworkspace, 7" + "$mod SHIFT, 8, movetoworkspace, 8" + "$mod SHIFT, 9, movetoworkspace, 9" + "$mod SHIFT, 0, movetoworkspace, 10" + ]; + + bindm = [ + "$mod, mouse:272, movewindow" + "$mod, mouse:273, resizewindow" + ]; + + bindni = [ ", Alt_L, sendkeystate, , Alt_L, down, class:discord" ]; + bindnir = [ ", Alt_L, sendkeystate, , Alt_L, up, class:discord" ]; + + windowrule = [ "noblur, xwayland:1" ]; + }; + }; + + xdg.configFile."uwsm/env".source = + "${config.home.sessionVariablesPackage}/etc/profile.d/hm-session-vars.sh"; + + home.pointerCursor = { + enable = true; + gtk.enable = true; + name = "Adwaita"; + package = pkgs.gnome-themes-extra; + size = 24; + }; + }; +} diff --git a/modules/hypr/hyprpaper.nix b/modules/hypr/hyprpaper.nix @@ -0,0 +1,19 @@ +{ + lib, + config, + + user, + ... +}: +{ + config = { + + home-manager.users.${user}.services.hyprpaper = { + enable = true; + settings = { + preload = [ "${./wallpaper.jpg}" ]; + wallpaper = [ ",${./wallpaper.jpg}" ]; + }; + }; + }; +} diff --git a/modules/hypr/wallpaper.jpg b/modules/hypr/wallpaper.jpg Binary files differ. diff --git a/modules/imsh-clients/default.nix b/modules/imsh-clients/default.nix @@ -0,0 +1,69 @@ +{ + user, + + imsh-clients, + ... +}: +{ + home-manager.users.${user} = { + imports = [ + imsh-clients.homeManagerModules.imsh-clients + ]; + + programs.imsh-clients = { + enable = true; + imsh-cast-monitor.waybar.enable = true; + }; + + wayland.windowManager.hyprland.settings.bind = + let + cat = builtins.concatStringsSep " "; + in + [ + (cat [ + "$mod, A, exec, uwsm app --" + "imsh-shot screen --cursor --utc --copy" + "--output ~/pic/scr/%Y-%m-%dT%H-%M-%S.%3NZ.png" + ]) + (cat [ + "$mod SHIFT, A, exec, uwsm app --" + "imsh-shot screen --cursor --utc --copy" + "--output ~/pic/scr/%Y-%m-%dT%H-%M-%S.%3NZ.png" + "--api-key '%rbw get scrn.is/api-key' --upload" + ]) + (cat [ + "$mod, S, exec, uwsm app --" + "imsh-shot area --freeze --utc --copy" + "--output ~/pic/scr/%Y-%m-%dT%H-%M-%S.%3NZ.png" + ]) + (cat [ + "$mod SHIFT, S, exec, uwsm app --" + "imsh-shot area --freeze --utc --copy" + "--output ~/pic/scr/%Y-%m-%dT%H-%M-%S.%3NZ.png" + "--api-key '%rbw get scrn.is/api-key' --upload" + ]) + (cat [ + "$mod, D, exec, uwsm app --" + "imsh-shot active --cursor --utc --copy" + "--output ~/pic/scr/%Y-%m-%dT%H-%M-%S.%3NZ.png" + ]) + (cat [ + "$mod SHIFT, D, exec, uwsm app --" + "imsh-shot active --cursor --utc --copy" + "--output ~/pic/scr/%Y-%m-%dT%H-%M-%S.%3NZ.png" + "--api-key '%rbw get scrn.is/api-key' --upload" + ]) + (cat [ + "$mod, R, exec, uwsm app --" + "imsh-cast --utc --copy" + "--output ~/vid/rec/%Y-%m-%dT%H-%M-%S.%3NZ.mp4" + ]) + (cat [ + "$mod SHIFT, R, exec, uwsm app --" + "imsh-cast --utc --copy" + "--output ~/vic/rec/%Y-%m-%dT%H-%M-%S.%3NZ.mp4" + "--api-key '%rbw get scrn.is/api-key' --upload" + ]) + ]; + }; +} diff --git a/modules/kitty/default.nix b/modules/kitty/default.nix @@ -0,0 +1,29 @@ +{ + lib, + config, + pkgs, + + user, + ... +}: +{ + config.home-manager.users.${user} = { + wayland.windowManager.hyprland.settings.bind = [ "$mod, Return, exec, uwsm app -- kitty" ]; + + programs.kitty = { + enable = true; + font = { + name = "JetBrains Mono Nerd Font Mono"; + size = 12; + package = pkgs.nerd-fonts.jetbrains-mono; + }; + settings = { + background = "#000000"; + background_opacity = 0.2; + window_padding_width = 10; + disable_ligatures = false; + confirm_os_window_close = 0; + }; + }; + }; +} diff --git a/modules/mutable-files/default.nix b/modules/mutable-files/default.nix @@ -0,0 +1,85 @@ +{ + home-manager.sharedModules = [ + ( + { lib, config, ... }: + let + fileOptionAttrPaths = [ + [ + "home" + "file" + ] + [ + "xdg" + "configFile" + ] + [ + "xdg" + "dataFile" + ] + ]; + + mergeAttrsList = builtins.foldl' (lib.mergeAttrs) { }; + + fileAttrsType = lib.types.attrsOf ( + lib.types.submodule ({ + options.mutable = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to copy the file without the read-only attribute instead of + symlinking. If you set this to `true`, you must also set `force` to + `true`. Mutable files are not removed when you remove them from your + configuration. + + This option is useful for programs that don't have a very good + support for read-only configurations. + ''; + }; + }) + ); + in + { + options = mergeAttrsList ( + map ( + attrPath: lib.setAttrByPath attrPath (lib.mkOption { type = fileAttrsType; }) + ) fileOptionAttrPaths + ); + + config.home.activation.mutableFileGeneration = + let + allFiles = ( + builtins.concatLists ( + map (attrPath: builtins.attrValues (lib.getAttrFromPath attrPath config)) fileOptionAttrPaths + ) + ); + + filterMutableFiles = builtins.filter ( + file: + (file.mutable or false) + && lib.assertMsg file.force "if you set `mutable` to `true` on a file you must also set `force` to `true`" + ); + + mutableFiles = filterMutableFiles allFiles; + + toCommand = ( + file: + let + source = lib.escapeShellArg file.source; + target = lib.escapeShellArg file.target; + in + '' + $VERBOSE_ECHO "${source} -> ${target}" + $DRY_RUN_CMD cp --remove-destination --no-preserve=mode ${source} ${target} + '' + ); + + command = '' + echo "Copying mutable home files for $HOME" + '' + + lib.concatLines (map toCommand mutableFiles); + in + (lib.hm.dag.entryAfter [ "linkGeneration" ] command); + } + ) + ]; +} diff --git a/modules/neovim/default.nix b/modules/neovim/default.nix @@ -0,0 +1,13 @@ +{ lib, config, ... }: +{ + programs.neovim = { + enable = true; + defaultEditor = true; + viAlias = true; + vimAlias = true; + configure.customLuaRC = builtins.readFile ./neovim.lua; + withRuby = lib.mkDefault false; + withPython3 = lib.mkDefault false; + withNodeJs = lib.mkDefault false; + }; +} diff --git a/modules/neovim/neovim.lua b/modules/neovim/neovim.lua @@ -0,0 +1,116 @@ +vim.opt.tabstop = 2 +vim.opt.shiftwidth = 0 +vim.opt.softtabstop = 0 +vim.opt.expandtab = true +vim.opt.number = true +vim.opt.relativenumber = true +vim.opt.scrolloff = 5 +vim.opt.path = vim.o.path .. '**' +vim.opt.wildmenu = true +vim.opt.cursorline = true +vim.opt.undofile = true +vim.opt.undodir = vim.fn.stdpath('data') .. '/undodir' +vim.opt.colorcolumn = '+1' +vim.opt.textwidth = 72 +vim.opt.formatoptions = 'cqj' +vim.keymap.set({'n','v'}, '<C-c>', '"+y') +vim.keymap.set({'n','v'}, '<C-v>', '"+p') +vim.keymap.set({'n','v'}, '<C-x>', '"+d') +vim.keymap.set({'n','v'}, '<C-q>', '<C-v>', { noremap = true}) +vim.api.nvim_create_autocmd('BufReadPost', { + group = vim.api.nvim_create_augroup('ReturnToLastLine', { clear = true }), + callback = function() + local last = vim.fn.line([['"]]) + if last > 1 and last < vim.fn.line("$") then + vim.cmd([[normal! g'"]]) + end + end +}) +vim.opt.list = true +vim.opt.listchars = { + trail = '~', + tab = '| ', + leadmultispace = ':' .. string.rep(' ', vim.opt.tabstop:get() - 1), +} +vim.api.nvim_create_autocmd('OptionSet', { + group = vim.api.nvim_create_augroup('ListcharsLeadmultispaceWidth', { clear = true }), + pattern = 'tabstop', + callback = function() + local listchars = vim.opt.listchars:get() + listchars.leadmultispace = ':' .. string.rep(' ', vim.opt.tabstop:get() - 1) + vim.opt.listchars = listchars + end +}) +vim.opt.cmdheight = 0 +vim.api.nvim_create_autocmd("RecordingEnter", { + callback = function() + vim.opt.cmdheight = 1 + end, +}) +vim.api.nvim_create_autocmd("RecordingLeave", { + callback = function() + vim.opt.cmdheight = 0 + end, +}) +vim.api.nvim_set_hl(0, 'Normal', { bg='none' }) +vim.api.nvim_set_hl(0, 'StatusLine', { bg='none' }) +vim.api.nvim_set_hl(0, 'WinBar', { bg='none' }) +vim.api.nvim_set_hl(0, 'WinBarNC', { bg='none', fg='gray' }) +function _G.WinBar() + local buffers = {} + local current = vim.api.nvim_win_get_buf(0) + for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do + if vim.bo[bufnr].buflisted then + local name = vim.fn.fnamemodify(vim.fn.bufname(bufnr), ":t") + if name == "" then name = "[No name]" end + local highlight = bufnr == current and "WinBar" or "WinBarNC" + local modified = vim.bo[bufnr].modified and "*" or "" + table.insert(buffers, string.format( + "%%#%s# %d:%s%s %%*", + highlight, bufnr, name, modified + )) + end + end + return table.concat(buffers) +end +vim.api.nvim_create_autocmd({ 'BufEnter', 'BufAdd', 'BufDelete'}, { + group = vim.api.nvim_create_augroup("WinBarVisibility", { clear = true }), + pattern = "*", + callback = function() + local buffers = 0 + for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do + if vim.bo[bufnr].buflisted then + buffers = buffers + 1 + end + end + if buffers > 1 then + vim.opt.winbar = "%!v:lua.WinBar()" + else + vim.opt.winbar = "" + end + end +}) +vim.diagnostic.config({ virtual_text = true }) +vim.opt.signcolumn = "yes" +vim.opt.completeopt = { "menuone", "noselect", "popup" } +vim.api.nvim_create_autocmd('InsertCharPre', { + group = vim.api.nvim_create_augroup("TriggerAutocomplete", { clear = true }), + buffer = vim.api.nvim_get_current_buf(), + callback = function() + vim.lsp.completion.get() + end +}) +vim.lsp.config('*', { + root_markers = { '.git' }, + on_attach = function(client, bufnr) + vim.lsp.completion.enable(true, client.id, bufnr, {}) + end, +}) +vim.lsp.config('nixd', { + cmd = { 'nixd' }, + filetypes = { 'nix' }, + root_markers = { 'flake.nix', '.git' }, +}) +vim.lsp.enable({ + "nixd", +}) diff --git a/modules/nixpak/default.nix b/modules/nixpak/default.nix @@ -0,0 +1,11 @@ +{ + lib, + pkgs, + nixpak, + ... +}: +{ + _module.args.mkNixPak = nixpak.lib.nixpak { + inherit lib pkgs; + }; +} diff --git a/modules/persistence/default.nix b/modules/persistence/default.nix @@ -0,0 +1,26 @@ +{ + lib, + config, + + impermanence, + ... +}: +{ + imports = [ impermanence.nixosModules.impermanence ]; + + config = { + fileSystems."/fix".neededForBoot = true; + + environment.persistence."/fix" = { + hideMounts = true; + + files = [ + "/etc/machine-id" + "/etc/ssh/ssh_host_ed25519_key" + "/etc/ssh/ssh_host_ed25519_key.pub" + "/etc/ssh/ssh_host_rsa_key" + "/etc/ssh/ssh_host_rsa_key.pub" + ]; + }; + }; +} diff --git a/modules/pipewire/default.nix b/modules/pipewire/default.nix @@ -0,0 +1,11 @@ +{ + security.rtkit.enable = true; + services.pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + jack.enable = true; + wireplumber.enable = true; + }; +} diff --git a/modules/plymouth/default.nix b/modules/plymouth/default.nix @@ -0,0 +1,10 @@ +{ + pkgs, + ... +}: +{ + config.boot.plymouth = { + enable = true; + font = "${pkgs.nerd-fonts.jetbrains-mono}/share/fonts/truetype/NerdFonts/JetBrainsMono/JetBrainsMonoNerdFontPropo-Regular.ttf"; + }; +} diff --git a/modules/rbw/default.nix b/modules/rbw/default.nix @@ -0,0 +1,37 @@ +{ + user, + email, + ... +}: +{ + environment.persistence."/fix".users.${user} = { + files = [ + { + file = ".local/share/rbw/device_id"; + parentDirectory.mode = "0700"; + } + ]; + directories = [ + { + directory = ".cache/rbw"; + mode = "0700"; + } + ]; + }; + + home-manager.users.${user} = + { config, ... }: + { + home.sessionVariables = { + SSH_AUTH_SOCK = "$XDG_RUNTIME_DIR/rbw/ssh-agent-socket"; + }; + + programs.rbw = { + enable = true; + settings = { + email = email; + pinentry = config.programs.wayprompt.package; + }; + }; + }; +} diff --git a/modules/secrets/default.nix b/modules/secrets/default.nix @@ -0,0 +1,18 @@ +{ + lib, + config, + sops-nix, + ... +}: +{ + imports = [ sops-nix.nixosModules.sops ]; + + config = { + home-manager.sharedModules = [ sops-nix.homeManagerModules.sops ]; + + sops = { + age.sshKeyPaths = [ "/fix/etc/ssh/ssh_host_ed25519_key" ]; + gnupg.sshKeyPaths = [ ]; + }; + }; +} diff --git a/modules/ssh/default.nix b/modules/ssh/default.nix @@ -0,0 +1,5 @@ +{ + services.openssh = { + enable = true; + }; +} diff --git a/modules/user/default.nix b/modules/user/default.nix @@ -0,0 +1,40 @@ +{ + lib, + config, + pkgs, + + user, + + home-manager, + ... +}: +{ + imports = [ + home-manager.nixosModules.home-manager + ]; + + config = { + users = { + users.${user} = { + isNormalUser = true; + hashedPasswordFile = config.sops.secrets.password.path; + group = user; + uid = 1000; + useDefaultShell = true; + extraGroups = [ + "wheel" + "video" + "audio" + ]; + }; + + groups.${user}.gid = 1000; + }; + + sops.secrets.password.neededForUsers = true; + + home-manager.users.${user} = { + home.stateVersion = lib.mkDefault config.system.stateVersion; + }; + }; +} diff --git a/modules/walker/default.nix b/modules/walker/default.nix @@ -0,0 +1,64 @@ +{ + lib, + config, + + user, + + walker, + ... +}: +{ + config.home-manager.users.${user} = { + imports = [ walker.homeManagerModules.walker ]; + + wayland.windowManager.hyprland.settings = { + bind = [ "$mod, Escape, exec, uwsm app -- walker" ]; + layerrule = [ + "blur,walker" + "ignorezero,walker" + ]; + }; + + programs.walker = { + enable = true; + runAsService = true; + + config = { + placeholders.default = { + input = "Search..."; + list = ""; + }; + providers = { + empty = [ ]; + }; + }; + + theme.style = '' + * { + background: none; + border: none; + box-shadow: none; + color: #eee; + font-family: JetBrains Mono Nerd Font Propo; + font-size: 12pt; + outline: none; + } + + .box-wrapper { + padding: 20px; + border-radius: 20px; + background: rgba(0,0,0,0.2); + } + ''; + + elephant = { + installService = true; + config = { + providers.desktopapplications = { + launch_prefix = "uwsm app --"; + }; + }; + }; + }; + }; +} diff --git a/modules/waybar/default.nix b/modules/waybar/default.nix @@ -0,0 +1,95 @@ +{ + lib, + config, + + user, + ... +}: +{ + home-manager.users.${user} = { + wayland.windowManager.hyprland.settings.layerrule = [ "blur,waybar" ]; + + programs.waybar = { + enable = true; + systemd.enable = true; + + style = builtins.readFile ./style.css; + + settings = { + mainBar = { + layer = "top"; + position = "top"; + height = 36; + modules-left = [ + "hyprland/workspaces" + "hyprland/window" + ]; + modules-center = [ + "clock#date" + "clock" + "clock#utc" + ]; + modules-right = [ + "battery" + "cpu" + "memory" + "disk" + "tray" + ]; + "hyprland/workspaces" = { + format = "{name}"; + on-click = "activate"; + sort-by = "number"; + }; + "clock#date" = { + interval = 1; + format = "󰃭 {:%a, %b %d}"; + }; + clock = { + interval = 1; + format = " {:%I:%M:%S%p}"; + }; + "clock#utc" = { + interval = 1; + format = " {:%I:%M:%S%p}"; + timezone = "UTC"; + }; + cpu = { + interval = 1; + format = " {usage}%"; + }; + memory = { + interval = 1; + format = " {percentage}%"; + }; + disk = { + interval = 1; + format = " {percentage_used}%"; + path = "/"; + }; + battery = { + bat = "BAT0"; + interval = 1; + states = { + warning = 20; + critical = 10; + }; + format = "{icon} {capacity}%"; + format-icons = [ + "" + "" + "" + "" + "" + ]; + tooltip-format = "{timeTo}"; + }; + tray = { + icon-size = 18; + spacing = 10; + }; + }; + }; + }; + }; +} diff --git a/modules/waybar/style.css b/modules/waybar/style.css @@ -0,0 +1,40 @@ +* { + border: none; + border-radius: 0; + font-family: JetBrains Mono Nerd Font Propo; + font-size: 12pt; +} + +window#waybar { + border: none; + color: #fff; + background: rgba(0,0,0,0.2); +} + +.modules-right > widget > *, #window { + padding: 0 10px; +} + +.modules-center > widget > * { + padding: 0 10px; +} + +#tray { + padding: 0; + padding-right: 10px; +} + +#workspaces button { + color: #fff; + transition: box-shadow 0.1s linear; +} + +#workspaces button:hover { + background: inherit; + box-shadow: inset 0px 2px #777; + text-shadow: inherit; +} + +#workspaces button.active { + box-shadow: inset 0px 2px #fff; +} diff --git a/modules/wayprompt/default.nix b/modules/wayprompt/default.nix @@ -0,0 +1,51 @@ +{ + lib, + + user, + ... +}: +{ + home-manager.users.${user} = { + wayland.windowManager.hyprland.settings.layerrule = [ + "blur,wayprompt" + "ignorezero,wayprompt" + ]; + + programs.wayprompt = { + enable = true; + + settings = { + general = { + font-regular = "JetBrains Mono Nerd Font Propo:size=12"; + font-large = "JetBrains Mono Nerd Font Propo:size=24"; + corner-radius = 10; + border = 0; + pin-square-amount = 32; + }; + + colours = { + background = "00000033"; + border = "00000033"; + text = "ffffffff"; + error-text = "ffffffff"; + + pin-background = "00000033"; + pin-border = "eeeeeeff"; + pin-square = "eeeeeeff"; + + ok-button = "00000033"; + ok-button-border = "eeeeeeff"; + ok-button-text = "eeeeeeff"; + + not-ok-button = "00000033"; + not-ok-button-border = "eeeeeeff"; + not-ok-button-text = "eeeeeeff"; + + cancel-button = "00000033"; + cancel-button-border = "eeeeeeff"; + cancel-button-text = "eeeeeeff"; + }; + }; + }; + }; +} diff --git a/modules/xdg-user-dirs/default.nix b/modules/xdg-user-dirs/default.nix @@ -0,0 +1,43 @@ +{ + lib, + config, + + user, + ... +}: +{ + config = { + home-manager.users.${user}.xdg.userDirs = + let + tilde = config.home-manager.users.${user}.home.homeDirectory; + in + { + enable = true; + createDirectories = true; + + # cli friendly names + documents = "${tilde}/doc"; + download = "${tilde}/dls"; + music = "${tilde}/mus"; + pictures = "${tilde}/pic"; + videos = "${tilde}/vid"; + + # some non-standard ones that should be there + extraConfig = { + XDG_GIT_DIR = "${tilde}/git"; + XDG_TMP_DIR = "${tilde}/tmp"; + XDG_MNT_DIR = "${tilde}/mnt"; + + # important git repos + XDG_DOT_DIR = "${tilde}/dot"; + XDG_SEC_DIR = "${tilde}/sec"; + XDG_SRV_DIR = "${tilde}/srv"; + }; + + # map useless dirs to tmp + desktop = "${tilde}/tmp"; + publicShare = "${tilde}/tmp"; + templates = "${tilde}/tmp"; + }; + }; +} diff --git a/modules/zsh/default.nix b/modules/zsh/default.nix @@ -0,0 +1,55 @@ +{ + lib, + pkgs, + ... +}: +{ + config = { + users = { + defaultUserShell = pkgs.zsh; + users.root.shell = pkgs.zsh; + }; + + environment.systemPackages = [ pkgs.fzf ]; + + programs = { + # Use this and remove other fzf-related config if this is made to work together with + # zsh-vi-mode (currently breaks ^r binding): + # fzf.keybindings = true; + # fzf.fuzzyCompletion = true; + git.enable = true; + direnv.enable = true; + }; + + programs.zsh = { + enable = true; + + shellInit = '' + # Silence new user configuration prompt + zsh-newuser-install() { :; } + ''; + + interactiveShellInit = '' + zvm_after_init() { + source <(fzf --zsh) + } + source ${pkgs.zsh-vi-mode}/share/zsh-vi-mode/zsh-vi-mode.plugin.zsh + ZSH_AUTOSUGGEST_STRATEGY=(history completion) + ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=200 + ZSH_AUTOSUGGEST_HISTORY_IGNORE="?(#c200,)" + source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh + source ${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh + ''; + + promptInit = '' + source ${pkgs.liquidprompt}/bin/liquidprompt + ''; + }; + + environment.etc."liquidpromptrc".source = ./liquidpromptrc; + + home-manager.sharedModules = lib.singleton { + programs.zsh.enable = true; + }; + }; +} diff --git a/modules/zsh/liquidpromptrc b/modules/zsh/liquidpromptrc @@ -0,0 +1,70 @@ +LP_ENV_VARS=( + "DIRENV_DIR +d" +) +LP_ENABLE_GIT=1 +LP_ENABLE_SSH_COLORS=1 +LP_ENABLE_KUBECONTEXT=1 +LP_ENABLE_KUBE_NAMESPACE=1 +LP_USER_ALWAYS=0 +LP_ENABLE_DIRSTACK=1 +LP_ENABLE_DISK=1 +LP_DISK_THRESHOLD=$((1024 * 1024)) +LP_DISK_THRESHOLD_PERC=1 +LP_ENABLE_RAM=1 +LP_RAM_THRESHOLD=0 +LP_RAM_THRESHOLD_PERC=10 +LP_ENABLE_RUNTIME=1 +LP_RUNTIME_THRESHOLD=2 +LP_ENABLE_TITLE=1 +LP_ENABLE_SCREEN_TITLE=1 + +LP_ENABLE_BATT=0 +LP_ENABLE_BZR=0 +LP_ENABLE_CHROOT=0 +LP_ENABLE_DETACHED_SESSIONS=0 +LP_ENABLE_DISPLAY=0 +LP_ENABLE_FOSSIL=0 +LP_ENABLE_HG=0 +LP_ENABLE_LOAD=0 +LP_ENABLE_MODULES=0 +LP_ENABLE_MODULES_VERSIONS=0 +LP_ENABLE_PERL_VENV=0 +LP_ENABLE_RUBY_VENV=0 +LP_ENABLE_SCLS=0 +LP_ENABLE_SVN=0 +LP_ENABLE_TEMP=0 +LP_ENABLE_VIRTUALENV=0 + +LP_MARK_BRACKET_CLOSE="" +LP_MARK_BRACKET_OPEN="" +LP_MARK_DEV_CLOSE="" +LP_MARK_DEV_MID=" " +LP_MARK_DEV_OPEN="" +LP_MARK_ENV_VARS_OPEN=" " +LP_MARK_ENV_VARS_SEP=" " +LP_MARK_ENV_VARS_CLOSE="" +LP_MARK_KUBECONTEXT="${BLUE}k=" +LP_MARK_DISK="df=" +LP_MARK_RAM="free=" +LP_MARK_JOBS_SEPARATOR="/" +LP_MARK_PERM="" +LP_COLOR_WRITE="" +LP_COLOR_NOWRITE="${BOLD_RED}ro=" +LP_MARK_PROXY=" +proxy" +LP_MARK_SHLVL=" l=" +LP_MARK_SHORTEN_PATH="..." +LP_MARK_UNTRACKED="*" +LP_MARK_GIT="%%" +LP_MARK_DIRSTACK="d=" + +LP_COLOR_CHANGES="$BOLD_RED" +LP_COLOR_KUBECONTEXT="$BLUE" +LP_COLOR_PATH="$CYAN" +LP_COLOR_PATH_ROOT="$CYAN" +LP_COLOR_PATH_LAST_DIR="$BOLD_CYAN" +LP_COLOR_PATH_SEPARATOR="$WHITE" +LP_COLOR_PATH_SHORTENED="$WHITE" +LP_COLOR_PATH_VCS_ROOT="$YELLOW" +LP_COLOR_ENV_VARS_SET="$BLUE" +LP_COLOR_ENV_VARS_UNSET="$RED" +