diff --git a/AGENTS.md b/AGENTS.md index 031f08f2..5af0c3e3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,10 +1,23 @@ +# Architecture + +This configuration follows the [dendritic pattern](doc/DENDRITIC.md). Key points: + +- All modules live in `parts/` +- Each feature file configures both NixOS and home-manager aspects together +- Host-specific values are in top-level config options (`config.dendrix.*`), not `specialArgs` +- Features own their persistence paths + # Bash commands + - `nh os switch`: build and switch to new NixOS generation (including home-manager) - `nh os build`: build only # Code style + ## Shell Scripts in Nix -Use `pkgs.writeShellApplication`, provide all necessary `runtimeInputs` and move the script into its own file inside the /scripts/ dir. + +Use `pkgs.writeShellApplication`, provide all necessary `runtimeInputs` and move the script into its own file inside the `/scripts/` dir. # Workflow + - Instead of searching the web for NixOS or home-manager options, use `man configuration.nix` for NixOS and `man home-configuration.nix` for the home-manager manual locally. diff --git a/README.md b/README.md index 12a6105a..592f6488 100644 --- a/README.md +++ b/README.md @@ -63,11 +63,18 @@ But here's a rough guide: ![nix-valley-of-doom](assets/nix-valley-of-despair.png) -## Notes +## Architecture + +This configuration follows the [dendritic pattern](doc/DENDRITIC.md) using flake-parts. + +## Upgrades +- [Upgrade Checklist](doc/upgrades/Checklist.md) - [NixOS 24.11 Upgrade Adventures](doc/upgrades/2411/NixOS-24.11.md) - [NixOS 24.05 Upgrade Adventures](doc/upgrades/2405/NixOS-24.05.md) -- [NixOS Upgrade Checklist](doc/upgrades/Checklist.md) + +## Notes + - [Moving an Existing Installation to a new Disk](doc/MOVING.md) ## Acknowledgements diff --git a/configuration.nix b/configuration.nix deleted file mode 100644 index a46c2d19..00000000 --- a/configuration.nix +++ /dev/null @@ -1,7 +0,0 @@ -{...}: { - imports = [ - ./nix - ./specialisations/light - ./system - ]; -} diff --git a/doc/DENDRITIC.md b/doc/DENDRITIC.md new file mode 100644 index 00000000..ae5a95b7 --- /dev/null +++ b/doc/DENDRITIC.md @@ -0,0 +1,250 @@ +# Dendritic Pattern Architecture + +This document describes the architecture of this NixOS configuration, which follows the [dendritic pattern](https://github.com/mightyiam/dendritic). + +## Core Principles + +1. **Aspect-oriented**: Each file configures a single feature across all relevant configuration classes (NixOS, home-manager) +2. **Top-level modules**: All files are flake-parts modules imported into a unified evaluation +3. **Co-location**: Related NixOS and home-manager config live together in one file +4. **Automatic imports**: Files in `parts/` are auto-imported; adding a feature = creating a file +5. **No specialArgs pass-thru**: Host-specific values live in top-level config options, accessible to all modules + +## Directory Structure + +``` +nixos-config/ +├── flake.nix # Minimal: inputs + import-tree ./parts +├── flake.lock +│ +├── parts/ # All flake-parts modules (auto-imported) +│ ├── modules.nix # Imports flake-parts.flakeModules.modules +│ ├── hosts.nix # Host definitions, dendrix options, and feature composition +│ │ +│ ├── _hosts/ # Host-specific config (underscore excludes from import-tree) +│ │ ├── flexbox/ +│ │ │ ├── default.nix # Host-specific NixOS settings +│ │ │ └── hardware-scan.nix +│ │ └── numenor/ +│ │ ├── default.nix +│ │ └── hardware-scan.nix +│ │ +│ ├── features/ # Feature modules +│ │ ├── core/ # Essential system features +│ │ │ ├── boot.nix +│ │ │ ├── networking.nix +│ │ │ ├── users.nix +│ │ │ ├── nix-settings.nix +│ │ │ └── locale.nix +│ │ │ +│ │ ├── hardware/ # Hardware-specific features +│ │ │ ├── nvidia.nix # Driver + env vars + home config +│ │ │ ├── amd.nix +│ │ │ ├── audio.nix # PipeWire + home audio tools +│ │ │ └── bluetooth.nix +│ │ │ +│ │ ├── desktop/ # Desktop environment +│ │ │ ├── niri.nix # Compositor + keybindings + scripts +│ │ │ ├── waybar.nix +│ │ │ ├── dunst.nix +│ │ │ ├── fuzzel.nix +│ │ │ └── theming.nix # Stylix + specialisations +│ │ │ +│ │ ├── dev/ # Development tools +│ │ │ ├── neovim/ # Complex features can be directories +│ │ │ │ ├── default.nix +│ │ │ │ ├── lsp.nix +│ │ │ │ └── plugins/ +│ │ │ ├── git.nix +│ │ │ ├── jujutsu.nix +│ │ │ └── claude-code.nix +│ │ │ +│ │ ├── apps/ # Applications +│ │ │ ├── firefox.nix +│ │ │ ├── alacritty.nix +│ │ │ ├── fish.nix +│ │ │ └── ... +│ │ │ +│ │ ├── services/ # System services +│ │ │ ├── docker.nix +│ │ │ ├── syncthing.nix +│ │ │ └── restic.nix +│ │ │ +│ │ └── security/ # Security features +│ │ ├── sops.nix +│ │ ├── gpg.nix +│ │ └── keyring.nix +│ +└── doc/ + └── DENDRITIC.md +``` + +## Anatomy of a Feature File + +A feature file configures all aspects of a single feature: + +```nix +# parts/features/apps/alacritty.nix +{ ... }: +{ + flake.modules.nixos.alacritty = { pkgs, ... }: { + fonts.packages = [ pkgs.fira-code ]; + }; + + flake.modules.homeManager.alacritty = { pkgs, lib, osConfig, ... }: { + programs.alacritty = { + enable = true; + settings = { + font.normal.family = "FiraCode Nerd Font"; + }; + }; + + # Feature owns its persistence paths + # Use osConfig to access NixOS-level dendrix options from home-manager + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ ".config/alacritty" ]; + }; + }; +} +``` + +## Host-Specific Values via NixOS Options + +Instead of threading `specialArgs` through module evaluations, host-specific values are defined as NixOS options (`config.dendrix.*`). These are defined in `parts/hosts.nix` and set for each host: + +```nix +# parts/hosts.nix (excerpt) +{config, lib, inputs, ...}: let + # NixOS module that defines dendrix options + dendrixOptionsModule = {lib, ...}: { + options.dendrix = { + hostname = lib.mkOption { type = lib.types.str; }; + isLaptop = lib.mkOption { type = lib.types.bool; default = false; }; + isImpermanent = lib.mkOption { type = lib.types.bool; default = false; }; + hasNvidia = lib.mkOption { type = lib.types.bool; default = false; }; + hasAmd = lib.mkOption { type = lib.types.bool; default = false; }; + stateVersion = lib.mkOption { type = lib.types.str; }; + homeStateVersion = lib.mkOption { type = lib.types.str; }; + }; + }; + + mkHost = { hostname, hostModule, dendrixConfig, ... }: + nixpkgs.lib.nixosSystem { + modules = [ + dendrixOptionsModule + { dendrix = dendrixConfig; } # Set values for this host + # ... other modules + ]; + }; +in { + flake.nixosConfigurations = { + flexbox = mkHost { + hostname = "flexbox"; + hostModule = ./_hosts/flexbox; + dendrixConfig = { + hostname = "flexbox"; + isLaptop = true; + hasNvidia = true; + # ... + }; + }; + }; +} +``` + +Feature modules access these values from NixOS config: + +```nix +# parts/features/hardware/nvidia.nix - NixOS modules use config.dendrix.* +{ ... }: +{ + flake.modules.nixos.nvidia = { config, lib, ... }: { + hardware.nvidia.enable = lib.mkIf config.dendrix.hasNvidia true; + }; + + # Home-manager modules use osConfig.dendrix.* + flake.modules.homeManager.nvidia = { osConfig, lib, ... }: { + home.sessionVariables = lib.mkIf osConfig.dendrix.hasNvidia { + __GLX_VENDOR_LIBRARY_NAME = "nvidia"; + }; + }; +} +``` + +## Host Definition + +Hosts are defined in `parts/hosts.nix` using a `mkHost` helper that composes all dendritic modules: + +```nix +{ config, lib, inputs, ... }: let + # Collect all dendritic modules + nixosModules = builtins.attrValues config.flake.modules.nixos; + homeManagerModules = builtins.attrValues config.flake.modules.homeManager; + + mkHost = { hostname, hostModule, dendrixConfig, ... }: + inputs.nixpkgs.lib.nixosSystem { + modules = + commonNixosModules # External modules (home-manager, stylix, etc.) + ++ [ dendrixOptionsModule ] + ++ nixosModules # All dendritic NixOS modules + ++ [ + hostModule # Host-specific hardware and settings + { dendrix = dendrixConfig; } + { + home-manager.sharedModules = homeManagerModules; + } + ]; + }; +in { + flake.nixosConfigurations = { + flexbox = mkHost { + hostname = "flexbox"; + hostModule = ./_hosts/flexbox; + dendrixConfig = { hostname = "flexbox"; isLaptop = true; hasNvidia = true; /* ... */ }; + }; + numenor = mkHost { + hostname = "numenor"; + hostModule = ./_hosts/numenor; + dendrixConfig = { hostname = "numenor"; isImpermanent = true; hasAmd = true; /* ... */ }; + }; + }; +} +``` + +All dendritic modules are included in all hosts. Features use `config.dendrix.*` to conditionally enable host-specific behavior. + +## Key Concepts + +### flake-parts + +[flake-parts](https://flake.parts) provides the module system for flake outputs. The `flake.modules..` options use `deferredModule` types, allowing modules to be defined once and composed into multiple host configurations. + +### Feature Ownership + +Each feature owns all its concerns: +- System-level configuration (`flake.modules.nixos.*`) +- User-level configuration (`flake.modules.homeManager.*`) +- Persistence paths (declared within the home-manager module) +- Scripts (co-located in the feature file or a sibling `scripts/` directory) + +### Specialisations + +Theme switching is handled in `parts/features/desktop/theming.nix`: + +```nix +flake.modules.nixos.theming = { ... }: { + stylix.base16Scheme = "gruvbox-dark-hard.yaml"; + + specialisation.light.configuration = { + stylix.base16Scheme = "catppuccin-latte.yaml"; + stylix.polarity = "light"; + }; +}; +``` + +## References + +- [Dendritic pattern introduction (video)](https://youtu.be/-TRbzkw6Hjs) +- [mightyiam/dendritic](https://github.com/mightyiam/dendritic) +- [flake.parts documentation](https://flake.parts) +- [Doc-Steve/dendritic-design-with-flake-parts](https://github.com/Doc-Steve/dendritic-design-with-flake-parts) diff --git a/flake.lock b/flake.lock index 437f7bf0..ed891036 100644 --- a/flake.lock +++ b/flake.lock @@ -179,6 +179,24 @@ } }, "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1768135262, + "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_3": { "inputs": { "nixpkgs-lib": [ "nur", @@ -199,7 +217,25 @@ "type": "github" } }, - "flake-parts_3": { + "flake-parts_4": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib_2" + }, + "locked": { + "lastModified": 1768135262, + "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_5": { "inputs": { "nixpkgs-lib": [ "stylix", @@ -381,6 +417,36 @@ "type": "github" } }, + "import-tree": { + "locked": { + "lastModified": 1763762820, + "narHash": "sha256-ZvYKbFib3AEwiNMLsejb/CWs/OL/srFQ8AogkebEPF0=", + "owner": "vic", + "repo": "import-tree", + "rev": "3c23749d8013ec6daa1d7255057590e9ca726646", + "type": "github" + }, + "original": { + "owner": "vic", + "repo": "import-tree", + "type": "github" + } + }, + "import-tree_2": { + "locked": { + "lastModified": 1763762820, + "narHash": "sha256-ZvYKbFib3AEwiNMLsejb/CWs/OL/srFQ8AogkebEPF0=", + "owner": "vic", + "repo": "import-tree", + "rev": "3c23749d8013ec6daa1d7255057590e9ca726646", + "type": "github" + }, + "original": { + "owner": "vic", + "repo": "import-tree", + "type": "github" + } + }, "niri": { "inputs": { "niri-stable": "niri-stable", @@ -540,6 +606,36 @@ "type": "github" } }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1765674936, + "narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs-lib_2": { + "locked": { + "lastModified": 1765674936, + "narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, "nixpkgs-regression": { "locked": { "lastModified": 1643052045, @@ -684,7 +780,7 @@ }, "nur": { "inputs": { - "flake-parts": "flake-parts_2", + "flake-parts": "flake-parts_3", "nixpkgs": "nixpkgs_6" }, "locked": { @@ -747,8 +843,10 @@ "root": { "inputs": { "determinate": "determinate", + "flake-parts": "flake-parts_2", "home-manager": "home-manager", "impermanence": "impermanence", + "import-tree": "import-tree", "niri": "niri", "nix-index-database": "nix-index-database", "nixos-hardware": "nixos-hardware", @@ -757,20 +855,21 @@ "nur": "nur", "rmob": "rmob", "secrets": "secrets", - "sops-nix": "sops-nix_2", + "sops-nix": "sops-nix", "stylix": "stylix", "zen-browser": "zen-browser" } }, "secrets": { "inputs": { + "flake-parts": "flake-parts_4", + "import-tree": "import-tree_2", "nixpkgs": [ "nixpkgs" - ], - "sops-nix": "sops-nix" + ] }, "locked": { - "narHash": "sha256-x3Mp8iZo/o5Xdz1s3og0cUAslOACoUqJolAjKk+H+ZE=", + "narHash": "sha256-4h200ww4RVC/wAq80j8Jq5pC7CgPUFPg5WvfV7U3COk=", "path": "/home/farlion/code/nixos-secrets", "type": "path" }, @@ -780,27 +879,6 @@ } }, "sops-nix": { - "inputs": { - "nixpkgs": [ - "secrets", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1764021963, - "narHash": "sha256-1m84V2ROwNEbqeS9t37/mkry23GBhfMt8qb6aHHmjuc=", - "owner": "Mic92", - "repo": "sops-nix", - "rev": "c482a1c1bbe030be6688ed7dc84f7213f304f1ec", - "type": "github" - }, - "original": { - "owner": "Mic92", - "repo": "sops-nix", - "type": "github" - } - }, - "sops-nix_2": { "inputs": { "nixpkgs": [ "nixpkgs" @@ -827,7 +905,7 @@ "base16-helix": "base16-helix", "base16-vim": "base16-vim", "firefox-gnome-theme": "firefox-gnome-theme", - "flake-parts": "flake-parts_3", + "flake-parts": "flake-parts_5", "gnome-shell": "gnome-shell", "nixpkgs": "nixpkgs_8", "nur": "nur_2", diff --git a/flake.nix b/flake.nix index 87774165..7872a30b 100644 --- a/flake.nix +++ b/flake.nix @@ -3,11 +3,13 @@ inputs = { determinate.url = "https://flakehub.com/f/DeterminateSystems/determinate/*"; + flake-parts.url = "github:hercules-ci/flake-parts"; home-manager = { url = "github:nix-community/home-manager/release-25.11"; inputs.nixpkgs.follows = "nixpkgs"; }; impermanence.url = "github:nix-community/impermanence"; + import-tree.url = "github:vic/import-tree"; niri = { url = "github:sodiboo/niri-flake"; }; @@ -35,149 +37,12 @@ }; }; - outputs = { - self, - determinate, - nixpkgs, - nixos-unstable, - home-manager, - impermanence, - niri, - nur, - secrets, - sops-nix, - stylix, - ... - } @ inputs: let - commonModules = [ - { - nixpkgs.overlays = [ - (_: _: overlays) - ]; - } - determinate.nixosModules.default - nixpkgs.nixosModules.notDetected - ./configuration.nix - nur.modules.nixos.default - impermanence.nixosModules.impermanence - sops-nix.nixosModules.sops - stylix.nixosModules.stylix - home-manager.nixosModules.home-manager - niri.nixosModules.niri - ]; - commonHomeManagerSettings = { - useGlobalPkgs = true; - useUserPackages = true; - backupFileExtension = "home-manager-backup"; - users.farlion = import ./home; - }; - overlays = { - unstable = import nixos-unstable { - system = "x86_64-linux"; - config.allowUnfree = true; - }; - }; - - mkSystem = { - hostname, - isImpermanent, - isLaptop, - isAmd, - isNvidia, - extraModules ? [], - }: let - machineArgs = { - inherit inputs secrets isImpermanent isLaptop; - }; - in - nixpkgs.lib.nixosSystem { - specialArgs = machineArgs; - modules = - commonModules - ++ [ - ./machines/${hostname}/hardware-scan.nix - ./machines/${hostname}/system.nix - { - home-manager = - commonHomeManagerSettings - // { - extraSpecialArgs = - machineArgs - // { - inherit isAmd isNvidia; - }; - }; - } - ] - ++ extraModules; - }; - in { - nixosConfigurations.flexbox = mkSystem { - hostname = "flexbox"; - isImpermanent = false; - isLaptop = true; - isAmd = false; - isNvidia = true; - extraModules = [./system/nvidia]; - }; - - nixosConfigurations.numenor = mkSystem { - hostname = "numenor"; - isImpermanent = true; - isLaptop = false; - isAmd = true; - isNvidia = false; - extraModules = [./system/amd ./system/btrfs]; - }; + outputs = inputs: + inputs.flake-parts.lib.mkFlake {inherit inputs;} ({...}: { + systems = ["x86_64-linux"]; - # Home-manager standalone configuration for `home-manager news` CLI - # Uses actual config with all host-specific features enabled for maximum news coverage - # Actual home-manager is managed via NixOS module (see nixosConfigurations above) - homeConfigurations.farlion = home-manager.lib.homeManagerConfiguration { - pkgs = nixpkgs.legacyPackages.x86_64-linux; - extraSpecialArgs = { - inherit inputs secrets; - # Enable all host-specific features to get maximum applicable news - isImpermanent = true; - isLaptop = true; - isAmd = true; - isNvidia = true; - # Mock osConfig for standalone mode - osConfig = { - networking.hostName = "standalone-news-config"; - specialisation = {}; - }; - }; - modules = [ - { - nixpkgs.overlays = [(_: _: overlays)]; - } - # Import impermanence home-manager module directly (the flake output is deprecated) - "${impermanence}/home-manager.nix" - nur.modules.homeManager.default - stylix.homeManagerModules.stylix - niri.homeModules.niri - { - home = { - username = "farlion"; - homeDirectory = "/home/farlion"; - }; - assertions = nixpkgs.lib.mkForce []; - } - (import ./home) + imports = [ + (inputs.import-tree ./parts) ]; - }; - - # Expose profiling helper as a package and an app - # Call with `nix run .#nh-eval-profile -- ` - packages.x86_64-linux.nh-eval-profile = let - pkgsFor = nixpkgs.legacyPackages.x86_64-linux; - in - pkgsFor.callPackage ./system/scripts/nh-eval-profile.nix {}; - - apps.x86_64-linux.nh-eval-profile = { - type = "app"; - program = "${self.packages.x86_64-linux.nh-eval-profile}/bin/nh-eval-profile"; - }; - }; + }); } diff --git a/home/aichat/default.nix b/home/aichat/default.nix deleted file mode 100644 index c0f62200..00000000 --- a/home/aichat/default.nix +++ /dev/null @@ -1,49 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/aichat" - ]; - }; - - programs.aichat = { - enable = true; - package = pkgs.unstable.aichat; - settings = { - model = "openai:gpt-5.1-chat-latest"; - keybindings = "vi"; - save_session = true; - compress_threshold = 0; - clients = [ - {type = "openai";} - {type = "deepseek";} - { - type = "gemini"; - patch.chat_completions.".*".body.safetySettings = [ - { - category = "HARM_CATEGORY_HARASSMENT"; - threshold = "BLOCK_NONE"; - } - { - category = "HARM_CATEGORY_HATE_SPEECH"; - threshold = "BLOCK_NONE"; - } - { - category = "HARM_CATEGORY_SEXUALLY_EXPLICIT"; - threshold = "BLOCK_NONE"; - } - { - category = "HARM_CATEGORY_DANGEROUS_CONTENT"; - threshold = "BLOCK_NONE"; - } - ]; - } - {type = "claude";} - ]; - }; - }; -} diff --git a/home/aider/default.nix b/home/aider/default.nix deleted file mode 100644 index b31b91a8..00000000 --- a/home/aider/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - programs.aider-chat = { - enable = true; - }; -} diff --git a/home/alacritty/default.nix b/home/alacritty/default.nix deleted file mode 100644 index 7646a4ef..00000000 --- a/home/alacritty/default.nix +++ /dev/null @@ -1,69 +0,0 @@ -{...}: { - programs.alacritty = { - enable = true; - - settings = { - cursor = { - vi_mode_style = { - shape = "Beam"; - blinking = "Always"; - }; - }; - - keyboard.bindings = [ - { - key = "Return"; - mods = "Control|Super"; - action = "SpawnNewInstance"; - } - { - key = "Escape"; - mods = "Alt"; - action = "ToggleViMode"; - } - { - key = "Semicolon"; - mode = "Vi|~Search"; - action = "Right"; - } - { - key = "L"; - mode = "Vi|~Search"; - action = "Up"; - } - { - key = "K"; - mode = "Vi|~Search"; - action = "Down"; - } - { - key = "J"; - mode = "Vi|~Search"; - action = "Left"; - } - { - key = 53; - mode = "Vi|~Search"; - mods = "Shift"; - action = "SearchBackward"; - } - ]; - - env = { - # Better color support in some apps - TERM = "xterm-256color"; - }; - - scrolling = { - history = 100000; - }; - - window = { - padding = { - x = 5; - y = 4; - }; - }; - }; - }; -} diff --git a/home/aliases/default.nix b/home/aliases/default.nix deleted file mode 100644 index e887ac3a..00000000 --- a/home/aliases/default.nix +++ /dev/null @@ -1,59 +0,0 @@ -{...}: { - home.shellAliases = { - ".." = "cd .."; - "..." = "cd ../.."; - "...." = "cd ../../.."; - "....." = "cd ../../../.."; - - as = "aichat --model openai:gpt-4o-mini-search-preview"; - ae = "aichat -e"; - - caffeinate = "systemctl --user stop swayidle"; - decaffeinate = "systemctl --user start swayidle"; - - c = "wl-copy"; - - cdn = "cd ~/nixos-config"; - cdc = "cd ~/code"; - - dira = "direnv allow"; - dird = "direnv deny"; - dirr = "direnv reload"; - dr = "direnv reload"; - - ghco = "gh pr checkout"; - ghpa = "gh pr review --approve"; - ghmr = "gh pr merge -r"; - - isdu = "isd --startup_mode=user"; - - k9s-kind = "k9s --context kind-kind"; - - kc = "kubectl"; - kc-kind = "kubectl --context kind-kind"; - - lh = "/run/current-system/sw/bin/ls -ah"; - - myip = "dig @resolver1.opendns.com ANY myip.opendns.com +short"; - - n = "nh os switch"; - - nl = "sudo nix-env --list-generations --profile /nix/var/nix/profiles/system"; - ngc = "sudo nix-env --delete-generations 30d --profile /nix/var/nix/profiles/system"; - - nsn = "nh search"; - - paste = "wl-paste"; - - ra = "systemctl restart --user pipewire"; - - rm = "trash-put"; - - stern-kind = "stern --context kind-kind"; - - tailup = "sudo tailscale up --accept-routes --accept-dns=false"; - taildown = "sudo tailscale down"; - - x = "exit"; - }; -} diff --git a/home/ansible/default.nix b/home/ansible/default.nix deleted file mode 100644 index 5ac99a7a..00000000 --- a/home/ansible/default.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ - lib, - isImpermanent, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".ansible" - ]; - }; -} diff --git a/home/asciinema/default.nix b/home/asciinema/default.nix deleted file mode 100644 index 8d992fc7..00000000 --- a/home/asciinema/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - programs.asciinema = { - enable = true; - }; -} diff --git a/home/aws/default.nix b/home/aws/default.nix deleted file mode 100644 index 91871f8f..00000000 --- a/home/aws/default.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ - isImpermanent, - lib, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".aws" - ]; - }; - - home.packages = [pkgs.awscli2]; -} diff --git a/home/bash/default.nix b/home/bash/default.nix deleted file mode 100644 index da8f5abe..00000000 --- a/home/bash/default.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - files = [ - ".bash_history" - ]; - }; - - programs.bash = { - enable = true; - package = pkgs.bashInteractive; - initExtra = '' - # Ctrl-x: Copy current command line to clipboard - copy-command-line() { - printf '%s' "$READLINE_LINE" | ${pkgs.wl-clipboard}/bin/wl-copy - } - bind -x '"\C-x": copy-command-line' - ''; - }; -} diff --git a/home/bitwarden/default.nix b/home/bitwarden/default.nix deleted file mode 100644 index da9f8ccc..00000000 --- a/home/bitwarden/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/Bitwarden" - ]; - }; - - home.packages = [ - pkgs.bitwarden-desktop - pkgs.bitwarden-cli - ]; -} diff --git a/home/bluetuith/default.nix b/home/bluetuith/default.nix deleted file mode 100644 index 306ceccd..00000000 --- a/home/bluetuith/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - programs.bluetuith = { - enable = true; - }; -} diff --git a/home/brave-browser/default.nix b/home/brave-browser/default.nix deleted file mode 100644 index b3da9fc3..00000000 --- a/home/brave-browser/default.nix +++ /dev/null @@ -1,52 +0,0 @@ -{ - lib, - isImpermanent, - isNvidia, - pkgs, - ... -}: let - # Try to focus an existing Brave window on link open so the workspace comes to the foreground - braveNiriOpen = pkgs.writeShellApplication { - name = "brave-niri-open"; - runtimeInputs = [pkgs.niri pkgs.jq pkgs.coreutils pkgs.brave]; - text = '' - #!/usr/bin/env bash - set -euo pipefail - brave_id=$(niri msg --json windows | jq -r '.[] | select(.app_id == "brave-browser") | .id' | head -n1 || true) - if [ -n "''${brave_id:-}" ]; then - niri msg action focus-window --id "$brave_id" >/dev/null 2>&1 || true - fi - exec ${pkgs.brave}/bin/brave ${ - if isNvidia - then "--enable-features=VaapiVideoDecoder,VaapiVideoEncoder --password-store=seahorse" - else "--password-store=seahorse" - } "$@" - ''; - }; -in { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/BraveSoftware" - ".cache/BraveSoftware" - ]; - }; - - home.packages = [ - pkgs.brave - ]; - - xdg.desktopEntries = { - brave-browser = { - exec = "${braveNiriOpen}/bin/brave-niri-open %U"; - name = "Brave Browser"; - comment = "Access the Internet"; - genericName = "Web Browser"; - categories = ["Network" "WebBrowser"]; - icon = "brave-browser"; - mimeType = ["application/pdf" "application/rdf+xml" "application/rss+xml" "application/xhtml+xml" "application/xhtml_xml" "application/xml" "image/gif" "image/jpeg" "image/png" "image/webp" "text/html" "text/xml" "x-scheme-handler/http" "x-scheme-handler/https" "x-scheme-handler/ipfs" "x-scheme-handler/ipns"]; - startupNotify = true; - terminal = false; - type = "Application"; - }; - }; -} diff --git a/home/broot/default.nix b/home/broot/default.nix deleted file mode 100644 index 1be2bd69..00000000 --- a/home/broot/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - programs.broot = { - enable = true; - }; -} diff --git a/home/btop/default.nix b/home/btop/default.nix deleted file mode 100644 index 48572568..00000000 --- a/home/btop/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - programs.btop = { - enable = true; - }; -} diff --git a/home/calibre/default.nix b/home/calibre/default.nix deleted file mode 100644 index 14f8767f..00000000 --- a/home/calibre/default.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - "Calibre Library" - ".config/calibre" - ".cache/calibre" - ]; - }; - - home.packages = [ - pkgs.calibre - ]; -} diff --git a/home/claude-code/default.nix b/home/claude-code/default.nix deleted file mode 100644 index 5834eee4..00000000 --- a/home/claude-code/default.nix +++ /dev/null @@ -1,52 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".claude" # Claude Code global settings, agents, and credentials - ".cache/claude-cli-nodejs" # Claude Code cache - ]; - files = [ - ".claude.json" # Claude Code runtime state (credentials, project settings, etc.) - ]; - }; - - programs.claude-code = { - enable = true; - package = pkgs.unstable.claude-code; - - memory.source = ./CLAUDE.md; - - settings = { - permissions = { - allow = [ - "Bash(jj log:*)" - "Bash(jj diff:*)" - "Bash(jj status)" - "Grep" - "WebFetch(domain:github.com)" - "WebSearch" - ]; - deny = [ - "Read(./.env)" - "Read(./.env.*)" - "Read(./secrets/secrets.json)" - "Read(./config/credentials.json)" - ]; - }; - alwaysThinkingEnabled = true; - }; - }; - - # See https://dylancastillo.co/til/fix-claude-code-shift-enter-alacritty.html - programs.alacritty.settings.keyboard.bindings = [ - { - key = "Return"; - mods = "Shift"; - chars = "\n"; - } - ]; -} diff --git a/home/cliphist/default.nix b/home/cliphist/default.nix deleted file mode 100644 index 77a4d628..00000000 --- a/home/cliphist/default.nix +++ /dev/null @@ -1,30 +0,0 @@ -{ - isImpermanent, - lib, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".cache/cliphist" - ]; - }; - - services.cliphist = { - enable = true; - }; - - # Fix cliphist systemd service to start after Niri is ready - systemd.user.services.cliphist = { - Install.WantedBy = lib.mkForce ["niri.service"]; - Unit.Requires = ["niri.service"]; - Unit.After = ["niri.service"]; - }; - systemd.user.services.cliphist-images = { - Install.WantedBy = lib.mkForce ["niri.service"]; - Unit.Requires = ["niri.service"]; - Unit.After = ["niri.service"]; - }; - - home.packages = [pkgs.xdg-utils]; # For image copy/pasting -} diff --git a/home/codex/default.nix b/home/codex/default.nix deleted file mode 100644 index 7b0ba8b4..00000000 --- a/home/codex/default.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".codex" - ]; - }; - - home.packages = with pkgs.unstable; [codex]; -} diff --git a/home/cpu-profile-toggler/default.nix b/home/cpu-profile-toggler/default.nix deleted file mode 100644 index 61ed9042..00000000 --- a/home/cpu-profile-toggler/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -# Toggle CPU profiles -{pkgs, ...}: let - cpu-profile-toggler = pkgs.writeShellApplication { - name = "cpu-profile-toggler"; - runtimeInputs = with pkgs; [gnugrep auto-cpufreq linuxKernel.packages.linux_zen.cpupower]; - text = builtins.readFile ./scripts/cpu-profile-toggler.sh; - }; -in { - home.packages = [cpu-profile-toggler]; -} diff --git a/home/dconf/default.nix b/home/dconf/default.nix deleted file mode 100644 index 37e6f149..00000000 --- a/home/dconf/default.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ - lib, - isImpermanent, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/dconf" - ]; - }; -} diff --git a/home/ddc-backlight/default.nix b/home/ddc-backlight/default.nix deleted file mode 100644 index b06c071b..00000000 --- a/home/ddc-backlight/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -# Toggle CPU profiles -{pkgs, ...}: let - ddc-backlight = pkgs.writeShellApplication { - name = "ddc-backlight"; - runtimeInputs = [pkgs.ddcutil pkgs.coreutils pkgs.util-linux]; - text = builtins.readFile ./scripts/ddc-backlight.sh; - }; -in { - home.packages = [ddc-backlight pkgs.ddcutil]; -} diff --git a/home/default.nix b/home/default.nix deleted file mode 100644 index 6666cf02..00000000 --- a/home/default.nix +++ /dev/null @@ -1,266 +0,0 @@ -{ - config, - inputs, - isImpermanent, - isNvidia, - lib, - osConfig, - pkgs, - secrets, - ... -}: let - impermanenceImports = lib.optionals isImpermanent [ - ./impermanence - ]; - - imports = - [ - inputs.sops-nix.homeManagerModules.sops - ./aichat - ./aider - ./alacritty - ./aliases - ./ansible - ./asciinema - ./aws - ./bash - ./bitwarden - ./bluetuith # Bluetooth TUI - ./brave-browser - ./broot - ./btop - ./calibre # Ebook reader - ./claude-code - ./cliphist - ./codex # OpenAI Codex - ./cpu-profile-toggler - ./devenv # devenv.sh - ./direnv - ./discord - ./dunst - ./easyeffects # GUI for Pipewire effects - ./email - ./firefox - ./fish - ./fix-flexbox-mike # Fix ALSA not detecting microphone on XPS 9700 - ./fuzzel - ./fzf - ./galaxy-buds-client - ./gimp - ./git - ./git-worktree-switcher # Provides wt - ./gnome-connections # RDP/VNC Client for Wayland - ./gtk-qt - ./hoppscotch # OSS Postman - ./hwatch # Modern watch alternative - ./isd # Interactive Systemd TUI in Python - ./jqp # TUI Playground for interacting with jq - ./jujutsu - ./k9s - ./kanshi # Wayland autorandr - ./kind-with-local-registry - ./kubernetes-tools - ./less - ./lf - ./libation # Audible liberator - ./libreoffice - ./lnav # Log File Navigator - ./mic-levels-maintainer - ./mpv # Media Player - ./mullvad-browser - ./nautilus # Gnome File Manager - ./neovim - ./networkmanager-dmenu - ./nh # https://github.com/nix-community/nh - ./niri - ./nix-index - ./nix-inspect # TUI for inspecting final NixOS config (and other nix exprs) - ./nushell - ./obs # OBS Studio - ./obsidian - ./onboard # Virtual Keyboard for layout visualization (no good Wayland options work) - ./pavucontrol # Pulse Audio Volume Control GUI - ./pgcli # Actually usable PostgreSQL CLI - ./pomodoro-gtk - ./portfolio-performance - ./psql # Postgresql Client with nicer config - ./pulsemixer # TUI (curses) mixer for pulseaudio, still useful under pipewire - ./qalculate # Calculator - ./ripgrep - ./ripgrep-all # Like rg, but also search in Office documents, PDFs etc...; rga-fzf is AMAZING! - ./rofimoji - ./showmethekey # screenkey for Wayland, show key presses - ./satty # Screenshot Annotation tool written in Rust - ./signal - ./solaar # Linux devices manager for the Logitech Unifying Receiver - ./sound-switcher - ./ssh - ./starship - ./stylix - ./syncthing - ./systemd-errors-and-warnings-counter - ./tealdeer - ./telegram - ./television # Fuzzy-finder in Rust with nixpkgs integration - ./tomat - ./trash-cli - ./tray-tui # TUI for tray icons - ./udiskie - ./urxvt - ./variety # Wallpaper Switcher/Randomizer with Quotes - ./virtual-cable # Virtual inputs/outputs via Pipewire (for OBS and beyond) - ./vlc - ./waybar - ./witr # Why is this running? - ./wlsunset # Day/night gamma adjustments for Wayland - ./wluma # Automatic screen brightness adjustment - ./xdg - ./ytmdesktop # Youtube Music Desktop (unofficial) - ./yubico # Yubikeys - ./zen - ./zoom - ./zoxide - ] - ++ impermanenceImports - ++ numenorImports - ++ [homeManagerSecrets]; - - isFlexbox = osConfig.networking.hostName == "flexbox"; - isNumenor = osConfig.networking.hostName == "numenor"; - - homeManagerSecrets = - if secrets ? homeManagerSecrets - then secrets.homeManagerSecrets {inherit isImpermanent lib pkgs;} - else {}; - - numenorImports = lib.optionals isNumenor [ - ./ddc-backlight - ]; -in { - home = { - # This value determines the Home Manager release that your - # configuration is compatible with. This helps avoid breakage - # when a new Home Manager release introduces backwards - # incompatible changes. - # - # You can update Home Manager without changing this value. See - # the Home Manager release notes for a list of state version - # changes in each release. - stateVersion = - if isFlexbox - then "22.05" - else if isNumenor - then "24.11" - else "24.11"; - - file."nixos-config" = { - source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/code/nixos-config"; - target = "nixos-config"; - }; - - # Symlink flake for `home-manager news` CLI to find homeConfigurations - file.".config/home-manager/flake.nix" = { - source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/code/nixos-config/flake.nix"; - }; - - packages = with pkgs; [ - alejandra # Nix Formatter - ast-grep # Pure Magic - bc # calculator - bind # Provides dig - dconf # Gnome configuration database - difftastic # structural diff difft, see https://github.com/Wilfred/difftastic - dive # Analyze docker images - dmidecode # Hardware info read from Bios - dnstracer - efivar # Tools and Libraries to manipulate EFI variables - fast-cli # Fast.com CLI `fast` - fastfetch # neofetch sucessor, system information tool - fd # Better find, written in Rust - ffmpeg-full - file # CLI program to show the type of a file - find-cursor - fortune - glow # Terminal markdown renderer - gomatrix # The Matrix - google-chrome - gucharmap # Unicode Character Map - hardinfo2 # Hardware/System Info - home-manager # CLI for managing home-manager, needed for `home-manager news` - httpie - iftop # Net top tool, see also nethogs - imagemagick - iotop-c - jq - kind # Kubernetes In Docker - kdePackages.kruler # Screen ruler - lazydocker # kind for vanilla Docker, kind of - libnotify # Provides notify-send - libsecret # `secret-tool` for interacting with gnome-keyring - lm_sensors # Tools for reading hardware sensors - lolcat # Pipe and See - lsof # Tool to list open file - ncdu # Disk Space Usage Visualization - nmap # Port Scanner - nethogs # Net top tool, see also iftop - net-tools # Things like arp, ifconfig, route, netstat etc... - neo-cowsay - nix-tree - oculante # img viewer written in Rust - kdePackages.okular # KDE document viewer - openssl - pdftk # PDF Manipulation Toolkit - pstree # Show the set of running processes as a tree - q-text-as-data # https://github.com/harelba/q - inputs.rmob.defaultPackage.x86_64-linux - screenkey # Screencast tool to display your keys inspired by Screenflick - smartmontools # Tools for monitoring the health of hard drives - s-tui # Processor monitor/stress test - stress # Simple workload generator for POSIX systems. It imposes a configurable amount of CPU, memory, I/O, and disk stress on the system - tcpdump - traceroute - tree - unzip - usbutils # Provides lsusb - wdisplays # arandr for wayland - external display/screen GUI - wf-recorder # Screen recorder for Wayland, useful for quick testing screen stuff - wget - wireguard-tools - whois - wl-clipboard - xournalpp # PDF Annotations, useful for saving Okular annotations as well - yq # Command-line YAML/XML/TOML processor - jq wrapper for YAML, XML, TOML documents - yt-dlp - zip - ]; - - sessionVariables = - { - PATH = "$HOME/bin:$PATH"; - NIXOS_CONFIG = "$HOME/code/nixos-config/"; - GC_INITIAL_HEAP_SIZE = "8G"; # Slightly improve nix eval times - DIRENV_LOG_FORMAT = ""; # Disable verbose direnv output showing env variables changed - NIXOS_OZONE_WL = "1"; # Enable Ozone-Wayland for Electron apps and Chromium - } - // lib.optionalAttrs isNvidia { - LIBVA_DRIVER_NAME = "nvidia"; - }; - }; - - inherit imports; - - programs = { - bat = { - enable = true; - }; - - man = { - enable = true; - generateCaches = false; # Speed up builds - }; - - vscode = { - enable = true; - }; - }; -} diff --git a/home/devenv/default.nix b/home/devenv/default.nix deleted file mode 100644 index 35ffadf6..00000000 --- a/home/devenv/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".local/share/devenv" - ]; - }; - - home.packages = [ - pkgs.devenv - ]; -} diff --git a/home/direnv/default.nix b/home/direnv/default.nix deleted file mode 100644 index 371164b3..00000000 --- a/home/direnv/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - lib, - isImpermanent, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".local/share/direnv" - ]; - }; - - programs.direnv = { - enable = true; - nix-direnv.enable = true; - config.strict_env = true; # Forces all .envrc scripts through set -euo pipefail - }; -} diff --git a/home/discord/default.nix b/home/discord/default.nix deleted file mode 100644 index 1148af25..00000000 --- a/home/discord/default.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ - lib, - isImpermanent, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/discord" - ]; - }; - - programs.discord = { - enable = true; - }; -} diff --git a/home/dunst/default.nix b/home/dunst/default.nix deleted file mode 100644 index 38d89aee..00000000 --- a/home/dunst/default.nix +++ /dev/null @@ -1,18 +0,0 @@ -{pkgs, ...}: { - services.dunst = { - enable = true; - - iconTheme = { - name = "Papirus-Dark"; - package = pkgs.papirus-icon-theme; - }; - - settings = { - global = { - browser = "brave"; - dmenu = "fuzzel --dmenu"; - follow = "mouse"; - }; - }; - }; -} diff --git a/home/easyeffects/default.nix b/home/easyeffects/default.nix deleted file mode 100644 index 727370fa..00000000 --- a/home/easyeffects/default.nix +++ /dev/null @@ -1,24 +0,0 @@ -# GUI for PipeWire effects -{ - isImpermanent, - lib, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/easyeffects" - ]; - }; - - # Preset from https://github.com/JackHack96/EasyEffects-Presets/blob/master/Bass%20Enhancing%20%2B%20Perfect%20EQ.json - # IRS file from the same repo above - home.file.".config/easyeffects/irs/Razor Surround ((48k Z-Edition)) 2.Stereo +20 bass.irs".source = ./presets/irs/razor-surround-48k-z-edition-stereo-plus20-bass.irs; - - services.easyeffects = { - enable = true; - preset = "bass-enhancing-perfect-eq"; - extraPresets = { - "bass-enhancing-perfect-eq" = builtins.fromJSON (builtins.readFile ./presets/output/bass-enhancing-perfect-eq.json); - }; - }; -} diff --git a/home/email/default.nix b/home/email/default.nix deleted file mode 100644 index f360452f..00000000 --- a/home/email/default.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ - isImpermanent, - lib, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".thunderbird" - ".cache/thunderbird" - ]; - }; - - programs.thunderbird = { - enable = true; - profiles = { - "main" = { - isDefault = true; - settings = { - "calendar.alarms.showmissed" = false; - "calendar.alarms.playsound" = false; - "calendar.alarms.show" = false; - }; - }; - }; - }; -} diff --git a/home/firefox/default.nix b/home/firefox/default.nix deleted file mode 100644 index 8d835a67..00000000 --- a/home/firefox/default.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ - isImpermanent, - lib, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".mozilla/firefox" - ".cache/mozilla/firefox" - ]; - }; - - programs.firefox = { - enable = true; - profiles = { - main = { - extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [ - bitwarden - ]; - id = 0; - isDefault = true; - }; - }; - }; -} diff --git a/home/fish/default.nix b/home/fish/default.nix deleted file mode 100644 index 26b7e2db..00000000 --- a/home/fish/default.nix +++ /dev/null @@ -1,143 +0,0 @@ -{ - config, - isImpermanent, - lib, - pkgs, - ... -}: let - functions = { - fish_user_key_bindings = - /* - fish - */ - '' - fish_vi_key_bindings - - # VI mode updates - bind -s --preset --mode default j backward-char - bind -s --preset --mode default \; forward-char - bind -s --preset k down-or-search - bind -s --preset l up-or-search - bind -s --preset --mode visual j backward-char - bind -s --preset --mode visual \; forward-char - bind -s --preset --mode visual l up-line - bind -s --preset --mode visual k down-line - - # Completions - bind -s --mode insert \cw forward-word - # Tab --> accept autosuggestions - bind -s --mode insert \t accept-autosuggestion - # CTRL-S --> original TAB behaviour - bind -s --mode insert \cs complete - - # Bang-Bang bindings, manually added so they have precedence: - bind --mode insert ! __history_previous_command - bind --mode insert '$' __history_previous_command_arguments - ''; - - ## Wrap LF to add ability to quit with Q in current directory - ## - ## Adapted for fish from https://github.com/gokcehan/lf/wiki/Tips#cd-to-current-directory-on-quit - ## - lf = - /* - fish - */ - '' - set -x LF_CD_FILE /var/tmp/.lfcd-$fish_pid - command lf $argv - if test -s "$LF_CD_FILE" - set DIR (realpath (cat "$LF_CD_FILE")) - if test "$DIR" != "$PWD" - cd "$DIR" - end - rm "$LF_CD_FILE" - end - set -e LF_CD_FILE - ''; - - pirate = - /* - fish - */ - '' - set toTranslate $argv - curl -sG \ - --data-urlencode "english=$toTranslate" \ - 'http://pirate.monkeyness.com/cgi-bin/translator.pl?client=monkeyness&version=1.0' -H 'Accept: application/json' -H 'Connection: keep-alive' -H 'Accept-Encoding: gzip, deflate' -H 'Referer: http://pirate.monkeyness.com/online_pirate_translator' -H 'Accept-Language: en-US,en;q=0.9' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36' --compressed --insecure \ - | xq -r .pirateAPI.pirate \ - | curl -sG \ - --data-urlencode "source_text=$toTranslate" \ - 'https://speakpirate.com/' -H 'authority: speakpirate.com' -H 'cache-control: max-age=0' -H 'origin: https://speakpirate.com' -H 'upgrade-insecure-requests: 1' -H 'content-type: application/x-www-form-urlencoded' -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36' -H 'sec-fetch-mode: navigate' -H 'sec-fetch-user: ?1' -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3' -H 'sec-fetch-site: same-origin' -H 'referer: https://speakpirate.com/' -H 'accept-encoding: gzip, deflate, br' -H 'accept-language: en-US,en;q=0.9' -H 'cookie: __utma=133499724.1448464120.1565964854.1565964854.1565964854.1; __utmc=133499724; __utmz=133499724.1565964854.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmt=1; __utmb=133499724.1.10.1565964854' --compressed \ - | pup 'textarea#translated json{}' | jq -r '.[0].text' - ''; - - # Source .env files - # Source: http://lewandowski.io/2016/10/fish-env/ - posix-source = - /* - fish - */ - '' - for i in (cat $argv) - set arr (echo $i | string match -r "([^=]+)=(.*)") - set -gx $arr[2] $arr[3] - end - ''; - - kubectlgetall = - /* - fish - */ - '' - for i in (kubectl api-resources --verbs=list --namespaced -o name | grep -v "events.events.k8s.io" | grep -v "events" | sort | uniq) - echo "Resource:" $i - kubectl -n "$argv[1]" get --ignore-not-found "$i" - end - ''; - }; - - plugins = with pkgs.fishPlugins; [ - { - name = "bang-bang"; - src = bang-bang.src; - } - ]; - - shellInit = '' - ${variables} - ''; - - variables = - /* - fish - */ - '' - set -g fish_key_bindings fish_default_key_bindings - set fish_greeting # disable greeting - ''; -in { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/fish" - ".local/share/fish" # contains some unecessary state, but https://github.com/fish-shell/fish-shell/issues/10730 prevents us from only syncing the history file (.local/share/fish/fish_history) - ]; - }; - - programs.fish = { - enable = true; - interactiveShellInit = shellInit; - inherit functions plugins; - shellAbbrs = - config.home.shellAliases - // { - # Fish/bash-specific aliases that aren't compatible with nushell - # Use bashInteractive to ensure bind command is available - bash = "${config.programs.bash.package}/bin/bash"; - cc = "tee /dev/tty | wl-copy"; - dark-theme = "nh os test --no-specialisation && niri-set-wallpaper"; - light-theme = "nh os test --specialisation light && niri-set-wallpaper"; - pa = "pw-play ~/Music/Own\\ Speech/IckbinArschratte.WAV"; - }; - }; -} diff --git a/home/fix-flexbox-mike/default.nix b/home/fix-flexbox-mike/default.nix deleted file mode 100644 index e63b2269..00000000 --- a/home/fix-flexbox-mike/default.nix +++ /dev/null @@ -1,27 +0,0 @@ -# Fix ALSA not detecting microphone on XPS 9700, see https://github.com/NixOS/nixpkgs/issues/130882#issuecomment-2584286824 -{ - lib, - osConfig, - pkgs, - ... -}: let - isFlexbox = osConfig.networking.hostName == "flexbox"; - xps-9700-mic-fixer = pkgs.writeShellApplication { - name = "xps-9700-mic-fixer"; - runtimeInputs = [pkgs.alsa-utils]; - text = builtins.readFile ./scripts/xps-9700-mic-fixer.sh; - }; -in { - systemd.user.services.fixXPS9700Mike = lib.mkIf isFlexbox { - Unit = { - Description = "Fix ALSA settings for internal mic on Dell XPS 9700"; - }; - Install.WantedBy = ["pipewire.service"]; - Service = { - Environment = "PATH=$PATH:/run/current-system/sw/bin"; - ExecStart = "${xps-9700-mic-fixer}/bin/xps-9700-mic-fixer"; - Type = "oneshot"; - RemainAfterExit = true; - }; - }; -} diff --git a/home/fuzzel/default.nix b/home/fuzzel/default.nix deleted file mode 100644 index a8cf1f48..00000000 --- a/home/fuzzel/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -{pkgs, ...}: { - programs.fuzzel = { - enable = true; - package = pkgs.unstable.fuzzel; - }; -} diff --git a/home/fzf/default.nix b/home/fzf/default.nix deleted file mode 100644 index d2d958dd..00000000 --- a/home/fzf/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -{...}: { - programs.fzf = { - enable = true; - defaultCommand = "rg --files --no-ignore-vcs --hidden"; - }; -} diff --git a/home/galaxy-buds-client/default.nix b/home/galaxy-buds-client/default.nix deleted file mode 100644 index 9398e512..00000000 --- a/home/galaxy-buds-client/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".local/share/GalaxyBudsClient" - ]; - }; - - home.packages = [ - pkgs.galaxy-buds-client - ]; -} diff --git a/home/gimp/default.nix b/home/gimp/default.nix deleted file mode 100644 index e20d984f..00000000 --- a/home/gimp/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/GIMP" - ".cache/gimp" - ]; - }; - - home.packages = [ - pkgs.gimp - ]; -} diff --git a/home/git-worktree-switcher/default.nix b/home/git-worktree-switcher/default.nix deleted file mode 100644 index 113bfb48..00000000 --- a/home/git-worktree-switcher/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - programs.git-worktree-switcher = { - enable = true; - }; -} diff --git a/home/git/default.nix b/home/git/default.nix deleted file mode 100644 index f9decb0c..00000000 --- a/home/git/default.nix +++ /dev/null @@ -1,104 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/glab-cli" - ]; - }; - - home.packages = with pkgs; [ - delta # Syntax highlighter for git - github-cli - glab - ]; - - home.file.".config/gh/config.yml".source = ./github-cli/gh.config.yml; - - programs.difftastic = { - enable = true; - git.enable = true; - }; - - programs.git = { - enable = true; - - includes = [ - { - path = "~/code/dlh/.gitconfig"; - condition = "gitdir:~/code/dlh/"; - } - { - path = "~/code/dlh/plansee/.gitconfig"; - condition = "gitdir:~/code/dlh/plansee/"; - } - ]; - - signing = { - signByDefault = true; - key = "24575DB93F6CEC16"; - }; - - settings = { - alias = { - c = "commit"; - - # Difftastic - dlog = "-c diff.external=difft log --ext-diff"; - dshow = "-c diff.external=difft show --ext-diff"; - ddiff = "-c diff.external=difft diff"; - # `git log` with patches shown with difftastic. - dl = "-c diff.external=difft log -p --ext-diff"; - # Show the most recent commit with difftastic. - ds = "-c diff.external=difft show --ext-diff"; - # `git diff` with difftastic. - dft = "-c diff.external=difft diff"; - - p = "push"; - rim = "rebase -i main"; - rimm = "rebase -i master"; - }; - - core = { - pager = "delta"; - }; - diff = { - colorMoved = "default"; - }; - init = { - defaultBranch = "main"; - }; - interactive = { - diffFilter = "delta --color-only"; - }; - merge = { - conflictstyle = "diff3"; - }; - pull = { - ff = "only"; - rebase = true; - }; - rebase = { - autoStash = true; - autoSquash = true; - }; - # https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/javascript.section.md#git-protocol-error - url = { - "https://github.com" = { - insteadOf = "git://github.com"; - }; - }; - user.email = "4farlion@gmail.com"; - user.name = "workflow"; - }; - - ignores = [".idea" "nohup.out" "mzdata" ".vimspector.json"]; - }; - - programs.mergiraf = { - enable = true; - }; -} diff --git a/home/gnome-connections/default.nix b/home/gnome-connections/default.nix deleted file mode 100644 index 8ee79471..00000000 --- a/home/gnome-connections/default.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/freerdp" # RDP server certificates/keys - ".config/dconf" # GNOME settings database (GSettings/dconf) - ]; - files = [ - ".config/connections.db" # GNOME Connections connection profiles database - ]; - }; - - home.packages = [ - pkgs.gnome-connections - ]; -} diff --git a/home/gtk-qt/default.nix b/home/gtk-qt/default.nix deleted file mode 100644 index f8fe7a0c..00000000 --- a/home/gtk-qt/default.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ - isImpermanent, - lib, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - files = [ - ".config/QtProject.conf" # Stuff like history and lastVisited - ]; - }; - - gtk = { - enable = true; - gtk3 = { - extraConfig = { - gtk-application-prefer-dark-theme = true; - }; - }; - }; - - qt = { - enable = true; - platformTheme.name = "qtct"; - style.name = "kvantum"; - }; - - home.packages = with pkgs; [ - lxappearance # GTK Theme testing + tweaking - libsForQt5.qt5ct # Qt 5 Theme testing + tweaking - qt6Packages.qt6ct # Qt 6 Theme testing + tweaking - ]; - - # https://wiki.archlinux.org/title/HiDPI - home.sessionVariables = { - QT_AUTO_SCREEN_SCALE_FACTOR = "1"; - QT_ENABLE_HIGHDPI_SCALING = "1"; - }; -} diff --git a/home/gtk-qt/qtct.conf b/home/gtk-qt/qtct.conf deleted file mode 100644 index 1db523c7..00000000 --- a/home/gtk-qt/qtct.conf +++ /dev/null @@ -1,8 +0,0 @@ -[Appearance] -icon_theme=Pop -standard_dialogs=gtk3 -style=kvantum-dark - -[Fonts] -fixed="FiraCode Nerd Font Mono,9,-1,5,25,0,0,0,0,0" -general="Fira Code,9,-1,5,50,0,0,0,0,0" diff --git a/home/hoppscotch/default.nix b/home/hoppscotch/default.nix deleted file mode 100644 index 5cf1ed96..00000000 --- a/home/hoppscotch/default.nix +++ /dev/null @@ -1,23 +0,0 @@ -{pkgs, ...}: let - # Wrap Hoppscotch with Wayland-friendly flags - hoppscotch-wrapped = pkgs.symlinkJoin { - name = "hoppscotch-wrapped"; - paths = [pkgs.unstable.hoppscotch]; - buildInputs = [pkgs.makeWrapper]; - postBuild = '' - wrapProgram $out/bin/hoppscotch \ - --set NIXOS_OZONE_WL 1 \ - --add-flags "--use-gl=desktop" \ - --add-flags "--disable-gpu-sandbox" - ''; - }; -in { - home.persistence."/persist".directories = [ - ".local/share/io.hoppscotch.desktop" # Auth tokens, collections, requests, workspaces - ".config/io.hoppscotch.desktop" # App settings, bundles, window state - ]; - - home.packages = [ - hoppscotch-wrapped - ]; -} diff --git a/home/hwatch/default.nix b/home/hwatch/default.nix deleted file mode 100644 index 6b54f257..00000000 --- a/home/hwatch/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{pkgs, ...}: { - home.packages = with pkgs; [ - hwatch - ]; -} diff --git a/home/impermanence/default.nix b/home/impermanence/default.nix deleted file mode 100644 index 035fb4f1..00000000 --- a/home/impermanence/default.nix +++ /dev/null @@ -1,15 +0,0 @@ -# General home-manager impermanence setup -# Note: specifics should live with their respective modules, where possible! -{ - home.persistence."/persist" = { - enable = true; - directories = [ - ".cache/nix" - ".config/helm" # Helm repositories - ".config/nix" # cachix repositories and such - ".local/share/home-manager" # home-manager news read state - ".local/share/nix" # Nix Repl History - ".local/state/home-manager" # home-manager generations and GC roots - ]; - }; -} diff --git a/home/isd/default.nix b/home/isd/default.nix deleted file mode 100644 index 7400cead..00000000 --- a/home/isd/default.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/isd_tui" - ".local/share/isd_tui" - ".cache/isd_tui" - ]; - }; - - home.packages = [ - pkgs.isd - ]; -} diff --git a/home/jqp/default.nix b/home/jqp/default.nix deleted file mode 100644 index 4b368f58..00000000 --- a/home/jqp/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - programs.jqp = { - enable = true; - }; -} diff --git a/home/jujutsu/default.nix b/home/jujutsu/default.nix deleted file mode 100644 index 9fc67c41..00000000 --- a/home/jujutsu/default.nix +++ /dev/null @@ -1,62 +0,0 @@ -{pkgs, ...}: { - programs.difftastic.enable = true; - programs.jjui = { - enable = true; - package = pkgs.unstable.jjui; - }; - programs.jujutsu = { - enable = true; - package = pkgs.unstable.jujutsu; - settings = { - remotes.origin.auto-track-bookmarks = "main"; - ui.diff-formatter = ["difft" "--color=always" "$left" "$right"]; - user = { - email = "4farlion@gmail.com"; - name = "workflow"; - }; - signing = { - backend = "gpg"; - key = "24575DB93F6CEC16"; - behavior = "own"; # sign commits you authored on modify - }; - aliases = { - bt = ["bookmark" "track"]; - c = ["commit"]; - init = ["git" "init" "--colocate"]; - push = [ - "util" - "exec" - "--" - "bash" - "-c" - '' - set -e - - # Check if current commit has both description and changes - has_description=$(jj log -r @ --no-graph --color never -T 'description' | grep -q . && echo "yes" || echo "no") - # Use 'empty' template keyword to check if commit has changes - has_changes=$(jj log -r @ --no-graph --color never -T 'empty' | grep -q "false" && echo "yes" || echo "no") - - if [ "$has_description" = "yes" ] && [ "$has_changes" = "yes" ]; then - echo "Current commit has description and changes, creating new commit..." - jj new - fi - - # Get the bookmark from the parent commit directly - bookmark=$(jj log -r 'ancestors(@) & bookmarks()' -n 1 --no-graph --color never -T 'bookmarks' | sed 's/\*$//' | tr -d ' ') - - if [ -z "$bookmark" ]; then - echo "No bookmark found on parent commit" - exit 1 - fi - - echo "Moving bookmark '$bookmark' to parent commit and pushing..." - jj bookmark set "$bookmark" -r @- - jj git fetch - jj git push --bookmark "$bookmark" --allow-new - '' - ]; - }; - }; - }; -} diff --git a/home/k9s/default.nix b/home/k9s/default.nix deleted file mode 100644 index f88592a5..00000000 --- a/home/k9s/default.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ - pkgs, - lib, - ... -}: { - programs.k9s = { - enable = true; - package = pkgs.unstable.k9s; - - skins = { - gruvbox-dark = ./gruvbox-dark.yaml; - gruvbox-light = ./gruvbox-light.yaml; - }; - - settings = { - k9s = { - ui = { - # Default to gruvbox-dark, override in light specialisation - skin = lib.mkDefault "gruvbox-dark"; - }; - }; - }; - }; -} diff --git a/home/kanshi/default.nix b/home/kanshi/default.nix deleted file mode 100644 index 188ecf69..00000000 --- a/home/kanshi/default.nix +++ /dev/null @@ -1,78 +0,0 @@ -{...}: { - services.kanshi = { - enable = true; - settings = [ - { - output = { - alias = "leftLG27"; - criteria = "HDMI-A-2"; - mode = "3840x2160@60.000Hz"; - position = "0,0"; - scale = 2.0; - transform = "90"; - }; - } - { - output = { - alias = "middleLG34"; - criteria = "DP-1"; - mode = "3840x2160@144.050Hz"; - position = "1080,208"; - scale = 1.5; - transform = "normal"; - }; - } - { - output = { - alias = "rightLG27"; - criteria = "HDMI-A-1"; - mode = "3840x2160@60.000Hz"; - position = "3640,0"; - scale = 2.0; - transform = "90"; - }; - } - { - profile = { - name = "numenor"; - outputs = [ - { - criteria = "$leftLG27"; - status = "enable"; - } - { - criteria = "$middleLG34"; - status = "enable"; - } - { - criteria = "$rightLG27"; - status = "enable"; - } - ]; - }; - } - { - profile = { - name = "numenor-movie"; - outputs = [ - { - criteria = "$leftLG27"; - status = "enable"; - position = "0,272"; - transform = "normal"; - } - { - criteria = "$middleLG34"; - status = "enable"; - position = "1920,132"; - } - { - criteria = "$rightLG27"; - status = "disable"; - } - ]; - }; - } - ]; - }; -} diff --git a/home/kind-with-local-registry/default.nix b/home/kind-with-local-registry/default.nix deleted file mode 100644 index b84555fd..00000000 --- a/home/kind-with-local-registry/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -# Local registry for faster image iteration, i.e. with Skaffold -# To use local registry, prefix images with localhost:5001/ and make sure `docker push` is enabled -# See https://kind.sigs.k8s.io/docs/user/local-registry/ -{pkgs, ...}: let - kind-with-local-registry = pkgs.writers.writeBashBin "kind-with-local-registry" ( - builtins.readFile ./scripts/kind-with-local-registry.sh - ); -in { - home.packages = [kind-with-local-registry]; -} diff --git a/home/kubernetes-tools/default.nix b/home/kubernetes-tools/default.nix deleted file mode 100644 index 45e0c560..00000000 --- a/home/kubernetes-tools/default.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".kube" - ]; - }; - - home.packages = [ - pkgs.kubectl - pkgs.kubectx # Kubectl Context switcher - pkgs.stern # Multi pod and container log tailing for Kubernetes - ]; -} diff --git a/home/less/default.nix b/home/less/default.nix deleted file mode 100644 index 23ce9bfb..00000000 --- a/home/less/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{...}: { - programs.less = { - enable = true; - config = '' - k forw-line - l back-line - ''; - }; -} diff --git a/home/lf/default.nix b/home/lf/default.nix deleted file mode 100644 index 7b95549c..00000000 --- a/home/lf/default.nix +++ /dev/null @@ -1,418 +0,0 @@ -{ - isImpermanent, - lib, - pkgs, - ... -}: let - # Provides the ability to download a file by dropping it into a window - dlfile = pkgs.writers.writeBashBin "dlfile" '' - url=$(dragon -t -x) - - if [ -n "$url" ]; then - printf "File Name: " - name="" - while [ -z $name ] || [ -e $name ] - do - read -r name - if [ -e "$name" ]; then - printf "File already exists, overwrite (y|n): " - read -r ans - - if [ "$ans" = "y" ]; then - break - else - printf "File Name: " - fi - fi - done - - # Download the file with curl - [ -n "$name" ] && curl -o "$name" "$url" || exit 1 - else - exit 1 - fi - ''; -in { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".local/share/lf" - ]; - }; - - home.packages = with pkgs; [ - chafa # Images to terminal pixels, used by pistol - dlfile # Provides the ability to download a file by dropping it into a window - imagemagick # Image conversion for clipboard operations - pistol # Image previewer - ripdrag - ]; - - home.file = { - ".config/pistol/pistol.conf".source = ./pistol/pistol.conf; - }; - - programs.lf = { - enable = true; - - commands = { - archive = '' - ''${{ - tar --create --xz --file="$PWD/$(basename "$f").tar.xz" "$f" - }} - ''; - - chmod = '' - ''${{ - printf "Mode Bits: " - read ans - - for file in "$fx" - do - chmod $ans $file - done - - lf -remote 'send reload' - }} - ''; - - dlfile = "%dlfile"; - dragon = "%ripdrag -a $fx"; - - mkdir = '' - ''${{ - printf "Directory Name: " - read ans - mkdir $ans - }} - ''; - - mkfile = '' - ''${{ - printf "File Name: " - read ans - $EDITOR $ans - }} - ''; - - paste-image = '' - ''${{ - printf "File Name (e.g., image.png): " - read ans - - if [ -z "$ans" ]; then - echo "No filename provided" - exit 1 - fi - - if [ -e "$ans" ]; then - printf "File already exists, overwrite (y|n): " - read overwrite - if [ "$overwrite" != "y" ]; then - exit 1 - fi - fi - - wl-paste > "$ans" - if [ $? -eq 0 ]; then - echo "Pasted clipboard content to $ans" - else - echo "Failed to paste from clipboard" - fi - }} - ''; - - open = '' - ''${{ - case $(file --mime-type "$f" -bL) in - text/*|application/json) $EDITOR "$f";; - video/*|image/*/application/pdf) xdg-open "$f";; - *) xdg-open "$f";; - esac - }} - ''; - - quit-and-cd = '' - &{{ - pwd > $LF_CD_FILE - lf -remote "send $id quit" - }} - ''; - - sudomkfile = '' - ''${{ - printf "File Name: " - read ans - sudo $EDITOR $ANS - }} - ''; - - trash = '' - ''${{ - files=$(printf "$fx" | tr '\n' ';') - while [ "$files" ]; do - # extract the substring from start of string up to delimiter. - # this is the first "element" of the string. - file=''${files%%;*} - - trash-put "$(basename "$file")" - # if there's only one element left, set `files` to an empty string. - # this causes us to exit this `while` loop. - # else, we delete the first "element" of the string from files, and move onto the next. - if [ "$files" = "$file" ]; then - files=''' - else - files="''${files#*;}" - fi - done - }} - ''; - - unarchive = '' - ''${{ - case "$f" in - *.zip) unzip "$f";; - *.tar.gz) tar -xzvf "$f" ;; - *.tar.bz2) tar -xjvf "$f" ;; - *.tar) tar -xvf "$f" ;; - *) echo "Unsupported format" ;; - esac - }} - ''; - - yank-file = ''$printf '%s' "$f" | wl-copy''; - yank-paths = ''$printf '%s' "$fx" | wl-copy''; - yank-dirname = ''&printf '%s' "$PWD" | wl-copy''; - yank-basename = ''&basename -a -- $fx | head -c-1 | wl-copy''; - yank-basename-without-extension = ''&basename -a -- $fx | sed -E 's/\.[^.]+$//' | head -c-1 | wl-copy''; - - yank-image = '' - ''${{ - # Copy the first selected file's binary content to clipboard as PNG - # Many apps only accept PNG from clipboard on Wayland - file="$(echo "$fx" | head -n1)" - mime_type="$(file --mime-type -b "$file")" - - if [[ "$mime_type" == image/png ]]; then - # Already PNG, copy directly - wl-copy -t image/png < "$file" - else - # Convert to PNG first for compatibility - convert "$file" png:- | wl-copy -t image/png - fi - }} - ''; - - z = '' - %{{ - result="$(zoxide query --exclude $PWD $@ | sed 's/\\/\\\\/g;s/"/\\"/g')" - lf -remote "send $id cd \"$result\"" - }} - ''; - - zi = '' - ''${{ - result="$(zoxide query -i | sed 's/\\/\\\\/g;s/"/\\"/g')" - lf -remote "send $id cd \"$result\"" - }} - ''; - }; - - keybindings = { - "." = "set hidden!"; - d = null; - dd = "trash"; - dl = "dlfile"; - dr = "dragon"; - f = "zi"; - h = "chmod"; - k = "down"; - l = "up"; - ";" = "open"; - j = "updir"; - m = null; - md = "mkdir"; - mf = "mkfile"; - mr = "sudomkfile"; - mp = "paste-image"; - Q = "quit-and-cd"; - x = "cut"; - Y = "yank-image"; - }; - - previewer = { - keybinding = "i"; - source = "${pkgs.pistol}/bin/pistol"; - }; - - settings = { - icons = true; - # Set IFS to newline to allow commands to work with spaces in filenames - ifs = "\n"; - }; - }; - - home.sessionVariables = { - LF_ICONS = '' - tw=:\ - st=:\ - ow=:\ - dt=:\ - di=:\ - fi=:\ - ln=:\ - or=:\ - ex=:\ - *.c=:\ - *.cc=:\ - *.clj=:\ - *.coffee=:\ - *.cpp=:\ - *.css=:\ - *.d=:\ - *.dart=:\ - *.erl=:\ - *.exs=:\ - *.fs=:\ - *.go=:\ - *.h=:\ - *.hh=:\ - *.hpp=:\ - *.hs=:\ - *.html=:\ - *.java=:\ - *.jl=:\ - *.js=:\ - *.json=:\ - *.lua=:\ - *.md=:\ - *.php=:\ - *.pl=:\ - *.pro=:\ - *.py=:\ - *.rb=:\ - *.rs=:\ - *.scala=:\ - *.ts=:\ - *.vim=:\ - *.cmd=:\ - *.ps1=:\ - *.sh=:\ - *.bash=:\ - *.zsh=:\ - *.fish=:\ - *.tar=:\ - *.tgz=:\ - *.arc=:\ - *.arj=:\ - *.taz=:\ - *.lha=:\ - *.lz4=:\ - *.lzh=:\ - *.lzma=:\ - *.tlz=:\ - *.txz=:\ - *.tzo=:\ - *.t7z=:\ - *.zip=:\ - *.z=:\ - *.dz=:\ - *.gz=:\ - *.lrz=:\ - *.lz=:\ - *.lzo=:\ - *.xz=:\ - *.zst=:\ - *.tzst=:\ - *.bz2=:\ - *.bz=:\ - *.tbz=:\ - *.tbz2=:\ - *.tz=:\ - *.deb=:\ - *.rpm=:\ - *.jar=:\ - *.war=:\ - *.ear=:\ - *.sar=:\ - *.rar=:\ - *.alz=:\ - *.ace=:\ - *.zoo=:\ - *.cpio=:\ - *.7z=:\ - *.rz=:\ - *.cab=:\ - *.wim=:\ - *.swm=:\ - *.dwm=:\ - *.esd=:\ - *.jpg=:\ - *.jpeg=:\ - *.mjpg=:\ - *.mjpeg=:\ - *.gif=:\ - *.bmp=:\ - *.pbm=:\ - *.pgm=:\ - *.ppm=:\ - *.tga=:\ - *.xbm=:\ - *.xpm=:\ - *.tif=:\ - *.tiff=:\ - *.png=:\ - *.svg=:\ - *.svgz=:\ - *.mng=:\ - *.pcx=:\ - *.mov=:\ - *.mpg=:\ - *.mpeg=:\ - *.m2v=:\ - *.mkv=:\ - *.webm=:\ - *.ogm=:\ - *.mp4=:\ - *.m4v=:\ - *.mp4v=:\ - *.vob=:\ - *.qt=:\ - *.nuv=:\ - *.wmv=:\ - *.asf=:\ - *.rm=:\ - *.rmvb=:\ - *.flc=:\ - *.avi=:\ - *.fli=:\ - *.flv=:\ - *.gl=:\ - *.dl=:\ - *.xcf=:\ - *.xwd=:\ - *.yuv=:\ - *.cgm=:\ - *.emf=:\ - *.ogv=:\ - *.ogx=:\ - *.aac=:\ - *.au=:\ - *.flac=:\ - *.m4a=:\ - *.mid=:\ - *.midi=:\ - *.mka=:\ - *.mp3=:\ - *.mpc=:\ - *.ogg=:\ - *.ra=:\ - *.wav=:\ - *.oga=:\ - *.opus=:\ - *.spx=:\ - *.xspf=:\ - *.pdf=:\ - *.nix=: - ''; - }; -} diff --git a/home/libation/default.nix b/home/libation/default.nix deleted file mode 100644 index 6f82571b..00000000 --- a/home/libation/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - "Libation" - ".local/share/Libation" - ]; - }; - - home.packages = [ - pkgs.libation - ]; -} diff --git a/home/libreoffice/default.nix b/home/libreoffice/default.nix deleted file mode 100644 index 43006658..00000000 --- a/home/libreoffice/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/libreoffice" - ]; - }; - - home.packages = [ - pkgs.libreoffice - ]; -} diff --git a/home/lnav/default.nix b/home/lnav/default.nix deleted file mode 100644 index a27f90b3..00000000 --- a/home/lnav/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/lnav" - ]; - }; - - home.packages = [ - pkgs.lnav - ]; -} diff --git a/home/mic-levels-maintainer/default.nix b/home/mic-levels-maintainer/default.nix deleted file mode 100644 index bd2da9cc..00000000 --- a/home/mic-levels-maintainer/default.nix +++ /dev/null @@ -1,29 +0,0 @@ -# Maintain input gain levels -{ - pkgs, - osConfig, - ... -}: let - isNumenor = osConfig.networking.hostName == "numenor"; - mic-levels-maintainer = pkgs.writers.writeBashBin "mic-levels-maintainer" ( - if isNumenor - then (builtins.readFile ./scripts/mic-levels-maintainer-numenor.sh) - else (builtins.readFile ./scripts/mic-levels-maintainer-flexbox.sh) - ); -in { - home.packages = [mic-levels-maintainer]; - - systemd.user.services.mic-levels-maintainer = { - Unit = { - After = ["obs-mic.service"]; - Description = "Maintain input gain levels"; - Requires = ["obs-mic.service"]; - }; - Install.WantedBy = ["obs-mic.service"]; - Service = { - Environment = "PATH=$PATH:/run/current-system/sw/bin"; - ExecStart = "${mic-levels-maintainer}/bin/mic-levels-maintainer"; - Restart = "always"; - }; - }; -} diff --git a/home/mpv/default.nix b/home/mpv/default.nix deleted file mode 100644 index d22a9f79..00000000 --- a/home/mpv/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/mpv" - ".cache/mpv" - ]; - }; - - home.packages = [ - pkgs.mpv - ]; -} diff --git a/home/mullvad-browser/default.nix b/home/mullvad-browser/default.nix deleted file mode 100644 index 08248360..00000000 --- a/home/mullvad-browser/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ - isImpermanent, - lib, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".mullvad" - ]; - }; - - home.packages = [ - pkgs.mullvad-browser - ]; -} diff --git a/home/nautilus/default.nix b/home/nautilus/default.nix deleted file mode 100644 index 0bbf0e40..00000000 --- a/home/nautilus/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/nautilus" - ".local/share/nautilus" - ]; - }; - - home.packages = [ - pkgs.nautilus - ]; -} diff --git a/home/neovim/default.nix b/home/neovim/default.nix deleted file mode 100644 index db8608f4..00000000 --- a/home/neovim/default.nix +++ /dev/null @@ -1,356 +0,0 @@ -{ - isImpermanent, - lib, - osConfig, - pkgs, - ... -}: let - isLightTheme = osConfig.specialisation != {}; -in { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".local/share/nvim" # Data - ".local/state/nvim" # state - ".cache/nvim" - ]; - }; - - imports = - [ - ./avante # Cursor style AI IDE - ./carbon - ./cmp - ./comment-nvim - ./dadbod - ./dap - ./diffview-nvim - ./fidget # Sidebar notifications for LSP - ./folds - ./fugitive - ./git-conflict-nvim - ./gitsigns - ./jdtls - ./jj - ./lspsaga - ./lualine - # Ensure devicons module is available before mocking it with mini.icons - ./mason-lsp - ./mini-icons - ./mini-operators - ./neotest - ./noice # UI for commandline, messages and popupmenu - ./conform - ./notify # Pluggable Notifications - ./nui # UI Components - ./nvim-tree-lua # File Tree - ./obsidian-nvim - ./oil - ./otter # LSP for embedded code in markdown/quarto - ./overseer - ./plenary # LUA Functions - ./rainbow-csv - ./render-markdown - ./telescope - ./toggleterm - ./treesitter - ./trouble - ./undotree - ./vim-be-good # Vim Motion Learnenings - ./vim-terraform - ./vim-visual-multi - ./web-devicons - ./yank-file-line - ] - ++ lib.optionals isLightTheme [ - ./gruvbox - ]; - - programs.neovim = { - enable = true; - - extraConfig = '' - set mouse=a - set number - set clipboard=unnamedplus - set ignorecase - set smartcase - - " Defaults to be overwritten by vim-sleuth - set tabstop=4 - set shiftwidth=4 - - set grepprg=rg\ --vimgrep\ --no-heading\ --smart-case - - noremap h ; - noremap ; l - noremap l k - noremap k j - noremap j h - noremap ; l - noremap l k - noremap k j - noremap j h - noremap : L - noremap L K - noremap K J - noremap J H - - " Vertical Awesomeness - nnoremap zz - nnoremap zz - nnoremap G Gzz - - tnoremap h - tnoremap j - tnoremap k - tnoremap l - inoremap h - inoremap j - inoremap k - inoremap l - nnoremap h - nnoremap j - nnoremap k - nnoremap l - - set shell=/etc/profiles/per-user/farlion/bin/fish - - let mapleader = ' ' - let maplocalleader = ',' - - " Diff Settings - set diffopt+=internal,algorithm:patience - - " Quickfix Lists - nnoremap :cprev - nnoremap :cnext - - " Saving - use z. for centering the cursor instead - nnoremap zz :update - - " Applying a macro to lines matching in visual selection - " https://medium.com/@schtoeffel/you-don-t-need-more-than-one-cursor-in-vim-2c44117d51db - xnoremap @ :call ExecuteMacroOverVisualRange() - function! ExecuteMacroOverVisualRange() - echo "@".getcmdline() - execute ":'<,'>normal @".nr2char(getchar()) - endfunction - - " https://github.com/Saecki/crates.nvim - lua require('crates').setup() - - " Fugitive - " See https://github.com/tpope/vim-fugitive/issues/1510#issuecomment-660837020 - function! s:ftplugin_fugitive() abort - nnoremap cc :Git commit --quiet - nnoremap ca :Git commit --quiet --amend - nnoremap ce :Git commit --quiet --amend --no-edit - endfunction - augroup quiet_fugitive - autocmd! - autocmd FileType fugitive call s:ftplugin_fugitive() - augroup END - - " Vim Visual Multi - let g:VM_custom_motions = {'h': ';', ';': 'l', 'l': 'k', 'k': 'j', 'j': 'h'} - let g:VM_mouse_mappings = 1 - - " Background light/dark toggling - nmap i :let &bg=(&bg=='light'?'dark':'light') - - " Sudo powers with :w!! - cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' edit! - ''; - - extraLuaConfig = '' - -- set termguicolors (24bit colors) to enable highlight groups - vim.opt.termguicolors = true - - -- Wrapped lines should follow the indent - vim.o.breakindent = true - - -- Remap for dealing with word wrap - vim.keymap.set('n', 'k', "v:count == 0 ? 'gj' : 'j'", { expr = true, silent = true }) - vim.keymap.set('n', 'l', "v:count == 0 ? 'gk' : 'k'", { expr = true, silent = true }) - - -- Save undo history - -- TODO: sync this if useful - vim.o.undofile = true - - -- Timeout and Updatetime settings - vim.o.timeout = true - vim.o.timeoutlen = 300 - vim.o.updatetime = 250 - - -- Set completeopt to have a better completion experience - vim.o.completeopt = 'menuone,noselect' - - -- Make faster - vim.keymap.set({ 'n', 'v' }, '', '', { silent = true }) - - -- Disable swapfiles - vim.opt.swapfile = false - - -- Terminal Mode shortcuts - vim.keymap.set('t', '', '', { silent = true }) - vim.keymap.set('t', '', [[]], { silent = true }) - - -- Faster exit shortcut - vim.keymap.set({ 'n', 'v', 't' }, 'ZQ', ':qa!') - - -- Disable F1 as help shortcut - vim.keymap.set({'n', 'i'}, '', '', { noremap = true, silent = true }) - ''; - - extraPackages = with pkgs; [ - harper # Grammar checker - nixd # Nix Language Server - nodejs # For vim to have npm for Mason etc... - prettierd # For yaml, html, json, markdown - pyright - shellcheck - shfmt - ]; - - plugins = with pkgs.vimPlugins; [ - argtextobj-vim - { - plugin = b64-nvim; # Base64 encoding/decoding - config = '' - local wk = require("which-key") - wk.add( - { - { - mode = { "v" }, - { "b", group = "[B]ase64" }, - { "bd", require("b64").decode, desc = "Base64 [D]ecode" }, - { "be", require("b64").decode, desc = "Base64 [E]ncode" }, - }, - } - ) - ''; - type = "lua"; - } - { - plugin = dressing-nvim; # Better UI for codeactions, code input etc... - config = '' - - ''; - type = "lua"; - } - { - plugin = friendly-snippets; # User-friendly snippets, work with LuaSnip and other engines - } - vim-highlightedyank - { - plugin = indent-blankline-nvim; # Indentation guides - config = '' - require("ibl").setup({ - exclude = { - filetypes = {"startify"}, - } - }) - local hooks = require "ibl.hooks" - hooks.register(hooks.type.ACTIVE, function(bufnr) - return vim.tbl_contains( - { "yaml", "html", "svelte" }, - vim.api.nvim_get_option_value("filetype", { buf = bufnr }) - ) - end) - local wk = require("which-key") - wk.add({ - { "[i", function() require("ibl").setup_buffer(0, {enabled = true}) end, desc = "Indentation Guides ON" }, - { "]i", function() require("ibl").setup_buffer(0, {enabled = false}) end, desc = "Indentation Guides OFF" }, - }) - ''; - type = "lua"; - } - { - plugin = nvim-lspconfig; # Defaults for loads of LSP languages - } - { - plugin = luasnip; # Snippet engine - } - { - plugin = markdown-preview-nvim; - config = '' - local wk = require("which-key") - wk.add({ - { "p", "MarkdownPreviewToggle", desc = "Toggle Markdown [P]review" }, - }) - ''; - type = "lua"; - } - vim-numbertoggle - { - plugin = vim-qf; # Quickfix improvements - config = ''''; - } - vim-rooter - vim-sleuth # Automatic shiftwidth and expandtab - { - plugin = vim-startify; - config = '' - let g:startify_change_to_dir = 0 - let g:startify_change_to_vcs_root = 1 - let g:startify_session_persistence = 1 - let g:startify_bookmarks = [ {'c': '~/nixos-config/home/neovim/default.nix'} ] - ''; - } - vim-surround - { - plugin = vim-suda; # Sudo support via :SudaRead and :SudaWrite - } - { - plugin = text-case-nvim; - config = '' - require("textcase").setup({ - prefix = "ga", - }) - require("telescope").load_extension("textcase") - local wk = require("which-key") - wk.add({ - { "ga.", "TextCaseOpenTelescope", desc = "Telescope" }, - }) - ''; - type = "lua"; - } - vim-textobj-entire - vim-toml - vim-unimpaired - { - plugin = which-key-nvim; - config = '' - require("which-key").setup { - } - ''; - type = "lua"; - } - - ## Language Specific Plugins - crates-nvim - dart-vim-plugin - elm-vim - vim-graphql - vim-jsonnet - { - plugin = neodev-nvim; # Nvim LUA development - config = '' - require("neodev").setup({ - library = { plugins = { "nvim-dap-ui", "neotest" }, types = true }, - }) - ''; - type = "lua"; - } - vim-flutter - vim-helm - vim-shellcheck - vim-solidity - vim-nix - ]; - - viAlias = true; - vimAlias = true; - vimdiffAlias = true; - }; -} diff --git a/home/networkmanager-dmenu/default.nix b/home/networkmanager-dmenu/default.nix deleted file mode 100644 index 2d88e28c..00000000 --- a/home/networkmanager-dmenu/default.nix +++ /dev/null @@ -1,4 +0,0 @@ -{pkgs, ...}: { - home.packages = [pkgs.networkmanager_dmenu]; - xdg.configFile."networkmanager-dmenu/config.ini".source = ./config.ini; -} diff --git a/home/nh/default.nix b/home/nh/default.nix deleted file mode 100644 index 05b1744f..00000000 --- a/home/nh/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -{...}: { - programs.nh = { - enable = true; - flake = "/home/farlion/code/nixos-config"; - }; -} diff --git a/home/niri/default.nix b/home/niri/default.nix deleted file mode 100644 index 59c04146..00000000 --- a/home/niri/default.nix +++ /dev/null @@ -1,640 +0,0 @@ -{ - config, - isNvidia, - lib, - osConfig, - pkgs, - ... -}: -with lib; let - isNumenor = osConfig.networking.hostName == "numenor"; - - leftScreen = - if isNumenor - then "HDMI-A-2" - else null; - mainScreen = - if isNumenor - then "DP-1" - else "eDP-1"; - rightScreen = - if isNumenor - then "HDMI-A-1" - else null; - - locker = "${pkgs.bash}/bin/bash -c '${pkgs.procps}/bin/pgrep -x swaylock || ${pkgs.swaylock-effects}/bin/swaylock --daemonize'"; - suspender = "${pkgs.systemd}/bin/systemctl suspend-then-hibernate"; - - # Wallpaper, until stylix supports it :) - wallpaperSetter = pkgs.writeShellApplication { - name = "niri-set-wallpaper"; - runtimeInputs = [pkgs.swaybg pkgs.procps]; - text = builtins.readFile ./scripts/niri-set-wallpaper.sh; - }; - - # Window Picker a la rofi - windowPicker = pkgs.writeShellApplication { - name = "niri-pick-window"; - runtimeInputs = [pkgs.niri pkgs.unstable.fuzzel pkgs.jq]; - text = builtins.readFile ./scripts/niri-pick-window.sh; - }; - - # Calculator via fuzzel + qalc - fuzzelCalc = pkgs.writeShellApplication { - name = "niri-qalc"; - runtimeInputs = [pkgs.unstable.fuzzel pkgs.libqalculate pkgs.wl-clipboard pkgs.libnotify]; - text = builtins.readFile ./scripts/niri-qalc.sh; - }; - - # Workspace reorderer - maintains logical order after moving workspaces between monitors - workspaceReorderer = pkgs.writeShellApplication { - name = "niri-reorder-workspaces"; - runtimeInputs = [pkgs.niri pkgs.jq]; - text = builtins.readFile ./scripts/niri-reorder-workspaces.sh; - }; - - # Auto-column - consumes new windows into columns on vertical monitors - autoColumn = pkgs.writeShellApplication { - name = "niri-auto-column"; - runtimeInputs = [pkgs.niri pkgs.jq pkgs.coreutils]; - text = builtins.readFile ./scripts/niri-auto-column.sh; - }; - - # Open a command and move its window to a workspace once title matches - openOnWorkspace = pkgs.writeShellApplication { - name = "niri-open-on-workspace"; - runtimeInputs = [pkgs.niri pkgs.jq]; - text = builtins.readFile ./scripts/niri-open-on-workspace.sh; - }; - - niriBinds = { - suffixes, - prefixes, - substitutions ? {}, - }: let - replacer = replaceStrings (attrNames substitutions) (attrValues substitutions); - format = prefix: suffix: let - actual-suffix = - if isList suffix.action - then { - action = head suffix.action; - args = tail suffix.action; - } - else { - inherit (suffix) action; - args = []; - }; - - action = replacer "${prefix.action}-${actual-suffix.action}"; - in { - name = "${prefix.key}+${suffix.key}"; - value.action.${action} = actual-suffix.args; - }; - pairs = attrs: fn: - concatMap ( - key: - fn { - inherit key; - action = attrs.${key}; - } - ) (attrNames attrs); - in - listToAttrs (pairs prefixes (prefix: pairs suffixes (suffix: [(format prefix suffix)]))); -in { - home.packages = with pkgs; [ - brightnessctl # For brightness +/- keys - fuzzelCalc # niri-qalc - hyprmagnifier # Screen magnifier for Wayland - playerctl # For play/pause etc... controlling media players that implement MPRIS - qt5.qtwayland # Needed for QT_QPA_PLATFORM=wayland - swaybg # Minmal wallpaper setter for Sway - wallpaperSetter # Specialization-aware wallpaper setting - windowPicker # niri-pick-window - workspaceReorderer # niri-reorder-workspaces - xwayland-satellite # For apps that need Xwayland - ]; - - programs.swaylock = { - enable = true; - package = pkgs.swaylock-effects; - settings = { - debug = false; - show-failed-attempts = true; - ignore-empty-password = true; - screenshots = true; - effect-pixelate = 10; # Pixellation level (higher = more pixelated) - effect-blur = "7x5"; - }; - }; - - services.swayidle = { - enable = true; - events = [ - { - event = "before-sleep"; - command = "${locker}"; - } - { - event = "lock"; - command = "${locker}"; - } - ]; - timeouts = [ - { - timeout = 360; - command = "${locker}"; - } - { - timeout = 370; - command = "/run/current-system/sw/bin/niri msg action power-off-monitors"; - resumeCommand = "${pkgs.coreutils}/bin/sleep 1; /run/current-system/sw/bin/niri msg action power-on-monitors"; - } - { - timeout = 1800; - command = "${suspender}"; - } - ]; - }; - - # Fix swayidle service dependencies for Niri/Wayland session - # Fails to boot with default settings - systemd.user.services.swayidle = { - Unit = { - After = ["niri.service" "graphical-session.target"]; - Wants = ["graphical-session.target"]; - # Override the default ConditionEnvironment to be less strict - ConditionEnvironment = lib.mkForce []; - }; - Service = { - # Add a small delay to double-ensure Wayland display is ready - ExecStartPre = "${pkgs.coreutils}/bin/sleep 2"; - # Restart the service if it fails (useful for session restarts) - Restart = lib.mkForce "on-failure"; - RestartSec = "5"; - }; - }; - - # Auto-column service for vertical monitors - systemd.user.services.niri-auto-column = lib.mkIf isNumenor { - Unit = { - Description = "Auto-consume windows into columns on vertical monitors"; - After = ["niri.service" "graphical-session.target"]; - Wants = ["graphical-session.target"]; - }; - Service = { - ExecStart = "${autoColumn}/bin/niri-auto-column"; - Restart = "on-failure"; - RestartSec = "5"; - }; - Install = { - WantedBy = ["graphical-session.target"]; - }; - }; - - programs.wleave = { - enable = true; - }; - - # Wallpaper, until stylix supports it :) - home.file.".local/share/wallpapers/gruvbox-light.png".source = ./wallpapers/gruvbox-light-rainbow.png; - home.file.".local/share/wallpapers/gruvbox-dark.png".source = ./wallpapers/gruvbox-dark-rainbow.png; - - # TODO: Activate once the Niri flake supports niri 25.11 - # Per-output layout settings for vertical monitors (raw KDL - not exposed in niri-flake settings) - # programs.niri.config = - # lib.optionalString (leftScreen != null) '' - # output "${leftScreen}" { - # layout { - # default-column-width { proportion 1.0; } - # } - # } - # '' - # + lib.optionalString (rightScreen != null) '' - # output "${rightScreen}" { - # layout { - # default-column-width { proportion 1.0; } - # } - # } - # ''; - - programs.niri.settings = rec { - # Environment - environment = { - NIXOS_OZONE_WL = "1"; # Enable Ozone-Wayland for Electron apps and Chromium, see https://nixos.wiki/wiki/Wayland - }; - - # Input Settings - input = { - keyboard = { - xkb = { - layout = "us,de,ua,pt"; - options = "eurosign:e,terminate:ctrl_alt_bksp"; - }; - }; - touchpad = { - dwt = true; # Disable touchpad while typing - disabled-on-external-mouse = false; - natural-scroll = false; - tap = true; - tap-button-map = "left-right-middle"; - }; - focus-follows-mouse.enable = true; - }; - - # Cursor Settings - cursor = { - hide-after-inactive-ms = 3000; - hide-when-typing = true; - }; - - # Startup - spawn-at-startup = [ - {command = ["${pkgs.bash}/bin/bash" "-c" "sleep 10 && systemctl --user restart xdg-desktop-portal"];} # Hacks around a timing prob with xdg-desktop-portal on first boot, see https://github.com/sodiboo/niri-flake/issues/509 - {command = ["systemctl" "--user" "restart" "kanshi"];} - {command = ["systemctl" "--user" "restart" "app-blueman@autostart"];} - {command = ["systemctl" "--user" "start" "gnome-keyring-ssh"];} # Start GNOME Keyring SSH agent - {command = ["obsidian"];} - {command = ["ytmdesktop" "--password-store=gnome-libsecret"];} - # {command = ["seahorse"];} # To unlock keyring - {command = ["${wallpaperSetter}/bin/niri-set-wallpaper"];} # Set wallpaper - {command = ["wlsunset-waybar"];} - {command = ["${openOnWorkspace}/bin/niri-open-on-workspace" "${workspaces."00".name}" "ChatGPT" "zen" "--new-window" "https://chatgpt.com/"];} - {command = ["${openOnWorkspace}/bin/niri-open-on-workspace" "${workspaces."09".name}" "[Vv]ikunja" "zen" "--new-window" "https://vikunja.hyena-byzantine.ts.net/"];} - ]; - - # Window Rules - # Find app_id or title with `niri msg windows` - window-rules = [ - { - matches = [ - {app-id = "^obsidian$";} - ]; - open-on-workspace = " 7"; - } - { - matches = [ - {app-id = "^signal$";} - {app-id = "^teams-for-linux$";} - {app-id = "^org.telegram.desktop$";} - ]; - open-on-workspace = " 8"; - } - { - matches = [ - {app-id = "^YouTube Music Desktop App$";} - ]; - open-on-workspace = " 9"; - } - { - matches = [ - {title = ".*[Vv]ikunja.*";} - ]; - open-on-workspace = " 9"; - } - { - matches = [ - {title = ".*ChatGPT.*";} - ]; - open-on-workspace = " a"; - } - # Floating windows - { - matches = [ - {title = ".*Pavucontrol.*";} - {title = ".*zoom.*";} - ]; - open-floating = true; - } - # Block from screencasting - { - matches = [ - {app-id = "^Bitwarden$";} - {app-id = "^com.obsproject.Studio$";} - ]; - block-out-from = "screen-capture"; - } - # Screen Cast Target Highlight - { - matches = [ - {is-window-cast-target = true;} - ]; - border = { - active = {color = "#f38ba8";}; - inactive = {color = "#7d0d2d";}; - }; - shadow = { - color = "#7d0d2d70"; - }; - tab-indicator = { - active = {color = "#f38ba8";}; - inactive = {color = "#7d0d2d";}; - }; - } - ]; - - # Named Workspaces - workspaces = { - "00" = { - name = " a"; - open-on-output = rightScreen; - }; - "01" = { - name = " 1"; - open-on-output = leftScreen; - }; - "02" = { - name = " 2"; - open-on-output = mainScreen; - }; - "03" = { - name = " 3"; - open-on-output = rightScreen; - }; - "04" = { - name = " 4"; - open-on-output = mainScreen; - }; - "05" = { - name = " 5"; - open-on-output = mainScreen; - }; - "06" = { - name = " 6"; - open-on-output = mainScreen; - }; - "07" = { - name = " 7"; - open-on-output = rightScreen; - }; - "08" = { - name = " 8"; - open-on-output = mainScreen; - }; - "09" = { - name = " 9"; - open-on-output = leftScreen; - }; - "10" = { - name = " 10"; - open-on-output = mainScreen; - }; - }; - - # Layout - layout = { - border = { - enable = true; - width = 2; # Default 4 - }; - - default-column-width.proportion = 1. / 2.; - - gaps = 4; # Default 16 - - preset-column-widths = [ - {proportion = 1. / 2.;} - {proportion = 1. / 3.;} - {proportion = 2. / 3.;} - ]; - - shadow = { - enable = true; - }; - }; - - # Style - prefer-no-csd = true; - - # Animations - animations = { - workspace-switch.enable = false; - }; - - # Keybindings - hotkey-overlay.skip-at-startup = true; - binds = with config.lib.niri.actions; - lib.attrsets.mergeAttrsList [ - { - "Mod+Shift+Slash".action = show-hotkey-overlay; - - "Mod+Return".action = spawn "alacritty"; - "Mod+Return".hotkey-overlay.title = "Open a Terminal: alacritty"; - "Mod+D".action = spawn "fuzzel"; - "Mod+D".hotkey-overlay.title = "Run an Application: fuzzel"; - "Mod+Shift+D".action = spawn "${windowPicker}/bin/niri-pick-window"; - "Mod+Shift+D".hotkey-overlay.title = "Pick a Window: niri-pick-window"; - "Mod+Shift+X".action = spawn-sh "swaylock --daemonize && niri msg action power-off-monitors"; - "Mod+Shift+X".hotkey-overlay.title = "Lock screen and turn off monitors"; - "Mod+z".action = spawn "hyprmagnifier"; - "Mod+z".hotkey-overlay.title = "Screen magnifier"; - "Mod+Shift+z".action = power-off-monitors; - "Mod+Shift+z".hotkey-overlay.title = "Power off Monitors"; - - "XF86AudioRaiseVolume".action = spawn-sh "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1+"; - "XF86AudioRaiseVolume".allow-when-locked = true; - "XF86AudioLowerVolume".action = spawn-sh "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1-"; - "XF86AudioLowerVolume".allow-when-locked = true; - "XF86AudioMute".action = spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"; - "XF86AudioMute".allow-when-locked = true; - "XF86AudioMicMute".action = spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"; - "XF86AudioMicMute".allow-when-locked = true; - "XF86MonBrightnessUp".action = spawn-sh "brightnessctl --class=backlight set 10%+"; - "XF86MonBrightnessUp".allow-when-locked = true; - - "XF86MonBrightnessDown".action = spawn-sh "brightnessctl --class=backlight set 10%-"; - "XF86MonBrightnessDown".allow-when-locked = true; - - "Mod+Shift+Q".action = close-window; - "Mod+Shift+Q".repeat = false; - } - (niriBinds { - suffixes."Left" = "column-left"; - suffixes."j" = "column-left"; - suffixes."Down" = "window-down"; - suffixes."k" = "window-down"; - suffixes."Up" = "window-up"; - suffixes."l" = "window-up"; - suffixes."Right" = "column-right"; - suffixes."semicolon" = "column-right"; - prefixes."Mod" = "focus"; - prefixes."Mod+Ctrl" = "move"; - prefixes."Mod+Shift" = "focus-monitor"; - prefixes."Mod+Shift+Ctrl" = "move-workspace-to-monitor"; - substitutions."monitor-column" = "monitor"; - substitutions."monitor-window" = "monitor"; - }) - (niriBinds { - suffixes."Home" = "first"; - suffixes."End" = "last"; - prefixes."Mod" = "focus-column"; - prefixes."Mod+Ctrl" = "move-column-to"; - }) - (niriBinds { - suffixes."U" = "workspace-down"; - suffixes."Page_Down" = "workspace-down"; - suffixes."I" = "workspace-up"; - suffixes."Page_Up" = "workspace-up"; - prefixes."Mod" = "focus"; - prefixes."Mod+Ctrl" = "move-window-to"; - prefixes."Mod+Shift" = "move"; - }) - (niriBinds { - suffixes = { - "a" = ["workspace" "${workspaces."00".name}"]; - "1" = ["workspace" "${workspaces."01".name}"]; - "2" = ["workspace" "${workspaces."02".name}"]; - "3" = ["workspace" "${workspaces."03".name}"]; - "4" = ["workspace" "${workspaces."04".name}"]; - "5" = ["workspace" "${workspaces."05".name}"]; - "6" = ["workspace" "${workspaces."06".name}"]; - "7" = ["workspace" "${workspaces."07".name}"]; - "8" = ["workspace" "${workspaces."08".name}"]; - "9" = ["workspace" "${workspaces."09".name}"]; - "0" = ["workspace" "${workspaces."10".name}"]; - }; - prefixes."Mod" = "focus"; - prefixes."Mod+Ctrl" = "move-column-to"; - }) - { - "Mod+BracketLeft".action = consume-or-expel-window-left; - "Mod+BracketRight".action = consume-or-expel-window-right; - - "Mod+Comma".action = consume-window-into-column; - "Mod+Period".action = expel-window-from-column; - - "Mod+R".action = switch-preset-column-width; - "Mod+Shift+R".action = switch-preset-window-height; - "Mod+Ctrl+R".action = reset-window-height; - - "Mod+F".action = maximize-column; - "Mod+Shift+F".action = fullscreen-window; - "Mod+Ctrl+F".action = expand-column-to-available-width; - - "Mod+C".action = center-column; - "Mod+Ctrl+C".action = center-visible-columns; - - "Mod+Minus".action = set-column-width "-10%"; - "Mod+Equal".action = set-column-width "+10%"; - "Mod+Shift+Minus".action = set-window-height "-10%"; - "Mod+Shift+Equal".action = set-window-height "+10%"; - - "Mod+V".action = toggle-window-floating; - "Mod+Shift+V".action = switch-focus-between-floating-and-tiling; - - "Mod+Shift+W".action = spawn-sh ( - builtins.concatStringsSep "; " [ - "systemctl --user restart waybar.service" - "${wallpaperSetter}/bin/niri-set-wallpaper" - ] - ); - "Mod+Shift+W".hotkey-overlay.title = "Restart Waybar"; - - "Mod+Space".action = switch-layout "next"; - "Mod+Shift+Space".action = switch-layout "prev"; - - "Print".action.screenshot = []; - "Print".hotkey-overlay.title = "Screenshot via Niri"; - "Mod+Print".action = spawn "satty-screenshot"; - "Mod+Print".hotkey-overlay.title = "Screenshot via Satty"; - "Mod+Shift+Print".action.screenshot-screen = []; - "Mod+Shift+Print".hotkey-overlay.title = "Instant Screenshot"; - - # Applications such as remote-desktop clients and software KVM switches may - # request that niri stops processing the keyboard shortcuts defined here - # so they may, for example, forward the key presses as-is to a remote machine. - # It's a good idea to bind an escape hatch to toggle the inhibitor, - # so a buggy application can't hold your session hostage. - # - # The allow-inhibiting=false property can be applied to other binds as well, - # which ensures niri always processes them, even when an inhibitor is active. - "Mod+Shift+Escape".action = toggle-keyboard-shortcuts-inhibit; - "Mod+Shift+Escape".allow-inhibiting = false; - - "Ctrl+Alt+Delete".action = quit; - } - - { - # Dynamic Cast ([G]rab Window or Screen) - "Mod+G".action = set-dynamic-cast-window; - "Mod+Shift+G".action = set-dynamic-cast-monitor; - "Mod+Delete".action = clear-dynamic-cast-target; - - # Fancy Moving - "Mod+Tab".action = focus-window-down-or-column-right; - "Mod+Shift+Tab".action = focus-window-up-or-column-left; - } - { - # Browser - "Mod+b".action = spawn-sh ( - if isNvidia - then "brave --profile-directory='Default' --enable-features=VaapiVideoDecoder,VaapiVideoEncoder --password-store=seahorse" - else "brave --profile-directory='Default' --password-store=seahorse" - ); - "Mod+b".hotkey-overlay.hidden = true; - "Mod+Shift+b".action = spawn "zen"; - "Mod+Shift+b".hotkey-overlay.hidden = true; - "Mod+h".action = spawn-sh ( - if isNvidia - then "brave --profile-directory='Profile 1' --enable-features=VaapiVideoDecoder,VaapiVideoEncoder --password-store=seahorse" - else "brave --profile-directory='Profile 1' --password-store=seahorse" - ); - "Mod+h".hotkey-overlay.hidden = true; - - # Cliphist via fuzzel - "Mod+p".action = spawn "cliphist-fuzzel-img"; - "Mod+p".hotkey-overlay.hidden = true; - # Single item clearing - "Mod+Shift+p".action = spawn-sh "cliphist list | fuzzel --dmenu | cliphist delete"; - "Mod+Shift+p".hotkey-overlay.hidden = true; - - # File Manager [n]avigate - "Mod+n".action = spawn-sh "alacritty -e fish -ic lf"; - "Mod+n".hotkey-overlay.hidden = true; - - # Calcu[M]athlator - "Mod+m".action = spawn "${fuzzelCalc}/bin/niri-qalc"; - "Mod+m".hotkey-overlay.title = "Calcu[M]athalor via qalculate"; - - # Logout and Power Menu - "Mod+Pause".action = spawn "wleave"; - - # Network ([W]ifi) Selection - "Mod+w".action = spawn "${pkgs.networkmanager_dmenu}/bin/networkmanager_dmenu"; - "Mod+w".hotkey-overlay.hidden = true; - - # Overview - "Mod+o".action = toggle-overview; - "Mod+o".repeat = false; - - # Reorder Workspaces (after moving them around) - "Mod+Shift+o".action = spawn "${workspaceReorderer}/bin/niri-reorder-workspaces"; - "Mod+Shift+o".hotkey-overlay.title = "Re[o]rder workspaces to maintain logical order"; - - # Rofi[e]moji - "Mod+e".action = spawn "rofimoji"; - "Mod+e".hotkey-overlay.hidden = true; - "Mod+Shift+e".action = spawn ["rofimoji" "--action" "clipboard"]; - "Mod+Shift+e".hotkey-overlay.hidden = true; - - # [S]ound Switcher - "Mod+s".action = spawn "sound-switcher"; - "Mod+s".hotkey-overlay.hidden = true; - } - { - # OBS Studio Controls - "Alt+F1".action = spawn "obs-main-scene"; - "Alt+F1".hotkey-overlay.title = "OBS: Switch to Main Scene"; - "Alt+F2".action = spawn "obs-screensharing"; - "Alt+F2".hotkey-overlay.title = "OBS: Switch to Screensharing"; - "Alt+F3".action = spawn "obs-catcam-toggle"; - "Alt+F3".hotkey-overlay.title = "OBS: Toggle Catcam"; - "Alt+F4".action = spawn "obs-recording-toggle"; - "Alt+F4".hotkey-overlay.title = "OBS: Start/Stop Recording"; - "Alt+F5".action = spawn "obs-recording-pause"; - "Alt+F5".hotkey-overlay.title = "OBS: Pause/Unpause Recording"; - "Alt+F6".action = spawn "obs-webcam-toggle"; - "Alt+F6".hotkey-overlay.title = "OBS: Toggle Webcam"; - } - ]; - }; -} diff --git a/home/nix-index/default.nix b/home/nix-index/default.nix deleted file mode 100644 index baa2a651..00000000 --- a/home/nix-index/default.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ - inputs, - isImpermanent, - lib, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - files = [ - ".local/state/comma-choices" # For , - ]; - }; - imports = [ - inputs.nix-index-database.homeModules.nix-index - ]; - - programs.nix-index = { - enable = true; - }; -} diff --git a/home/nix-inspect/default.nix b/home/nix-inspect/default.nix deleted file mode 100644 index 32c0debf..00000000 --- a/home/nix-inspect/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{pkgs, ...}: { - home.packages = [ - pkgs.nix-inspect - ]; -} diff --git a/home/nushell/default.nix b/home/nushell/default.nix deleted file mode 100644 index c105e8f2..00000000 --- a/home/nushell/default.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ - isImpermanent, - lib, - pkgs, - ... -}: let - # Custom nushell build with system-clipboard support for Ctrl+X keybinding - nushellWithClipboard = pkgs.unstable.nushell.overrideAttrs (oldAttrs: { - cargoBuildFeatures = (oldAttrs.cargoBuildFeatures or []) ++ ["system-clipboard"]; - }); -in { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/nushell" - ]; - }; - home.file = { - ".config/nushell/.stignore" = { - source = ./syncthing/stignore-nushell; - }; - }; - programs.nushell = { - enable = true; - configFile.source = ./config.nu; - envFile.source = ./env.nu; - package = nushellWithClipboard; - }; -} diff --git a/home/obs/default.nix b/home/obs/default.nix deleted file mode 100644 index 712904d8..00000000 --- a/home/obs/default.nix +++ /dev/null @@ -1,85 +0,0 @@ -{ - isImpermanent, - lib, - pkgs, - osConfig, - ... -}: let - isFlexbox = osConfig.networking.hostName == "flexbox"; - - # OBS Control Scripts - obsMainScene = pkgs.writeShellApplication { - name = "obs-main-scene"; - runtimeInputs = [pkgs.obs-cmd]; - text = builtins.readFile ./scripts/obs-main-scene.sh; - }; - - obsScreensharing = pkgs.writeShellApplication { - name = "obs-screensharing"; - runtimeInputs = [pkgs.obs-cmd]; - text = builtins.readFile ./scripts/obs-screensharing.sh; - }; - - obsCatcamToggle = pkgs.writeShellApplication { - name = "obs-catcam-toggle"; - runtimeInputs = [pkgs.obs-cmd]; - text = builtins.readFile ./scripts/obs-catcam-toggle.sh; - }; - - obsRecordingToggle = pkgs.writeShellApplication { - name = "obs-recording-toggle"; - runtimeInputs = [pkgs.obs-cmd]; - text = builtins.readFile ./scripts/obs-recording-toggle.sh; - }; - - obsRecordingPause = pkgs.writeShellApplication { - name = "obs-recording-pause"; - runtimeInputs = [pkgs.obs-cmd]; - text = builtins.readFile ./scripts/obs-recording-pause.sh; - }; - - obsWebcamToggle = pkgs.writeShellApplication { - name = "obs-webcam-toggle"; - runtimeInputs = [pkgs.obs-cmd]; - text = builtins.readFile ./scripts/obs-webcam-toggle.sh; - }; -in { - home.packages = [ - pkgs.obs-cmd - obsMainScene - obsScreensharing - obsCatcamToggle - obsRecordingToggle - obsRecordingPause - obsWebcamToggle - ]; - - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/obs-studio" - ]; - }; - programs.obs-studio = { - enable = true; - plugins = with pkgs.obs-studio-plugins; [ - obs-backgroundremoval - pkgs.unstable.obs-studio-plugins.obs-noise - pkgs.unstable.obs-studio-plugins.pixel-art - pkgs.unstable.obs-studio-plugins.obs-recursion-effect - pkgs.unstable.obs-studio-plugins.obs-retro-effects - obs-vintage-filter - ]; - }; - xdg.desktopEntries = lib.mkIf isFlexbox { - obs = { - name = "OBS Studio (NVIDIA GPU)"; - exec = "nvidia-offload obs"; - genericName = "Streaming/Recording Software"; - terminal = false; - type = "Application"; - categories = ["AudioVideo" "Recorder"]; - icon = "com.obsproject.Studio"; - startupNotify = true; - }; - }; -} diff --git a/home/obsidian/default.nix b/home/obsidian/default.nix deleted file mode 100644 index 1166a28f..00000000 --- a/home/obsidian/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/obsidian" - "Obsidian" - ]; - }; - - home.packages = [ - pkgs.obsidian - ]; -} diff --git a/home/onboard/default.nix b/home/onboard/default.nix deleted file mode 100644 index bb2c37a7..00000000 --- a/home/onboard/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -# Onboard Keyboard Layout -{pkgs, ...}: { - home.packages = [ - pkgs.onboard - ]; -} diff --git a/home/pavucontrol/default.nix b/home/pavucontrol/default.nix deleted file mode 100644 index c024944b..00000000 --- a/home/pavucontrol/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - files = [ - ".config/pavucontrol.ini" - ]; - }; - - home.packages = [ - pkgs.pavucontrol - ]; -} diff --git a/home/pgcli/default.nix b/home/pgcli/default.nix deleted file mode 100644 index d1029742..00000000 --- a/home/pgcli/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - programs.pgcli = { - enable = true; - }; -} diff --git a/home/pomodoro-gtk/default.nix b/home/pomodoro-gtk/default.nix deleted file mode 100644 index 123edf85..00000000 --- a/home/pomodoro-gtk/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{pkgs, ...}: { - home.packages = [ - pkgs.pomodoro-gtk - ]; -} diff --git a/home/portfolio-performance/default.nix b/home/portfolio-performance/default.nix deleted file mode 100644 index 507400c8..00000000 --- a/home/portfolio-performance/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".PortfolioPerformance" - ]; - }; - - home.packages = [ - pkgs.unstable.portfolio - ]; -} diff --git a/home/psql/default.nix b/home/psql/default.nix deleted file mode 100644 index e9215858..00000000 --- a/home/psql/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -{pkgs, ...}: { - home.packages = [ - pkgs.postgresql - ]; - home.file.".psqlrc".source = ./psqlrc; -} diff --git a/home/pulsemixer/default.nix b/home/pulsemixer/default.nix deleted file mode 100644 index f59d347f..00000000 --- a/home/pulsemixer/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{pkgs, ...}: { - home.file = { - ".config/pulsemixer.cfg".source = ./pulsemixer.cfg; - }; - - home.packages = [ - pkgs.pulsemixer - ]; -} diff --git a/home/qalculate/default.nix b/home/qalculate/default.nix deleted file mode 100644 index 6499e684..00000000 --- a/home/qalculate/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/qalculate" - ".local/share/qalculate" - ]; - }; - - home.packages = [ - pkgs.qalculate-gtk - ]; -} diff --git a/home/ripgrep-all/default.nix b/home/ripgrep-all/default.nix deleted file mode 100644 index 33858a52..00000000 --- a/home/ripgrep-all/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - programs.ripgrep-all = { - enable = true; - }; -} diff --git a/home/ripgrep/default.nix b/home/ripgrep/default.nix deleted file mode 100644 index b7b71fb1..00000000 --- a/home/ripgrep/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{...}: { - programs.ripgrep = { - enable = true; - arguments = [ - "--smart-case" - ]; - }; -} diff --git a/home/rofimoji/default.nix b/home/rofimoji/default.nix deleted file mode 100644 index a9ad92d5..00000000 --- a/home/rofimoji/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{pkgs, ...}: { - home.packages = [ - pkgs.rofimoji - pkgs.wtype # insert emojis directly - ]; - - xdg.configFile."rofimoji.rc".source = ./rofimoji.rc; -} diff --git a/home/satty/default.nix b/home/satty/default.nix deleted file mode 100644 index 8c6431a1..00000000 --- a/home/satty/default.nix +++ /dev/null @@ -1,36 +0,0 @@ -{ - isImpermanent, - lib, - pkgs, - ... -}: let - sattyScreenshot = pkgs.writeShellApplication { - name = "satty-screenshot"; - runtimeInputs = with pkgs; [ - satty - grim - wl-clipboard - jq - niri - ]; - text = builtins.readFile ./scripts/satty-screenshot.sh; - }; -in { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".cache/satty" - ]; - }; - - programs.satty = { - enable = true; - settings.general = { - fullscreen = true; - initial-tool = "crop"; - }; - }; - - home.packages = [ - sattyScreenshot - ]; -} diff --git a/home/showmethekey/default.nix b/home/showmethekey/default.nix deleted file mode 100644 index f868b24d..00000000 --- a/home/showmethekey/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -{pkgs, ...}: let -in { - home.packages = [ - pkgs.showmethekey - ]; -} diff --git a/home/signal/default.nix b/home/signal/default.nix deleted file mode 100644 index f2b41412..00000000 --- a/home/signal/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/Signal" - ]; - }; - - home.packages = [ - pkgs.signal-desktop - ]; -} diff --git a/home/solaar/default.nix b/home/solaar/default.nix deleted file mode 100644 index 285c1c3a..00000000 --- a/home/solaar/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/solaar" - ]; - }; - - home.packages = [ - pkgs.hicolor-icon-theme - pkgs.solaar - ]; -} diff --git a/home/sound-switcher/default.nix b/home/sound-switcher/default.nix deleted file mode 100644 index 131e98a2..00000000 --- a/home/sound-switcher/default.nix +++ /dev/null @@ -1,15 +0,0 @@ -# Rofi-based sound switcher -{ - pkgs, - osConfig, - ... -}: let - isNumenor = osConfig.networking.hostName == "numenor"; - sound-switcher = pkgs.writers.writeBashBin "sound-switcher" ( - if isNumenor - then (builtins.readFile ./scripts/sound-switcher-numenor.sh) - else (builtins.readFile ./scripts/sound-switcher-flexbox.sh) - ); -in { - home.packages = [sound-switcher]; -} diff --git a/home/ssh/README.md b/home/ssh/README.md deleted file mode 100644 index 4b394827..00000000 --- a/home/ssh/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# SSH Agent Setup with GNOME Keyring - -This configuration sets up SSH agent integration with GNOME Keyring on your Niri NixOS system. This allows SSH key passphrases to be stored securely in GNOME Keyring and automatically provided when needed. - -## How it works - -1. **GNOME Keyring SSH Agent**: Instead of using the default ssh-agent, we use GNOME Keyring's SSH agent component which integrates with the keyring for secure passphrase storage. - -2. **Automatic Key Addition**: SSH is configured with `AddKeysToAgent yes` so keys are automatically added to the agent when first used. - -3. **Persistent Storage**: Passphrases are stored in GNOME Keyring and will be remembered between sessions. - -## Usage - -### First Time Setup - -1. **Rebuild your NixOS configuration**: - ```bash - sudo nixos-rebuild switch --flake . - ``` - -2. **Restart your session** or manually start the SSH agent: - ```bash - systemctl --user start gnome-keyring-ssh - ``` - -3. **Add your SSH keys** (optional, they'll be added automatically when first used): - ```bash - ssh-add-all # Adds all SSH keys from ~/.ssh - ``` - -### Daily Usage - -- **First SSH connection**: You'll be prompted to enter your key passphrase once per session -- **Subsequent connections**: No passphrase required - GNOME Keyring provides it automatically -- **Unlocking keyring**: If prompted to unlock the keyring, enter your user password - -### Useful Commands - -- **List loaded keys**: `ssh-add -l` -- **Add all keys manually**: `ssh-add-all` -- **Add specific key**: `ssh-add ~/.ssh/id_ed25519` -- **Remove all keys**: `ssh-add -D` -- **Check agent status**: `systemctl --user status gnome-keyring-ssh` - -### Troubleshooting - -If SSH agent isn't working: - -1. **Check if service is running**: - ```bash - systemctl --user status gnome-keyring-ssh - ``` - -2. **Check environment variable**: - ```bash - echo $SSH_AUTH_SOCK - # Should show: /run/user/1000/keyring/ssh - ``` - -3. **Restart the service**: - ```bash - systemctl --user restart gnome-keyring-ssh - ``` - -4. **Unlock GNOME Keyring**: - - Run `seahorse` (GNOME Passwords and Keys) - - Unlock the "Default" keyring if locked - -### Security Notes - -- Passphrases are stored encrypted in GNOME Keyring -- The keyring is protected by your user login password -- Keys are automatically removed from the agent when you log out -- You can view and manage stored passphrases using Seahorse (GNOME Passwords and Keys) diff --git a/home/ssh/default.nix b/home/ssh/default.nix deleted file mode 100644 index cf11f59e..00000000 --- a/home/ssh/default.nix +++ /dev/null @@ -1,36 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: let - ssh-add-all = pkgs.writeShellApplication { - name = "ssh-add-all"; - runtimeInputs = with pkgs; [openssh coreutils]; - text = builtins.readFile ./ssh-add-all.sh; - }; -in { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".ssh" - ]; - }; - - home.packages = [ssh-add-all]; - - programs.ssh = { - enable = true; - enableDefaultConfig = false; - matchBlocks."*" = { - addKeysToAgent = "yes"; - }; - }; - - # Disable default ssh-agent since we use gcr-ssh-agent (via services.gnome.gnome-keyring) - services.ssh-agent.enable = false; - - # Point to the new gcr SSH agent socket (NixOS 25.11+) - home.sessionVariables = { - SSH_AUTH_SOCK = "$XDG_RUNTIME_DIR/gcr/ssh"; - }; -} diff --git a/home/starship/default.nix b/home/starship/default.nix deleted file mode 100644 index 3825d636..00000000 --- a/home/starship/default.nix +++ /dev/null @@ -1,128 +0,0 @@ -{ - isImpermanent, - lib, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".cache/starship" - ]; - }; - - programs.starship = { - enable = true; - - enableTransience = true; - - settings = { - format = lib.concatStrings [ - "$username" - "$hostname" - "$localip" - "$shlvl" - "$singularity" - "$directory" - "$vcsh" - "$docker_context" - "$package" - "$buf" - "$c" - "$cmake" - "$cobol" - "$container" - "$daml" - "$dart" - "$deno" - "$dotnet" - "$elixir" - "$elm" - "$erlang" - "$golang" - "$haskell" - "$helm" - "$java" - "$julia" - "$kotlin" - "$lua" - "$nim" - "$nodejs" - "$ocaml" - "$perl" - "$php" - "$pulumi" - "$purescript" - "$python" - "$rlang" - "$red" - "$ruby" - "$rust" - "$scala" - "$swift" - "$terraform" - "$vlang" - "$vagrant" - "$zig" - "$nix_shell" - "$conda" - "$spack" - "$memory_usage" - "$aws" - "$gcloud" - "$kubernetes" - "$openstack" - "$azure" - "$env_var" - "$crystal" - "$custom" - "$sudo" - "$cmd_duration" - "$line_break" - "$jobs" - "$battery" - "$time" - "$status" - "$shell" - "$character" - ]; - - aws = { - disabled = true; - }; - - gcloud = { - disabled = true; - format = "on [$symbol$account(@$domain)|($project)](green) "; - }; - - kubernetes = { - disabled = false; - style = "green"; - contexts = [ - { - "context_pattern" = "kind-kind"; - "context_alias" = "kind"; - } - ]; - }; - - shell = { - disabled = false; - fish_indicator = "🐟"; - bash_indicator = "💩"; - nu_indicator = "🦀"; - }; - - status = { - disabled = false; - map_symbol = true; - pipestatus = false; - }; - - nix_shell = { - disabled = true; - format = "via [$symbol$state]($style) "; - impure_msg = ""; - }; - }; - }; -} diff --git a/home/stylix/default.nix b/home/stylix/default.nix deleted file mode 100644 index 35c0bb79..00000000 --- a/home/stylix/default.nix +++ /dev/null @@ -1,19 +0,0 @@ -{pkgs, ...}: { - home.packages = [ - pkgs.papirus-icon-theme - ]; - stylix = { - iconTheme = { - enable = true; - package = pkgs.papirus-icon-theme; - light = "Papirus-Light"; - dark = "Papirus-Dark"; - }; - targets = { - firefox = { - profileNames = ["main"]; - }; - neovim.enable = false; - }; - }; -} diff --git a/home/syncthing/default.nix b/home/syncthing/default.nix deleted file mode 100644 index 452f1be5..00000000 --- a/home/syncthing/default.nix +++ /dev/null @@ -1,30 +0,0 @@ -{ - isImpermanent, - lib, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".local/state/syncthing" # device keys and certificates - ".config/syncthing" # pre-v1.27.0 uses this instead of $XDG_STATE_HOME above, keeping for backward-compatibility - ]; - files = [ - ".config/syncthingtray.ini" - ]; - }; - - services.syncthing = { - enable = true; - tray = { - enable = true; - command = "syncthingtray --wait"; - }; - }; - - systemd.user.services.syncthingtray = { - Service = { - Restart = "on-failure"; - RestartSec = 5; - }; - }; -} diff --git a/home/systemd-errors-and-warnings-counter/default.nix b/home/systemd-errors-and-warnings-counter/default.nix deleted file mode 100644 index 2f962c2e..00000000 --- a/home/systemd-errors-and-warnings-counter/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -# Display number of systemd errors and warnings in last 10 minutes -{pkgs, ...}: let - systemd-errors-and-warnings-counter = pkgs.writeShellApplication { - name = "systemd-errors-and-warnings-counter"; - runtimeInputs = [pkgs.systemd pkgs.coreutils]; - text = builtins.readFile ./scripts/systemd-errors-and-warnings-counter.sh; - }; -in { - home.packages = [systemd-errors-and-warnings-counter]; -} diff --git a/home/tealdeer/default.nix b/home/tealdeer/default.nix deleted file mode 100644 index d31d3eba..00000000 --- a/home/tealdeer/default.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ - lib, - isImpermanent, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".cache/tealdeer" - ]; - }; - - programs.tealdeer = { - enable = true; - settings = { - updates = { - auto_update = true; - }; - }; - }; -} diff --git a/home/telegram/default.nix b/home/telegram/default.nix deleted file mode 100644 index 16139d0e..00000000 --- a/home/telegram/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".local/share/TelegramDesktop" - ]; - }; - - home.packages = [ - pkgs.telegram-desktop - ]; -} diff --git a/home/television/default.nix b/home/television/default.nix deleted file mode 100644 index 1d9c2acc..00000000 --- a/home/television/default.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ - isImpermanent, - lib, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/television/cable" - ]; - }; - - programs.television = { - enable = true; - }; - programs.nix-search-tv = { - enable = true; - }; -} diff --git a/home/tomat/default.nix b/home/tomat/default.nix deleted file mode 100644 index d28be98f..00000000 --- a/home/tomat/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - services.tomat = { - enable = true; - }; -} diff --git a/home/trash-cli/default.nix b/home/trash-cli/default.nix deleted file mode 100644 index e235870a..00000000 --- a/home/trash-cli/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{pkgs, ...}: { - home.packages = [ - pkgs.trash-cli - ]; -} diff --git a/home/tray-tui/default.nix b/home/tray-tui/default.nix deleted file mode 100644 index 7f4fc046..00000000 --- a/home/tray-tui/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - programs.tray-tui = { - enable = true; - }; -} diff --git a/home/udiskie/default.nix b/home/udiskie/default.nix deleted file mode 100644 index 2704edbf..00000000 --- a/home/udiskie/default.nix +++ /dev/null @@ -1,27 +0,0 @@ -# Indicator icon for automounting USB drives -{ - lib, - pkgs, - ... -}: { - services.udiskie = { - enable = true; - automount = false; - }; - - # Ensure udiskie starts after the Niri session is up, to avoid tray race conditions - systemd.user.services.udiskie = { - Unit = { - After = ["niri.service" "graphical-session.target"]; - Wants = ["graphical-session.target"]; - # Relax environment conditions in case defaults are too strict for Niri - ConditionEnvironment = lib.mkForce []; - }; - Service = { - # Small delay to ensure Wayland environment and tray are ready - ExecStartPre = "${pkgs.coreutils}/bin/sleep 2"; - Restart = lib.mkForce "on-failure"; - RestartSec = "5"; - }; - }; -} diff --git a/home/urxvt/default.nix b/home/urxvt/default.nix deleted file mode 100644 index 9e2ddafa..00000000 --- a/home/urxvt/default.nix +++ /dev/null @@ -1,38 +0,0 @@ -{pkgs, ...}: { - programs.urxvt = { - enable = true; - - extraConfig = { - # Perl extensions - perl-ext-common = "default,matcher,resize-font,vtwheel,keyboard-select,-searchable-scrollback"; - - # Matcher (clickable URLs) - url-launcher = "${pkgs.xdg-utils}/bin/xdg-open"; - "matcher.button" = 1; - - # Messes with CTRL+SHIFT Keybindings, see https://wiki.archlinux.org/index.php/Rxvt-unicode#Perl_extensions - iso14755_52 = false; - - # https://github.com/muennich/urxvt-perls#keyboard-select - "keyboard-select.clipboard" = true; - }; - - fonts = ["xft:FiraCode Nerd Font Mono:size=8"]; - - # Messes with CTRL+SHIFT Keybindings, see https://wiki.archlinux.org/index.php/Rxvt-unicode#Perl_extensions - iso14755 = false; - - keybindings = { - "Shift-Control-C" = "eval:selection_to_clipboard"; - "Shift-Control-V" = "eval:paste_clipboard"; - - # https://github.com/muennich/urxvt-perls#keyboard-select - "Meta-Escape" = "perl:keyboard-select:activate"; - "Meta-Shift-S" = "perl:keyboard-select:search"; - }; - - package = pkgs.rxvt-unicode; - - scroll.bar.enable = false; - }; -} diff --git a/home/variety/default.nix b/home/variety/default.nix deleted file mode 100644 index 2190de6f..00000000 --- a/home/variety/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{pkgs, ...}: { - home.packages = [ - pkgs.variety - ]; - - home.file = { - ".config/variety/variety.conf".source = ./variety.conf; - }; -} diff --git a/home/virtual-cable/default.nix b/home/virtual-cable/default.nix deleted file mode 100644 index fb70f452..00000000 --- a/home/virtual-cable/default.nix +++ /dev/null @@ -1,21 +0,0 @@ -# Virtual inputs/outputs via Pipewire (for OBS and beyond) -{pkgs, ...}: let - obs-mic = pkgs.writers.writeBashBin "obs-mic" (builtins.readFile ./scripts/obs-mic.sh); -in { - home.packages = [obs-mic]; - - systemd.user.services.obs-mic = { - Unit = { - After = ["wireplumber.service"]; - Description = "Set up virtualMic and virtualSpeaker for OBS"; - Requires = ["wireplumber.service"]; - }; - Install.WantedBy = ["wireplumber.service"]; - Service = { - Environment = "PATH=$PATH:/run/current-system/sw/bin"; - ExecStartPre = "${pkgs.coreutils}/bin/sleep 5"; # TODO: Find a better way to wait for WirePlumber to fully start - ExecStart = "${obs-mic}/bin/obs-mic"; - Type = "oneshot"; - }; - }; -} diff --git a/home/vlc/default.nix b/home/vlc/default.nix deleted file mode 100644 index c1ffcb7e..00000000 --- a/home/vlc/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/vlc" - ".local/share/vlc" - ]; - }; - - home.packages = [ - pkgs.vlc - ]; -} diff --git a/home/waybar/default.nix b/home/waybar/default.nix deleted file mode 100644 index 8c8a2f71..00000000 --- a/home/waybar/default.nix +++ /dev/null @@ -1,578 +0,0 @@ -{ - config, - lib, - isNvidia, - osConfig, - pkgs, - ... -}: let - dunst-dnd-waybar = pkgs.writeShellApplication { - name = "dunst-dnd-waybar"; - runtimeInputs = [pkgs.dunst]; - text = builtins.readFile ./scripts/dunst-dnd-waybar.sh; - }; - hostName = osConfig.networking.hostName; - isNumenor = hostName == "numenor"; - isFlexbox = hostName == "flexbox"; - - leftSection = { - modules-left = [ - "niri/workspaces" - ]; - }; - - centerSection = { - modules-center = [ - "systemd-failed-units" - "group/cpu" - "memory" - "disk" - "group/gpu" - "group/network" - "group/screens" - "group/audio" - "bluetooth" - "group/power" - ]; - - systemd-failed-units = { - format = " {nr_failed} failed"; - format-ok = " 0"; - hide-on-ok = false; - on-click = "alacritty --command journalctl --pager-end --catalog --boot --priority 3..3 | lnav"; - on-click-right = "alacritty --command isd"; - }; - - "group/cpu" = { - modules = [ - "cpu" - "temperature#cpu" - "custom/cpu-profile-toggler" - ]; - orientation = "inherit"; - }; - - cpu = { - format = " {usage}%"; - states = { - info = 80; - }; - on-click = "alacritty --command btop"; - on-click-right = "alacritty --command btop"; - }; - - "temperature#cpu" = { - critical-threshold = 90; - format = " {temperatureC}°C"; - on-click = "alacritty --command btop"; - on-click-right = "alacritty --command btop"; - hwmon-path = - if isNumenor - then "/sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon2/temp1_input" - else "/sys/devices/platform/coretemp.0/hwmon/hwmon7/temp1_input"; - }; - - "custom/cpu-profile-toggler" = { - format = "{icon}"; - format-icons = { - performance = ""; - powersave = ""; - }; - exec = "cpu-profile-toggler"; - on-click = "auto-cpufreq-gtk"; - on-click-middle = "cpu-profile-toggler --toggle"; - on-click-right = "cpu-profile-toggler --reset"; - return-type = "json"; - interval = 5; - }; - - memory = { - format = " {percentage}%"; - states = { - warning = 60; - critical = 80; - }; - on-click = "alacritty --command btop"; - on-click-right = "alacritty --command btop"; - tooltip-format = "{used:0.1f}GiB mem used, {swapUsed:0.1f}GiB swap used"; - }; - - disk = { - format = " {percentage_used}%"; - on-click = "alacritty --command ncdu /"; - on-click-right = "alacritty --command btop"; - states = { - warning = 60; - critical = 80; - }; - }; - - "group/gpu" = { - modules = - [ - ( - if isNvidia - then "custom/nvidia" - else "custom/gpu-usage" - ) - ] - ++ lib.optional (!isNvidia) "temperature#gpu"; - orientation = "inherit"; - }; - - "custom/gpu-usage" = { - exec = "cat /sys/class/hwmon/hwmon1/device/gpu_busy_percent"; - format = " {}%"; - return-type = ""; - interval = 1; - on-click = "alacritty --command nvtop"; - on-click-right = "alacritty --command nvtop"; - }; - - "custom/nvidia" = { - exec = "nvidia-smi --query-gpu=utilization.gpu,temperature.gpu --format=csv,nounits,noheader | sed 's/\\([0-9]\\+\\), \\([0-9]\\+\\)/\\1%  \\2°C/g'"; - format = " {}"; - interval = 2; - on-click = "alacritty --command nvtop"; - on-click-right = "alacritty --command nvtop"; - }; - - "temperature#gpu" = { - critical-threshold = 90; - on-click = "alacritty --command nvtop"; - on-click-right = "alacritty --command nvtop"; - hwmon-path = - if isNumenor - then "/sys/devices/pci0000:00/0000:00:01.1/0000:01:00.0/0000:02:00.0/0000:03:00.0/hwmon/hwmon1/temp2_input" - else "TODO find me according to waybar docs similar to CPU temp hwmon path"; - }; - - "group/network" = { - modules = ["network" "network#tailscale" "custom/macgyver" "network#mullvad" "network#wireguard"]; - orientation = "inherit"; - }; - - "network" = { - interval = 3; - format = " "; - format-disconnected = " "; - format-ethernet = " "; - format-wifi = " "; - format-linked = " 🚧"; - format-alt = "{bandwidthDownBytes} {bandwidthUpBytes}"; - tooltip-format = "IF:{ifname} SSID:{essid} FREQ:{frequency} :{signalStrength} IP:{ipaddr} GW:{gwaddr} NM:{netmask} {bandwidthDownBytes} {bandwidthUpBytes}"; - tooltip-format-ethernet = "IF:{ifname} IP:{ipaddr} NM:{netmask} {bandwidthDownBytes} {bandwidthUpBytes}"; - tooltip-format-wifi = "IF:{ifname} SSID:{essid} FREQ:{frequency} :{signalStrength} IP:{ipaddr} GW:{gwaddr} NM:{netmask} {bandwidthDownBytes} {bandwidthUpBytes}"; - tooltip-format-linked = "IF:{ifname} IP:{ipaddr} Connected but no internet"; - on-click-right = "alacritty --command nmtui"; - }; - - "network#tailscale" = { - interface = "tailscale0"; - interval = 3; - format-linked = " "; - format = " "; - format-alt = " {bandwidthDownBytes} {bandwidthUpBytes}"; - tooltip-format = " Tailscale IP:{ipaddr} NM:{netmask} {bandwidthDownBytes} {bandwidthUpBytes}"; - tooltip-format-linked = " Tailscale down. Right click to connect."; - on-click-right = "tailscale up"; - on-click-middle = "tailscale down"; - }; - - "custom/macgyver" = { - exec = "macgyver-status"; - return-type = "json"; - interval = 3; - format = "{icon}"; - format-icons = { - up = ""; - down = ""; - error = "❌"; - }; - tooltip-format = " MacGyver is {text}"; - on-click-right = "sudo systemctl start macgyver"; - on-click-middle = "sudo systemctl stop macgyver"; - }; - - "network#mullvad" = { - interface = "wg0-mullvad"; - interval = 3; - format-disconnected = " "; - format-disabled = " "; - format-linked = " "; - format = " "; - format-alt = " {bandwidthDownBytes} {bandwidthUpBytes}"; - tooltip-format = " Mullvad IP:{ipaddr} NM:{netmask} {bandwidthDownBytes} {bandwidthUpBytes}"; - tooltip-format-linked = " Mullvad down. Right click to connect or double right click for GUI."; - tooltip-format-disabled = " Mullvad down. Rickt click to connect or double right click for GUI."; - on-click-right = "mullvad connect"; - on-double-click-right = "mullvad-gui"; - on-click-middle = "mullvad disconnect"; - }; - - "network#wireguard" = { - interface = "wg0"; - interval = 3; - format-disconnected = " "; - format-disabled = " "; - format-linked = " "; - format = " "; - format-alt = " {bandwidthDownBytes} {bandwidthUpBytes}"; - tooltip-format = " Wireguard IP:{ipaddr} GW:{gwaddr} NM:{netmask} {bandwidthDownBytes} {bandwidthUpBytes}"; - tooltip-format-linked = " Wireguard down."; - tooltip-format-disabled = " Wireguard down."; - }; - - "group/screens" = { - modules = - if isFlexbox - then ["backlight" "custom/wlsunset"] - else ["custom/ddc-backlight-left" "custom/ddc-backlight-middle" "custom/ddc-backlight-right" "custom/wlsunset"]; - orientation = "inherit"; - }; - - backlight = { - format = "{icon} {percent}%"; - format-icons = ["🌑" "🌘" "🌗" "🌖" "🌕"]; - }; - - # Direct ddcutil control - required for monitors that only expose VCP Feature 10 (Brightness) - # rather than the "Backlight Level White" feature that ddcci-driver-linux requires - # See https://gitlab.com/ddcci-driver-linux/ddcci-driver-linux - "custom/ddc-backlight-left" = { - format = "{icon} "; - tooltip-format = "Left {percentage}%"; - format-icons = ["🌑" "🌘" "🌗" "🌖" "🌕"]; - exec = "ddc-backlight 7"; # For i2c-7 - exec-on-event = false; - on-scroll-up = "flock /tmp/ddc_backlight.lock ddcutil setvcp 10 + 5 -b 7"; - on-scroll-down = "flock /tmp/ddc_backlight.lock ddcutil setvcp 10 - 5 -b 7"; - on-click = "kanshictl switch numenor-movie"; - on-click-middle = "wdisplays"; - on-click-right = "kanshictl switch numenor"; - return-type = "json"; - interval = 60; - }; - - "custom/ddc-backlight-middle" = { - format = "{icon} "; - tooltip-format = "Middle {percentage}%"; - format-icons = ["🌑" "🌘" "🌗" "🌖" "🌕"]; - exec = "ddc-backlight 9"; # For i2c-9 - exec-on-event = false; - on-scroll-up = "flock /tmp/ddc_backlight.lock ddcutil setvcp 10 + 5 -b 9"; - on-scroll-down = "flock /tmp/ddc_backlight.lock ddcutil setvcp 10 - 5 -b 9"; - on-click = "kanshictl switch numenor-movie"; - on-click-middle = "wdisplays"; - on-click-right = "kanshictl switch numenor"; - return-type = "json"; - interval = 60; - }; - - "custom/ddc-backlight-right" = { - format = "{icon}"; - tooltip-format = "Right {percentage}%"; - format-icons = ["🌑" "🌘" "🌗" "🌖" "🌕"]; - exec = "ddc-backlight 6"; # For i2c-6 - exec-on-event = false; - on-scroll-up = "flock /tmp/ddc_backlight.lock ddcutil setvcp 10 + 5 -b 6"; - on-scroll-down = "flock /tmp/ddc_backlight.lock ddcutil setvcp 10 - 5 -b 6"; - on-click = "kanshictl switch numenor-movie"; - on-click-middle = "wdisplays"; - on-click-right = "kanshictl switch numenor"; - return-type = "json"; - interval = 60; - }; - - "custom/wlsunset" = { - interval = 1; - exec = "if pgrep wlsunset >/dev/null 2>&1; then stdbuf -oL printf '{\"alt\": \"on\",\"class\": \"on\"}'; else stdbuf -oL printf '{\"alt\": \"off\",\"class\": \"off\"}'; fi"; - on-click = "wlsunset-waybar"; - return-type = "json"; - format = " {icon}"; - tooltip-format = "wlsunset: {alt}"; - signal = 1; # SIGRTMIN+1 or 35 for updating immediately from script - format-icons = { - on = ""; - off = ""; - }; - }; - - "group/audio" = { - modules = [ - "pulseaudio#in" - "pulseaudio#out" - "mpris" - ]; - orientation = "inherit"; - }; - - "pulseaudio#in" = { - format = "{format_source}"; - format-source = " {volume}%"; - format-source-muted = ""; - format-icons = { - "bluez_input.DC:69:E2:9A:6E:30" = ""; - "alsa_input.usb-Apple__Inc._USB-C_to_3.5mm_Headphone_Jack_Adapter_DWH84440324JKLTA7-00.mono-fallback" = ""; - default = ["" ""]; - }; - max-volume = 200; - scroll-step = 5; - on-scroll-up = "pactl set-source-volume @DEFAULT_SOURCE@ +5%"; - on-scroll-down = "pactl set-source-volume @DEFAULT_SOURCE@ -5%"; - on-click = "pavucontrol --tab=4"; - on-click-right = "alacritty --command pulsemixer"; - on-click-middle = "pactl set-source-mute @DEFAULT_SOURCE@ toggle"; - tooltip-format = "{format_source}"; - }; - - "pulseaudio#out" = { - format = "{icon} {volume}%"; - format-bluetooth = "{icon} {volume}%"; - format-muted = ""; - format-icons = { - "alsa_output.usb-Generic_USB_Audio-00.analog-stereo" = ""; - "alsa_output.usb-Lenovo_ThinkPad_Thunderbolt_3_Dock_USB_Audio_000000000000-00.analog-stereo" = ""; - "bluez_output.14_3F_A6_28_DC_51.1" = ""; - "alsa_output.pci-0000_03_00.1.hdmi-stereo-extra3" = "🍿"; - "bluez_output.DC_69_E2_9A_6E_30.1" = ""; - "bluez_sink.DC_69_E2_9A_6E_30.handsfree_head_unit" = ""; - "alsa_output.pci-0000_00_1f.3-platform-sof_sdw.HiFi___ucm0003.hw_sofsoundwire_2__sink" = ""; - "alsa_output.pci-0000_00_1f.3-platform-sof_sdw.HiFi___ucm0005.hw_sofsoundwire_2__sink" = ""; - "alsa_output.pci-0000_00_1f.3-platform-sof_sdw.HiFi___ucm0007.hw_sofsoundwire_2__sink" = ""; - "alsa_output.pci-0000_00_1f.3-platform-sof_sdw.HiFi__hw_sofsoundwire_2__sink" = ""; - "alsa_output.pci-0000_00_1f.3-platform-sof_sdw.HiFi__Speaker__sink" = ""; - "alsa_output.pci-0000_00_1f.3-platform-sof_sdw.HiFi__hw_sofsoundwire__sink" = ""; - "alsa_output.usb-Apple__Inc._USB-C_to_3.5mm_Headphone_Jack_Adapter_DWH84440324JKLTA7-00.analog-stereo" = ""; - "bluez_output.34_E3_FB_C5_01_E0.1" = ""; - "bluez_sink.34_E3_FB_C5_01_E0.handsfree_head_unit" = ""; - default = ["" ""]; - }; - max-volume = 200; - on-click = "pavucontrol --tab=3"; - on-click-right = "alacritty --command pulsemixer"; - on-click-middle = "pactl set-sink-mute @DEFAULT_SINK@ toggle"; - ignored-sinks = ["Easy Effects Sink"]; - }; - - mpris = { - format = "{player_icon}"; - format-paused = "{status_icon}"; - on-click-right = ''niri msg action focus-window --id $(niri msg --json windows | jq -r '.[] | select(.app_id == "YouTube Music Desktop App") | .id')''; - player-icons = { - default = "▶"; - mpv = ""; - chromium = ""; - }; - status-icons = { - paused = "⏸"; - }; - }; - - bluetooth = { - format = "{icon}"; - format-connected = "{icon} {num_connections}"; - format-connnected-battery = "{icon} {num_connections} {device_battery_percentage}%"; - format-icons = { - connected = ""; - on = ""; - off = ""; - disabled = ""; - disconnected = ""; - default = ""; - }; - on-click = "bluetoothctl power on"; - on-click-right = "bluetoothctl power off"; - on-click-middle = "blueman-manager"; - tooltip-format = "{status} {num_connections}"; - }; - - "group/power" = { - modules = [ - "battery" - ]; - orientation = "inherit"; - }; - - battery = { - events = { - on-discharging-warning = "notify-send -u normal 'Low Battery'"; - on-discharging-critical = "notify-send -u critical 'Very Low Battery'"; - on-charging-100 = "notify-send -u normal 'Battery is full'"; - }; - states = { - warning = 30; - critical = 15; - }; - format = " {icon} {capacity}%"; - format-icons = ["" "" "" "" ""]; - tooltip = true; - backend = "upower"; - }; - }; - - rightSection = { - modules-right = [ - "privacy" - "custom/dunst-dnd" - "custom/caffeinate" - "niri/language" - "clock" - "tray" - ]; - - "custom/dunst-dnd" = { - exec = "${dunst-dnd-waybar}/bin/dunst-dnd-waybar"; - return-type = "json"; - interval = 1; - format = "{icon}{text}"; - tooltip-format = "{text}"; - format-icons = { - running = ""; - dnd = ""; - }; - on-click = "dunstctl set-paused toggle"; - on-click-right = "dunstctl set-paused false"; - }; - - "custom/caffeinate" = { - exec = "if systemctl --user is-active swayidle >/dev/null 2>&1; then echo '{\"alt\": \"deactivated\", \"class\": \"deactivated\"}'; else echo '{\"alt\": \"activated\", \"class\": \"activated\"}'; fi"; - return-type = "json"; - interval = 1; - format = " {icon}"; - format-icons = { - activated = ""; - deactivated = ""; - }; - on-click = "systemctl --user stop swayidle"; - on-click-right = "systemctl --user start swayidle"; - on-click-middle = "systemctl --user start swayidle"; - }; - - "niri/language" = { - format = "{short}"; - on-click = "niri msg action switch-layout next"; - on-click-right = "niri msg action switch-layout prev"; - on-click-middle = "niri msg action switch-layout 0"; - }; - - clock = { - format = "{:%H:%M}"; - format-alt = "{:%A, %B %d, %Y (%R)}  "; - tooltip-format = "{calendar}"; - calendar = { - mode = "year"; - mode-mon-col = 3; - weeks-pos = "right"; - on-scroll = 1; - format = { - months = "{}"; - days = "{}"; - weeks = "W{}"; - weekdays = "{}"; - today = "{}"; - }; - }; - actions = { - on-click-right = "mode"; - on-scroll-up = "shift_up"; - on-scroll-down = "shift_down"; - }; - on-click-middle = "brave calendar.google.com"; - }; - - tray = { - show-passive-items = true; - spacing = 3; - }; - }; -in { - programs.waybar = { - enable = true; - settings = { - main = - { - layer = "top"; - position = "bottom"; - expand-center = true; - output = [ - "DP-1" # Numenor main screen - "eDP-1" # Flexbox - ]; - } - // leftSection // centerSection // rightSection; - - aux = - { - layer = "top"; - position = "bottom"; - output = [ - "HDMI-A-1" - "HDMI-A-2" - ]; - } - // leftSection // rightSection; - }; - - style = '' - .info { - color: @base0D; - } - .critical { - color: @base08; - } - .warning { - color: @base0A; - } - - #systemd-failed-units.degraded { - color: @base08; - } - - #network.ethernet, - #network.wifi { - color: @base0B; - } - - #custom-dunst-dnd.dnd { - color: @base0A; - } - - #custom-macgyver.up { - color: @base0B; - } - - #custom-wlsunset.off { - color: @base0A; - } - - #custom-caffeinate.activated { - color: @base0A; - } - - #language:not(.us) { - color: @base0A; - } - - /* Can remove once https://github.com/nix-community/stylix/pull/1919 is merged */ - tooltip label { - color: @base05; - } - ''; - - systemd = { - enable = true; - enableDebug = false; - enableInspect = false; - }; - }; - - systemd.user.services.waybar = { - # Give on-click commands access to binaries they need - Service.Environment = lib.mkForce "PATH=/run/wrappers/bin:${config.home.profileDirectory}/bin:/run/current-system/sw/bin"; - # Fix for niri startup - Install.WantedBy = lib.mkForce ["niri.service"]; - Unit.Requires = ["niri.service"]; - Unit.After = ["niri.service"]; - }; -} diff --git a/home/witr/default.nix b/home/witr/default.nix deleted file mode 100644 index ed9fe501..00000000 --- a/home/witr/default.nix +++ /dev/null @@ -1,3 +0,0 @@ -{pkgs, ...}: { - home.packages = [pkgs.unstable.witr]; -} diff --git a/home/wlsunset/default.nix b/home/wlsunset/default.nix deleted file mode 100644 index e97f6751..00000000 --- a/home/wlsunset/default.nix +++ /dev/null @@ -1,14 +0,0 @@ -# Day/night gamma adjustments for Wayland -{pkgs, ...}: let - # https://github.com/CyrilSLi/linux-scripts/blob/main/waybar/wlsunset.sh - wlsunset-waybar = pkgs.writeShellApplication { - name = "wlsunset-waybar"; - runtimeInputs = with pkgs; [wlsunset procps killall]; - text = builtins.readFile ./scripts/wlsunset-waybar.sh; - }; -in { - home.packages = [ - pkgs.wlsunset - wlsunset-waybar - ]; -} diff --git a/home/wluma/default.nix b/home/wluma/default.nix deleted file mode 100644 index bb053958..00000000 --- a/home/wluma/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -# Automatic brightness adjustment based on screen contents and ALS -{ - lib, - osConfig, - ... -}: let - isFlexbox = osConfig.networking.hostName == "flexbox"; -in { - services.wluma = lib.mkIf isFlexbox { - enable = true; - }; -} diff --git a/home/xdg/default.nix b/home/xdg/default.nix deleted file mode 100644 index 44e0479b..00000000 --- a/home/xdg/default.nix +++ /dev/null @@ -1,62 +0,0 @@ -{ - isImpermanent, - lib, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".xournal" # Other recently used files - ]; - # TODO: This somehow tries to replace the file after it's created by something else - # See https://github.com/nix-community/impermanence/issues/147 - # files = [ - # ".local/share/recently-used.xbel" # Recently used files - # ]; - }; - - home.packages = [ - pkgs.selectdefaultapplication # GUI XDG Default Application Chooser - ]; - - home.sessionVariables = { - XDG_CONFIG_HOME = "/home/farlion/.config"; - }; - - home.preferXdgDirectories = true; - - xdg = { - mimeApps = { - associations = { - added = { - "x-scheme-handler/tg" = "org.telegram.desktop.desktop"; - }; - }; - enable = true; - defaultApplications = { - "application/pdf" = ["okular.desktop"]; - "applications/x-www-browser" = ["brave-browser.desktop"]; - "image/bmp" = ["oculante.desktop"]; - "image/gif" = ["oculante.desktop"]; - "image/jpeg" = ["oculante.desktop"]; - "image/png" = ["oculante.desktop"]; - "image/svg+xml" = ["oculante.desktop"]; - "image/tiff" = ["oculante.desktop"]; - "image/webp" = ["oculante.desktop"]; - "inode/directory" = ["lf.desktop"]; - "text/html" = ["brave-browser.desktop"]; - "text/plain" = ["nvim.desktop"]; - "x-scheme-handler/about" = ["brave-browser.desktop"]; - "x-scheme-handler/http" = ["brave-browser.desktop"]; - "x-scheme-handler/https" = ["brave-browser.desktop"]; - "x-scheme-handler/mailto" = ["brave-browser.desktop"]; - "x-scheme-handler/msteams" = ["teams-for-linux.desktop"]; - "x-scheme-handler/slack" = ["slack.desktop"]; - "x-scheme-handler/tg" = ["org.telegram.desktop.desktop"]; - "x-scheme-handler/tonsite" = ["org.telegram.desktop.desktop"]; - "x-scheme-handler/unknown" = ["brave-browser.desktop"]; - "x-scheme-handler/webcal" = ["brave-browser.desktop"]; - }; - }; - }; -} diff --git a/home/ytmdesktop/default.nix b/home/ytmdesktop/default.nix deleted file mode 100644 index 0b94684e..00000000 --- a/home/ytmdesktop/default.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".config/YouTube Music Desktop App" - ]; - }; - - home.packages = [ - pkgs.unstable.ytmdesktop - ]; - - # Override the desktop file to add --password-store flag for Last.fm integration - # See: https://github.com/ytmdesktop/ytmdesktop/issues/1428 - xdg.desktopEntries.ytmdesktop = { - name = "YouTube Music Desktop App"; - genericName = "Music Player"; - comment = "YouTube Music Desktop App"; - exec = "${pkgs.unstable.ytmdesktop}/bin/ytmdesktop --password-store=\"gnome-libsecret\" %U"; - icon = "ytmdesktop"; - terminal = false; - type = "Application"; - categories = ["Audio" "AudioVideo" "Player"]; - mimeType = ["x-scheme-handler/ytmd"]; - startupNotify = true; - }; -} diff --git a/home/yubico/default.nix b/home/yubico/default.nix deleted file mode 100644 index eb6fcce3..00000000 --- a/home/yubico/default.nix +++ /dev/null @@ -1,19 +0,0 @@ -{pkgs, ...}: { - imports = [ - ./modules/yubikey-touch-detector - ]; - - home.packages = with pkgs; [ - pam_u2f # U2F (via yubikey) support for PAM - yubikey-manager # ykman - yubioath-flutter # Yubikey management GUI - ]; - - services = { - yubikey-touch-detector.enable = true; - dunst.settings.yubikey_touch_detector_icon = { - summary = "YubiKey is waiting for a touch"; - new_icon = "${pkgs.yubikey-touch-detector}/share/icons/hicolor/128x128/apps/yubikey-touch-detector.png"; - }; - }; -} diff --git a/home/zen/default.nix b/home/zen/default.nix deleted file mode 100644 index ae87bc7f..00000000 --- a/home/zen/default.nix +++ /dev/null @@ -1,54 +0,0 @@ -{ - lib, - isImpermanent, - inputs, - config, - pkgs, - ... -}: let - # Try to focus an existing Zen window on link open so the workspace comes to the foreground - zenNiriOpen = pkgs.writeShellApplication { - name = "zen-niri-open"; - runtimeInputs = [pkgs.niri pkgs.jq pkgs.coreutils config.programs.zen-browser.package]; - text = '' - zen_id=$(niri msg --json windows | jq -r '.[] | select(.app_id == "zen-browser" or .app_id == "zen") | .id' | head -n1 || true) - if [ -n "''${zen_id:-}" ]; then - niri msg action focus-window --id "$zen_id" >/dev/null 2>&1 || true - fi - exec ${config.programs.zen-browser.package}/bin/zen "$@" - ''; - }; -in { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".zen" - ]; - }; - - home.packages = [zenNiriOpen]; - - imports = [ - inputs.zen-browser.homeModules.beta - ]; - - programs.zen-browser = { - enable = true; - profiles = { - main = { - extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [ - bitwarden - darkreader - ublock-origin - ]; - }; - }; - }; - - stylix.targets.zen-browser.profileNames = ["main"]; - - home.sessionVariables = { - BROWSER = "${zenNiriOpen}/bin/zen-niri-open"; - DEFAULT_BROWSER = "${zenNiriOpen}/bin/zen-niri-open"; - MOZ_LEGACY_PROFILES = 1; # Temporary fix, see https://github.com/0xc000022070/zen-browser-flake/issues/179 - }; -} diff --git a/home/zoom/default.nix b/home/zoom/default.nix deleted file mode 100644 index 26de6945..00000000 --- a/home/zoom/default.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - files = [ - ".config/zoom.conf" - ".config/zoomus.conf" - ]; - directories = [ - ".zoom" - ".cache/zoom" - ]; - }; - - home.packages = [ - pkgs.zoom-us - ]; -} diff --git a/home/zoxide/default.nix b/home/zoxide/default.nix deleted file mode 100644 index fdc4e5b3..00000000 --- a/home/zoxide/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ - lib, - isImpermanent, - ... -}: { - home.persistence."/persist" = lib.mkIf isImpermanent { - directories = [ - ".cache/zoxide" # Some stuff for nushell - ".local/share/zoxide" # Zoxide DB - ]; - }; - - programs.zoxide = { - enable = true; - }; -} diff --git a/machines/flexbox/system.nix b/machines/flexbox/system.nix deleted file mode 100644 index 453fa57c..00000000 --- a/machines/flexbox/system.nix +++ /dev/null @@ -1,51 +0,0 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page -# and in the NixOS manual (accessible by running ‘nixos-help’). -{...}: { - # https://lore.kernel.org/linux-nvme/YnR%2FFiWbErNGXIx+@kbusch-mbp/T/ - boot.kernelParams = [ - "nvme_core.default_ps_max_latency_us=0" # Stability probs - "acpiphp.disable=1" # NVME disk power management probs - ]; - - # GPU - hardware.nvidia.prime = { - offload = { - enable = true; - enableOffloadCmd = true; - }; - intelBusId = "PCI:0:2:0"; - nvidiaBusId = "PCI:1:0:0"; - }; - - # Disable power saving for fixing Comet Lake Audio Card (SOF) breaking after suspend - services.udev.extraRules = '' - ACTION=="add|change", SUBSYSTEM=="pci", KERNELS=="0000:00:1f.3", ATTR{power/control}="on" - ''; - - # LVM on LUKS - boot.initrd.luks.devices = { - root = { - device = "/dev/disk/by-uuid/ae713884-749b-4edb-adbc-b16fe447e956"; - preLVM = true; - }; - }; - - networking.hostName = "flexbox"; - - # The global useDHCP flag is deprecated, therefore explicitly set to - # false here. Per-interface useDHCP will be mandatory in the future, - # so this generated config replicates the default behaviour. - networking.useDHCP = false; - #networking.interfaces.enp164s0u1.useDHCP = true; - networking.interfaces.wlp0s20f3.useDHCP = true; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database - # versions on your system were taken. It‘s perfectly fine and - # recommended to leave this value at the release version of the first - # install of this system. Before changing this value read the - # documentation for this option (e.g. man configuration.nix or on - # https://nixos.org/nixos/options.html). - system.stateVersion = "22.05"; # Did you read the comment? -} diff --git a/machines/numenor/system.nix b/machines/numenor/system.nix deleted file mode 100644 index 3342933a..00000000 --- a/machines/numenor/system.nix +++ /dev/null @@ -1,55 +0,0 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page, on -# https://search.nixos.org/options and in the NixOS manual (`nixos-help`). -{pkgs, ...}: { - # LVM on LUKS - boot.initrd.luks.devices = { - root = { - device = "/dev/nvme0n1p6"; - preLVM = true; - }; - }; - - # Needed for ddcutil - hardware.i2c.enable = true; - - # Plenty of RAM so... - boot.tmp.useTmpfs = true; - - networking.useDHCP = false; - networking.interfaces.enp74s0.useDHCP = true; - - networking.hostName = "numenor"; - - # Disable Wifi at boot - systemd.services.disable-wifi = { - enable = true; - description = "Disable Wi-Fi at boot"; - after = ["network.target" "NetworkManager.service"]; - wantedBy = ["multi-user.target"]; - path = with pkgs; [networkmanager]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${pkgs.networkmanager}/bin/nmcli radio wifi off"; - }; - }; - - # This option defines the first version of NixOS you have installed on this particular machine, - # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions. - # - # Most users should NEVER change this value after the initial install, for any reason, - # even if you've upgraded your system to a new NixOS release. - # - # This value does NOT affect the Nixpkgs version your packages and OS are pulled from, - # so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how - # to actually do that. - # - # This value being lower than the current NixOS release does NOT mean your system is - # out of date, out of support, or vulnerable. - # - # Do NOT change this value unless you have manually inspected all the changes it would make to your configuration, - # and migrated your data accordingly. - # - # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion . - system.stateVersion = "24.11"; # Did you read the comment? -} diff --git a/nix/default.nix b/nix/default.nix deleted file mode 100644 index 0b9cdb4a..00000000 --- a/nix/default.nix +++ /dev/null @@ -1,26 +0,0 @@ -{pkgs, ...}: { - nix = { - settings = { - # trusted users for pulling from caches - trusted-users = ["root" "farlion" "@wheel" "@sudo"]; - substituters = [ - "https://cache.nixos.org/" - ]; - trusted-public-keys = [ - ]; - }; - - extraOptions = '' - experimental-features = nix-command flakes - ''; - - nixPath = [ - "nixpkgs=${pkgs.path}" - "nixos-unstable=${pkgs.unstable.path}" - ]; - }; - - nixpkgs = { - config.allowUnfree = true; - }; -} diff --git a/parts/_hosts/flexbox/default.nix b/parts/_hosts/flexbox/default.nix new file mode 100644 index 00000000..9adfaaea --- /dev/null +++ b/parts/_hosts/flexbox/default.nix @@ -0,0 +1,38 @@ +{config, lib, pkgs, ...}: { + imports = [./hardware-scan.nix]; + + # Flexbox-specific settings + boot.kernelParams = [ + "nvme_core.default_ps_max_latency_us=0" + "acpiphp.disable=1" + ]; + + # NVIDIA GPU settings + hardware.nvidia.prime = { + offload = { + enable = true; + enableOffloadCmd = true; + }; + intelBusId = "PCI:0:2:0"; + nvidiaBusId = "PCI:1:0:0"; + }; + + # SOF audio card power fix + services.udev.extraRules = '' + ACTION=="add|change", SUBSYSTEM=="pci", KERNELS=="0000:00:1f.3", ATTR{power/control}="on" + ''; + + # LVM on LUKS + boot.initrd.luks.devices = { + root = { + device = "/dev/disk/by-uuid/ae713884-749b-4edb-adbc-b16fe447e956"; + preLVM = true; + }; + }; + + networking.hostName = "flexbox"; + networking.useDHCP = false; + networking.interfaces.wlp0s20f3.useDHCP = true; + + system.stateVersion = "22.05"; +} diff --git a/machines/flexbox/hardware-scan.nix b/parts/_hosts/flexbox/hardware-scan.nix similarity index 100% rename from machines/flexbox/hardware-scan.nix rename to parts/_hosts/flexbox/hardware-scan.nix diff --git a/parts/_hosts/numenor/default.nix b/parts/_hosts/numenor/default.nix new file mode 100644 index 00000000..7764c15f --- /dev/null +++ b/parts/_hosts/numenor/default.nix @@ -0,0 +1,36 @@ +{config, lib, pkgs, ...}: { + imports = [./hardware-scan.nix]; + + # LVM on LUKS + boot.initrd.luks.devices = { + root = { + device = "/dev/nvme0n1p6"; + preLVM = true; + }; + }; + + # Needed for ddcutil + hardware.i2c.enable = true; + + # Plenty of RAM so... + boot.tmp.useTmpfs = true; + + networking.useDHCP = false; + networking.interfaces.enp74s0.useDHCP = true; + networking.hostName = "numenor"; + + # Disable Wifi at boot + systemd.services.disable-wifi = { + enable = true; + description = "Disable Wi-Fi at boot"; + after = ["network.target" "NetworkManager.service"]; + wantedBy = ["multi-user.target"]; + path = [pkgs.networkmanager]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.networkmanager}/bin/nmcli radio wifi off"; + }; + }; + + system.stateVersion = "24.11"; +} diff --git a/machines/numenor/hardware-scan.nix b/parts/_hosts/numenor/hardware-scan.nix similarity index 100% rename from machines/numenor/hardware-scan.nix rename to parts/_hosts/numenor/hardware-scan.nix diff --git a/parts/features/apps/aichat.nix b/parts/features/apps/aichat.nix new file mode 100644 index 00000000..05a3bd96 --- /dev/null +++ b/parts/features/apps/aichat.nix @@ -0,0 +1,51 @@ +{...}: { + flake.modules.homeManager.aichat = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/aichat" + ]; + }; + + programs.aichat = { + enable = true; + package = pkgs.unstable.aichat; + settings = { + model = "openai:gpt-5.1-chat-latest"; + keybindings = "vi"; + save_session = true; + compress_threshold = 0; + clients = [ + {type = "openai";} + {type = "deepseek";} + { + type = "gemini"; + patch.chat_completions.".*".body.safetySettings = [ + { + category = "HARM_CATEGORY_HARASSMENT"; + threshold = "BLOCK_NONE"; + } + { + category = "HARM_CATEGORY_HATE_SPEECH"; + threshold = "BLOCK_NONE"; + } + { + category = "HARM_CATEGORY_SEXUALLY_EXPLICIT"; + threshold = "BLOCK_NONE"; + } + { + category = "HARM_CATEGORY_DANGEROUS_CONTENT"; + threshold = "BLOCK_NONE"; + } + ]; + } + {type = "claude";} + ]; + }; + }; + }; +} diff --git a/parts/features/apps/alacritty.nix b/parts/features/apps/alacritty.nix new file mode 100644 index 00000000..62e15b1f --- /dev/null +++ b/parts/features/apps/alacritty.nix @@ -0,0 +1,70 @@ +{...}: { + flake.modules.homeManager.alacritty = {...}: { + programs.alacritty = { + enable = true; + + settings = { + cursor = { + vi_mode_style = { + shape = "Beam"; + blinking = "Always"; + }; + }; + + keyboard.bindings = [ + { + key = "Return"; + mods = "Control|Super"; + action = "SpawnNewInstance"; + } + { + key = "Escape"; + mods = "Alt"; + action = "ToggleViMode"; + } + { + key = "Semicolon"; + mode = "Vi|~Search"; + action = "Right"; + } + { + key = "L"; + mode = "Vi|~Search"; + action = "Up"; + } + { + key = "K"; + mode = "Vi|~Search"; + action = "Down"; + } + { + key = "J"; + mode = "Vi|~Search"; + action = "Left"; + } + { + key = 53; + mode = "Vi|~Search"; + mods = "Shift"; + action = "SearchBackward"; + } + ]; + + env = { + TERM = "xterm-256color"; # Better color support in some apps + }; + + scrolling = { + history = 100000; + }; + + window = { + padding = { + x = 5; + y = 4; + }; + }; + }; + }; + }; +} diff --git a/parts/features/apps/aliases.nix b/parts/features/apps/aliases.nix new file mode 100644 index 00000000..ded91862 --- /dev/null +++ b/parts/features/apps/aliases.nix @@ -0,0 +1,61 @@ +{...}: { + flake.modules.homeManager.aliases = {...}: { + home.shellAliases = { + ".." = "cd .."; + "..." = "cd ../.."; + "...." = "cd ../../.."; + "....." = "cd ../../../.."; + + as = "aichat --model openai:gpt-4o-mini-search-preview"; + ae = "aichat -e"; + + caffeinate = "systemctl --user stop swayidle"; + decaffeinate = "systemctl --user start swayidle"; + + c = "wl-copy"; + + cdn = "cd ~/nixos-config"; + cdc = "cd ~/code"; + + dira = "direnv allow"; + dird = "direnv deny"; + dirr = "direnv reload"; + dr = "direnv reload"; + + ghco = "gh pr checkout"; + ghpa = "gh pr review --approve"; + ghmr = "gh pr merge -r"; + + isdu = "isd --startup_mode=user"; + + k9s-kind = "k9s --context kind-kind"; + + kc = "kubectl"; + kc-kind = "kubectl --context kind-kind"; + + lh = "/run/current-system/sw/bin/ls -ah"; + + myip = "dig @resolver1.opendns.com ANY myip.opendns.com +short"; + + n = "nh os switch"; + + nl = "sudo nix-env --list-generations --profile /nix/var/nix/profiles/system"; + ngc = "sudo nix-env --delete-generations 30d --profile /nix/var/nix/profiles/system"; + + nsn = "nh search"; + + paste = "wl-paste"; + + ra = "systemctl restart --user pipewire"; + + rm = "trash-put"; + + stern-kind = "stern --context kind-kind"; + + tailup = "sudo tailscale up --accept-routes --accept-dns=false"; + taildown = "sudo tailscale down"; + + x = "exit"; + }; + }; +} diff --git a/parts/features/apps/ansible.nix b/parts/features/apps/ansible.nix new file mode 100644 index 00000000..b30bddd8 --- /dev/null +++ b/parts/features/apps/ansible.nix @@ -0,0 +1,7 @@ +{...}: { + flake.modules.homeManager.ansible = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".ansible"]; + }; + }; +} diff --git a/parts/features/apps/asciinema.nix b/parts/features/apps/asciinema.nix new file mode 100644 index 00000000..c3cd64fc --- /dev/null +++ b/parts/features/apps/asciinema.nix @@ -0,0 +1,7 @@ +{...}: { + flake.modules.homeManager.asciinema = {...}: { + programs.asciinema = { + enable = true; + }; + }; +} diff --git a/parts/features/apps/aws.nix b/parts/features/apps/aws.nix new file mode 100644 index 00000000..12a2918a --- /dev/null +++ b/parts/features/apps/aws.nix @@ -0,0 +1,14 @@ +{...}: { + flake.modules.homeManager.aws = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".aws"]; + }; + + home.packages = [pkgs.awscli2]; + }; +} diff --git a/parts/features/apps/bash.nix b/parts/features/apps/bash.nix new file mode 100644 index 00000000..242c5af3 --- /dev/null +++ b/parts/features/apps/bash.nix @@ -0,0 +1,26 @@ +{...}: { + flake.modules.homeManager.bash = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + files = [ + ".bash_history" + ]; + }; + + programs.bash = { + enable = true; + package = pkgs.bashInteractive; + initExtra = '' + # Ctrl-x: Copy current command line to clipboard + copy-command-line() { + printf '%s' "$READLINE_LINE" | ${pkgs.wl-clipboard}/bin/wl-copy + } + bind -x '"\C-x": copy-command-line' + ''; + }; + }; +} diff --git a/parts/features/apps/brave-browser.nix b/parts/features/apps/brave-browser.nix new file mode 100644 index 00000000..51209043 --- /dev/null +++ b/parts/features/apps/brave-browser.nix @@ -0,0 +1,54 @@ +{...}: { + flake.modules.homeManager.brave-browser = { + lib, + pkgs, + osConfig, + ... + }: let + isNvidia = osConfig.dendrix.hasNvidia; + # Try to focus an existing Brave window on link open so the workspace comes to the foreground + braveNiriOpen = pkgs.writeShellApplication { + name = "brave-niri-open"; + runtimeInputs = [pkgs.niri pkgs.jq pkgs.coreutils pkgs.brave]; + text = '' + #!/usr/bin/env bash + set -euo pipefail + brave_id=$(niri msg --json windows | jq -r '.[] | select(.app_id == "brave-browser") | .id' | head -n1 || true) + if [ -n "''${brave_id:-}" ]; then + niri msg action focus-window --id "$brave_id" >/dev/null 2>&1 || true + fi + exec ${pkgs.brave}/bin/brave ${ + if isNvidia + then "" + else "--password-store=seahorse" + } "$@" + ''; + }; + in { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/BraveSoftware" + ".cache/BraveSoftware" + ]; + }; + + home.packages = [ + pkgs.brave + ]; + + xdg.desktopEntries = { + brave-browser = { + exec = "${braveNiriOpen}/bin/brave-niri-open %U"; + name = "Brave Browser"; + comment = "Access the Internet"; + genericName = "Web Browser"; + categories = ["Network" "WebBrowser"]; + icon = "brave-browser"; + mimeType = ["application/pdf" "application/rdf+xml" "application/rss+xml" "application/xhtml+xml" "application/xhtml_xml" "application/xml" "image/gif" "image/jpeg" "image/png" "image/webp" "text/html" "text/xml" "x-scheme-handler/http" "x-scheme-handler/https" "x-scheme-handler/ipfs" "x-scheme-handler/ipns"]; + startupNotify = true; + terminal = false; + type = "Application"; + }; + }; + }; +} diff --git a/parts/features/apps/broot.nix b/parts/features/apps/broot.nix new file mode 100644 index 00000000..a4bc1129 --- /dev/null +++ b/parts/features/apps/broot.nix @@ -0,0 +1,5 @@ +{...}: { + flake.modules.homeManager.broot = {...}: { + programs.broot.enable = true; + }; +} diff --git a/parts/features/apps/btop.nix b/parts/features/apps/btop.nix new file mode 100644 index 00000000..9c54b28c --- /dev/null +++ b/parts/features/apps/btop.nix @@ -0,0 +1,5 @@ +{...}: { + flake.modules.homeManager.btop = {...}: { + programs.btop.enable = true; + }; +} diff --git a/parts/features/apps/calibre.nix b/parts/features/apps/calibre.nix new file mode 100644 index 00000000..74bd8606 --- /dev/null +++ b/parts/features/apps/calibre.nix @@ -0,0 +1,19 @@ +# Ebook reader +{...}: { + flake.modules.homeManager.calibre = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + "Calibre Library" + ".config/calibre" + ".cache/calibre" + ]; + }; + + home.packages = [pkgs.calibre]; + }; +} diff --git a/home/cpu-profile-toggler/scripts/cpu-profile-toggler.sh b/parts/features/apps/cpu-profile-toggler/_scripts/cpu-profile-toggler.sh similarity index 100% rename from home/cpu-profile-toggler/scripts/cpu-profile-toggler.sh rename to parts/features/apps/cpu-profile-toggler/_scripts/cpu-profile-toggler.sh diff --git a/parts/features/apps/cpu-profile-toggler/default.nix b/parts/features/apps/cpu-profile-toggler/default.nix new file mode 100644 index 00000000..e26e273e --- /dev/null +++ b/parts/features/apps/cpu-profile-toggler/default.nix @@ -0,0 +1,12 @@ +# Toggle CPU profiles +{...}: { + flake.modules.homeManager.cpu-profile-toggler = {pkgs, ...}: let + cpu-profile-toggler = pkgs.writeShellApplication { + name = "cpu-profile-toggler"; + runtimeInputs = with pkgs; [gnugrep auto-cpufreq linuxKernel.packages.linux_zen.cpupower]; + text = builtins.readFile ./_scripts/cpu-profile-toggler.sh; + }; + in { + home.packages = [cpu-profile-toggler]; + }; +} diff --git a/parts/features/apps/ddc-backlight/default.nix b/parts/features/apps/ddc-backlight/default.nix new file mode 100644 index 00000000..f4dfb129 --- /dev/null +++ b/parts/features/apps/ddc-backlight/default.nix @@ -0,0 +1,12 @@ +{...}: { + flake.modules.homeManager.ddc-backlight = {lib, osConfig, pkgs, ...}: let + ddc-backlight = pkgs.writeShellApplication { + name = "ddc-backlight"; + runtimeInputs = [pkgs.ddcutil pkgs.coreutils pkgs.util-linux]; + text = builtins.readFile ./scripts/ddc-backlight.sh; + }; + in + lib.mkIf (!osConfig.dendrix.isLaptop) { + home.packages = [ddc-backlight pkgs.ddcutil]; + }; +} diff --git a/home/ddc-backlight/scripts/ddc-backlight.sh b/parts/features/apps/ddc-backlight/scripts/ddc-backlight.sh similarity index 100% rename from home/ddc-backlight/scripts/ddc-backlight.sh rename to parts/features/apps/ddc-backlight/scripts/ddc-backlight.sh diff --git a/parts/features/apps/discord.nix b/parts/features/apps/discord.nix new file mode 100644 index 00000000..9c47a1e6 --- /dev/null +++ b/parts/features/apps/discord.nix @@ -0,0 +1,13 @@ +{...}: { + flake.modules.homeManager.discord = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/discord" + ]; + }; + + programs.discord = { + enable = true; + }; + }; +} diff --git a/parts/features/apps/firefox.nix b/parts/features/apps/firefox.nix new file mode 100644 index 00000000..fd3d402e --- /dev/null +++ b/parts/features/apps/firefox.nix @@ -0,0 +1,28 @@ +{...}: { + flake.modules.homeManager.firefox = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".mozilla/firefox" + ".cache/mozilla/firefox" + ]; + }; + + programs.firefox = { + enable = true; + profiles = { + main = { + extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [ + bitwarden + ]; + id = 0; + isDefault = true; + }; + }; + }; + }; +} diff --git a/parts/features/apps/fish.nix b/parts/features/apps/fish.nix new file mode 100644 index 00000000..a6f4c475 --- /dev/null +++ b/parts/features/apps/fish.nix @@ -0,0 +1,145 @@ +{...}: { + flake.modules.homeManager.fish = { + osConfig, + config, + lib, + pkgs, + ... + }: let + functions = { + fish_user_key_bindings = + /* + fish + */ + '' + fish_vi_key_bindings + + # VI mode updates + bind -s --preset --mode default j backward-char + bind -s --preset --mode default \; forward-char + bind -s --preset k down-or-search + bind -s --preset l up-or-search + bind -s --preset --mode visual j backward-char + bind -s --preset --mode visual \; forward-char + bind -s --preset --mode visual l up-line + bind -s --preset --mode visual k down-line + + # Completions + bind -s --mode insert \cw forward-word + # Tab --> accept autosuggestions + bind -s --mode insert \t accept-autosuggestion + # CTRL-S --> original TAB behaviour + bind -s --mode insert \cs complete + + # Bang-Bang bindings, manually added so they have precedence: + bind --mode insert ! __history_previous_command + bind --mode insert '$' __history_previous_command_arguments + ''; + + ## Wrap LF to add ability to quit with Q in current directory + ## + ## Adapted for fish from https://github.com/gokcehan/lf/wiki/Tips#cd-to-current-directory-on-quit + ## + lf = + /* + fish + */ + '' + set -x LF_CD_FILE /var/tmp/.lfcd-$fish_pid + command lf $argv + if test -s "$LF_CD_FILE" + set DIR (realpath (cat "$LF_CD_FILE")) + if test "$DIR" != "$PWD" + cd "$DIR" + end + rm "$LF_CD_FILE" + end + set -e LF_CD_FILE + ''; + + pirate = + /* + fish + */ + '' + set toTranslate $argv + curl -sG \ + --data-urlencode "english=$toTranslate" \ + 'http://pirate.monkeyness.com/cgi-bin/translator.pl?client=monkeyness&version=1.0' -H 'Accept: application/json' -H 'Connection: keep-alive' -H 'Accept-Encoding: gzip, deflate' -H 'Referer: http://pirate.monkeyness.com/online_pirate_translator' -H 'Accept-Language: en-US,en;q=0.9' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36' --compressed --insecure \ + | xq -r .pirateAPI.pirate \ + | curl -sG \ + --data-urlencode "source_text=$toTranslate" \ + 'https://speakpirate.com/' -H 'authority: speakpirate.com' -H 'cache-control: max-age=0' -H 'origin: https://speakpirate.com' -H 'upgrade-insecure-requests: 1' -H 'content-type: application/x-www-form-urlencoded' -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36' -H 'sec-fetch-mode: navigate' -H 'sec-fetch-user: ?1' -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3' -H 'sec-fetch-site: same-origin' -H 'referer: https://speakpirate.com/' -H 'accept-encoding: gzip, deflate, br' -H 'accept-language: en-US,en;q=0.9' -H 'cookie: __utma=133499724.1448464120.1565964854.1565964854.1565964854.1; __utmc=133499724; __utmz=133499724.1565964854.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmt=1; __utmb=133499724.1.10.1565964854' --compressed \ + | pup 'textarea#translated json{}' | jq -r '.[0].text' + ''; + + # Source .env files + # Source: http://lewandowski.io/2016/10/fish-env/ + posix-source = + /* + fish + */ + '' + for i in (cat $argv) + set arr (echo $i | string match -r "([^=]+)=(.*)") + set -gx $arr[2] $arr[3] + end + ''; + + kubectlgetall = + /* + fish + */ + '' + for i in (kubectl api-resources --verbs=list --namespaced -o name | grep -v "events.events.k8s.io" | grep -v "events" | sort | uniq) + echo "Resource:" $i + kubectl -n "$argv[1]" get --ignore-not-found "$i" + end + ''; + }; + + plugins = with pkgs.fishPlugins; [ + { + name = "bang-bang"; + src = bang-bang.src; + } + ]; + + shellInit = '' + ${variables} + ''; + + variables = + /* + fish + */ + '' + set -g fish_key_bindings fish_default_key_bindings + set fish_greeting # disable greeting + ''; + in { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/fish" + ".local/share/fish" # contains some unecessary state, but https://github.com/fish-shell/fish-shell/issues/10730 prevents us from only syncing the history file (.local/share/fish/fish_history) + ]; + }; + + programs.fish = { + enable = true; + interactiveShellInit = shellInit; + inherit functions plugins; + shellAbbrs = + config.home.shellAliases + // { + # Fish/bash-specific aliases that aren't compatible with nushell + # Use bashInteractive to ensure bind command is available + bash = "${config.programs.bash.package}/bin/bash"; + cc = "tee /dev/tty | wl-copy"; + dark-theme = "nh os test --no-specialisation && niri-set-wallpaper"; + light-theme = "nh os test --specialisation light && niri-set-wallpaper"; + pa = "pw-play ~/Music/Own\\ Speech/IckbinArschratte.WAV"; + }; + }; + }; +} diff --git a/home/fix-flexbox-mike/scripts/xps-9700-mic-fixer.sh b/parts/features/apps/fix-flexbox-mike/_scripts/xps-9700-mic-fixer.sh similarity index 100% rename from home/fix-flexbox-mike/scripts/xps-9700-mic-fixer.sh rename to parts/features/apps/fix-flexbox-mike/_scripts/xps-9700-mic-fixer.sh diff --git a/parts/features/apps/fix-flexbox-mike/default.nix b/parts/features/apps/fix-flexbox-mike/default.nix new file mode 100644 index 00000000..a19ad346 --- /dev/null +++ b/parts/features/apps/fix-flexbox-mike/default.nix @@ -0,0 +1,29 @@ +# Fix ALSA not detecting microphone on XPS 9700, see https://github.com/NixOS/nixpkgs/issues/130882#issuecomment-2584286824 +{...}: { + flake.modules.homeManager.fix-flexbox-mike = { + lib, + pkgs, + osConfig, + ... + }: let + isFlexbox = osConfig.dendrix.hostname == "flexbox"; + xps-9700-mic-fixer = pkgs.writeShellApplication { + name = "xps-9700-mic-fixer"; + runtimeInputs = [pkgs.alsa-utils]; + text = builtins.readFile ./_scripts/xps-9700-mic-fixer.sh; + }; + in { + systemd.user.services.fixXPS9700Mike = lib.mkIf isFlexbox { + Unit = { + Description = "Fix ALSA settings for internal mic on Dell XPS 9700"; + }; + Install.WantedBy = ["pipewire.service"]; + Service = { + Environment = "PATH=$PATH:/run/current-system/sw/bin"; + ExecStart = "${xps-9700-mic-fixer}/bin/xps-9700-mic-fixer"; + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + }; +} diff --git a/parts/features/apps/fzf.nix b/parts/features/apps/fzf.nix new file mode 100644 index 00000000..cc390a44 --- /dev/null +++ b/parts/features/apps/fzf.nix @@ -0,0 +1,8 @@ +{...}: { + flake.modules.homeManager.fzf = {...}: { + programs.fzf = { + enable = true; + defaultCommand = "rg --files --no-ignore-vcs --hidden"; + }; + }; +} diff --git a/parts/features/apps/galaxy-buds-client.nix b/parts/features/apps/galaxy-buds-client.nix new file mode 100644 index 00000000..ba334a32 --- /dev/null +++ b/parts/features/apps/galaxy-buds-client.nix @@ -0,0 +1,14 @@ +{...}: { + flake.modules.homeManager.galaxy-buds-client = { + lib, + pkgs, + osConfig, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".local/share/GalaxyBudsClient"]; + }; + + home.packages = [pkgs.galaxy-buds-client]; + }; +} diff --git a/parts/features/apps/gimp.nix b/parts/features/apps/gimp.nix new file mode 100644 index 00000000..1f6e4d18 --- /dev/null +++ b/parts/features/apps/gimp.nix @@ -0,0 +1,19 @@ +{...}: { + flake.modules.homeManager.gimp = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/GIMP" + ".cache/gimp" + ]; + }; + + home.packages = [ + pkgs.gimp + ]; + }; +} diff --git a/parts/features/apps/gnome-connections.nix b/parts/features/apps/gnome-connections.nix new file mode 100644 index 00000000..2ee20af0 --- /dev/null +++ b/parts/features/apps/gnome-connections.nix @@ -0,0 +1,17 @@ +{...}: { + flake.modules.homeManager.gnome-connections = { + lib, + pkgs, + osConfig, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/freerdp" # RDP server certificates/keys + ]; + files = [".config/connections.db"]; # GNOME Connections connection profiles database + }; + + home.packages = [pkgs.gnome-connections]; + }; +} diff --git a/parts/features/apps/hoppscotch.nix b/parts/features/apps/hoppscotch.nix new file mode 100644 index 00000000..27013ace --- /dev/null +++ b/parts/features/apps/hoppscotch.nix @@ -0,0 +1,23 @@ +{...}: { + # Wrap Hoppscotch with Wayland-friendly flags + flake.modules.homeManager.hoppscotch = {pkgs, ...}: let + hoppscotch-wrapped = pkgs.symlinkJoin { + name = "hoppscotch-wrapped"; + paths = [pkgs.unstable.hoppscotch]; + buildInputs = [pkgs.makeWrapper]; + postBuild = '' + wrapProgram $out/bin/hoppscotch \ + --set NIXOS_OZONE_WL 1 \ + --add-flags "--use-gl=desktop" \ + --add-flags "--disable-gpu-sandbox" + ''; + }; + in { + home.persistence."/persist".directories = [ + ".local/share/io.hoppscotch.desktop" # Auth tokens, collections, requests, workspaces + ".config/io.hoppscotch.desktop" # App settings, bundles, window state + ]; + + home.packages = [hoppscotch-wrapped]; + }; +} diff --git a/parts/features/apps/hwatch.nix b/parts/features/apps/hwatch.nix new file mode 100644 index 00000000..8d0f232c --- /dev/null +++ b/parts/features/apps/hwatch.nix @@ -0,0 +1,6 @@ +# Modern watch alternative +{...}: { + flake.modules.homeManager.hwatch = {pkgs, ...}: { + home.packages = [pkgs.hwatch]; + }; +} diff --git a/parts/features/apps/isd.nix b/parts/features/apps/isd.nix new file mode 100644 index 00000000..12c927df --- /dev/null +++ b/parts/features/apps/isd.nix @@ -0,0 +1,19 @@ +# Interactive Systemd TUI in Python +{...}: { + flake.modules.homeManager.isd = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/isd_tui" + ".local/share/isd_tui" + ".cache/isd_tui" + ]; + }; + + home.packages = [pkgs.isd]; + }; +} diff --git a/parts/features/apps/jqp.nix b/parts/features/apps/jqp.nix new file mode 100644 index 00000000..54c6fec5 --- /dev/null +++ b/parts/features/apps/jqp.nix @@ -0,0 +1,6 @@ +# TUI Playground for interacting with jq +{...}: { + flake.modules.homeManager.jqp = {...}: { + programs.jqp.enable = true; + }; +} diff --git a/home/kind-with-local-registry/scripts/kind-with-local-registry.sh b/parts/features/apps/kind-with-local-registry/_scripts/kind-with-local-registry.sh similarity index 100% rename from home/kind-with-local-registry/scripts/kind-with-local-registry.sh rename to parts/features/apps/kind-with-local-registry/_scripts/kind-with-local-registry.sh diff --git a/parts/features/apps/kind-with-local-registry/default.nix b/parts/features/apps/kind-with-local-registry/default.nix new file mode 100644 index 00000000..24cb35ca --- /dev/null +++ b/parts/features/apps/kind-with-local-registry/default.nix @@ -0,0 +1,12 @@ +# Local registry for faster image iteration, i.e. with Skaffold +# See https://kind.sigs.k8s.io/docs/user/local-registry/ +# To use local registry, prefix images with localhost:5001/ and make sure `docker push` is enabled +{...}: { + flake.modules.homeManager.kind-with-local-registry = {pkgs, ...}: let + kind-with-local-registry = pkgs.writers.writeBashBin "kind-with-local-registry" ( + builtins.readFile ./_scripts/kind-with-local-registry.sh + ); + in { + home.packages = [kind-with-local-registry]; + }; +} diff --git a/parts/features/apps/kubernetes-tools.nix b/parts/features/apps/kubernetes-tools.nix new file mode 100644 index 00000000..752500b9 --- /dev/null +++ b/parts/features/apps/kubernetes-tools.nix @@ -0,0 +1,18 @@ +{...}: { + flake.modules.homeManager.kubernetes-tools = { + lib, + pkgs, + osConfig, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".kube"]; + }; + + home.packages = [ + pkgs.kubectl + pkgs.kubectx # Kubectl Context switcher + pkgs.stern # Multi pod and container log tailing for Kubernetes + ]; + }; +} diff --git a/parts/features/apps/less.nix b/parts/features/apps/less.nix new file mode 100644 index 00000000..7091e40d --- /dev/null +++ b/parts/features/apps/less.nix @@ -0,0 +1,11 @@ +{...}: { + flake.modules.homeManager.less = {...}: { + programs.less = { + enable = true; + config = '' + k forw-line + l back-line + ''; + }; + }; +} diff --git a/home/lf/pistol/pistol.conf b/parts/features/apps/lf/_pistol/pistol.conf similarity index 100% rename from home/lf/pistol/pistol.conf rename to parts/features/apps/lf/_pistol/pistol.conf diff --git a/parts/features/apps/lf/default.nix b/parts/features/apps/lf/default.nix new file mode 100644 index 00000000..fbe87b57 --- /dev/null +++ b/parts/features/apps/lf/default.nix @@ -0,0 +1,420 @@ +{...}: { + flake.modules.homeManager.lf = { + osConfig, + lib, + pkgs, + ... + }: let + # Provides the ability to download a file by dropping it into a window + dlfile = pkgs.writers.writeBashBin "dlfile" '' + url=$(dragon -t -x) + + if [ -n "$url" ]; then + printf "File Name: " + name="" + while [ -z $name ] || [ -e $name ] + do + read -r name + if [ -e "$name" ]; then + printf "File already exists, overwrite (y|n): " + read -r ans + + if [ "$ans" = "y" ]; then + break + else + printf "File Name: " + fi + fi + done + + # Download the file with curl + [ -n "$name" ] && curl -o "$name" "$url" || exit 1 + else + exit 1 + fi + ''; + in { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".local/share/lf" + ]; + }; + + home.packages = with pkgs; [ + chafa # Images to terminal pixels, used by pistol + dlfile # Provides the ability to download a file by dropping it into a window + imagemagick # Image conversion for clipboard operations + pistol # Image previewer + ripdrag + ]; + + home.file = { + ".config/pistol/pistol.conf".source = ./_pistol/pistol.conf; + }; + + programs.lf = { + enable = true; + + commands = { + archive = '' + ''${{ + tar --create --xz --file="$PWD/$(basename "$f").tar.xz" "$f" + }} + ''; + + chmod = '' + ''${{ + printf "Mode Bits: " + read ans + + for file in "$fx" + do + chmod $ans $file + done + + lf -remote 'send reload' + }} + ''; + + dlfile = "%dlfile"; + dragon = "%ripdrag -a $fx"; + + mkdir = '' + ''${{ + printf "Directory Name: " + read ans + mkdir $ans + }} + ''; + + mkfile = '' + ''${{ + printf "File Name: " + read ans + $EDITOR $ans + }} + ''; + + paste-image = '' + ''${{ + printf "File Name (e.g., image.png): " + read ans + + if [ -z "$ans" ]; then + echo "No filename provided" + exit 1 + fi + + if [ -e "$ans" ]; then + printf "File already exists, overwrite (y|n): " + read overwrite + if [ "$overwrite" != "y" ]; then + exit 1 + fi + fi + + wl-paste > "$ans" + if [ $? -eq 0 ]; then + echo "Pasted clipboard content to $ans" + else + echo "Failed to paste from clipboard" + fi + }} + ''; + + open = '' + ''${{ + case $(file --mime-type "$f" -bL) in + text/*|application/json) $EDITOR "$f";; + video/*|image/*/application/pdf) xdg-open "$f";; + *) xdg-open "$f";; + esac + }} + ''; + + quit-and-cd = '' + &{{ + pwd > $LF_CD_FILE + lf -remote "send $id quit" + }} + ''; + + sudomkfile = '' + ''${{ + printf "File Name: " + read ans + sudo $EDITOR $ANS + }} + ''; + + trash = '' + ''${{ + files=$(printf "$fx" | tr '\n' ';') + while [ "$files" ]; do + # extract the substring from start of string up to delimiter. + # this is the first "element" of the string. + file=''${files%%;*} + + trash-put "$(basename "$file")" + # if there's only one element left, set `files` to an empty string. + # this causes us to exit this `while` loop. + # else, we delete the first "element" of the string from files, and move onto the next. + if [ "$files" = "$file" ]; then + files=''' + else + files="''${files#*;}" + fi + done + }} + ''; + + unarchive = '' + ''${{ + case "$f" in + *.zip) unzip "$f";; + *.tar.gz) tar -xzvf "$f" ;; + *.tar.bz2) tar -xjvf "$f" ;; + *.tar) tar -xvf "$f" ;; + *) echo "Unsupported format" ;; + esac + }} + ''; + + yank-file = ''$printf '%s' "$f" | wl-copy''; + yank-paths = ''$printf '%s' "$fx" | wl-copy''; + yank-dirname = ''&printf '%s' "$PWD" | wl-copy''; + yank-basename = ''&basename -a -- $fx | head -c-1 | wl-copy''; + yank-basename-without-extension = ''&basename -a -- $fx | sed -E 's/\.[^.]+$//' | head -c-1 | wl-copy''; + + yank-image = '' + ''${{ + # Copy the first selected file's binary content to clipboard as PNG + # Many apps only accept PNG from clipboard on Wayland + file="$(echo "$fx" | head -n1)" + mime_type="$(file --mime-type -b "$file")" + + if [[ "$mime_type" == image/png ]]; then + # Already PNG, copy directly + wl-copy -t image/png < "$file" + else + # Convert to PNG first for compatibility + convert "$file" png:- | wl-copy -t image/png + fi + }} + ''; + + z = '' + %{{ + result="$(zoxide query --exclude $PWD $@ | sed 's/\\/\\\\/g;s/"/\\"/g')" + lf -remote "send $id cd \"$result\"" + }} + ''; + + zi = '' + ''${{ + result="$(zoxide query -i | sed 's/\\/\\\\/g;s/"/\\"/g')" + lf -remote "send $id cd \"$result\"" + }} + ''; + }; + + keybindings = { + "." = "set hidden!"; + d = null; + dd = "trash"; + dl = "dlfile"; + dr = "dragon"; + f = "zi"; + h = "chmod"; + k = "down"; + l = "up"; + ";" = "open"; + j = "updir"; + m = null; + md = "mkdir"; + mf = "mkfile"; + mr = "sudomkfile"; + mp = "paste-image"; + Q = "quit-and-cd"; + x = "cut"; + Y = "yank-image"; + }; + + previewer = { + keybinding = "i"; + source = "${pkgs.pistol}/bin/pistol"; + }; + + settings = { + icons = true; + # Set IFS to newline to allow commands to work with spaces in filenames + ifs = "\n"; + }; + }; + + home.sessionVariables = { + LF_ICONS = '' + tw=:\ + st=:\ + ow=:\ + dt=:\ + di=:\ + fi=:\ + ln=:\ + or=:\ + ex=:\ + *.c=:\ + *.cc=:\ + *.clj=:\ + *.coffee=:\ + *.cpp=:\ + *.css=:\ + *.d=:\ + *.dart=:\ + *.erl=:\ + *.exs=:\ + *.fs=:\ + *.go=:\ + *.h=:\ + *.hh=:\ + *.hpp=:\ + *.hs=:\ + *.html=:\ + *.java=:\ + *.jl=:\ + *.js=:\ + *.json=:\ + *.lua=:\ + *.md=:\ + *.php=:\ + *.pl=:\ + *.pro=:\ + *.py=:\ + *.rb=:\ + *.rs=:\ + *.scala=:\ + *.ts=:\ + *.vim=:\ + *.cmd=:\ + *.ps1=:\ + *.sh=:\ + *.bash=:\ + *.zsh=:\ + *.fish=:\ + *.tar=:\ + *.tgz=:\ + *.arc=:\ + *.arj=:\ + *.taz=:\ + *.lha=:\ + *.lz4=:\ + *.lzh=:\ + *.lzma=:\ + *.tlz=:\ + *.txz=:\ + *.tzo=:\ + *.t7z=:\ + *.zip=:\ + *.z=:\ + *.dz=:\ + *.gz=:\ + *.lrz=:\ + *.lz=:\ + *.lzo=:\ + *.xz=:\ + *.zst=:\ + *.tzst=:\ + *.bz2=:\ + *.bz=:\ + *.tbz=:\ + *.tbz2=:\ + *.tz=:\ + *.deb=:\ + *.rpm=:\ + *.jar=:\ + *.war=:\ + *.ear=:\ + *.sar=:\ + *.rar=:\ + *.alz=:\ + *.ace=:\ + *.zoo=:\ + *.cpio=:\ + *.7z=:\ + *.rz=:\ + *.cab=:\ + *.wim=:\ + *.swm=:\ + *.dwm=:\ + *.esd=:\ + *.jpg=:\ + *.jpeg=:\ + *.mjpg=:\ + *.mjpeg=:\ + *.gif=:\ + *.bmp=:\ + *.pbm=:\ + *.pgm=:\ + *.ppm=:\ + *.tga=:\ + *.xbm=:\ + *.xpm=:\ + *.tif=:\ + *.tiff=:\ + *.png=:\ + *.svg=:\ + *.svgz=:\ + *.mng=:\ + *.pcx=:\ + *.mov=:\ + *.mpg=:\ + *.mpeg=:\ + *.m2v=:\ + *.mkv=:\ + *.webm=:\ + *.ogm=:\ + *.mp4=:\ + *.m4v=:\ + *.mp4v=:\ + *.vob=:\ + *.qt=:\ + *.nuv=:\ + *.wmv=:\ + *.asf=:\ + *.rm=:\ + *.rmvb=:\ + *.flc=:\ + *.avi=:\ + *.fli=:\ + *.flv=:\ + *.gl=:\ + *.dl=:\ + *.xcf=:\ + *.xwd=:\ + *.yuv=:\ + *.cgm=:\ + *.emf=:\ + *.ogv=:\ + *.ogx=:\ + *.aac=:\ + *.au=:\ + *.flac=:\ + *.m4a=:\ + *.mid=:\ + *.midi=:\ + *.mka=:\ + *.mp3=:\ + *.mpc=:\ + *.ogg=:\ + *.ra=:\ + *.wav=:\ + *.oga=:\ + *.opus=:\ + *.spx=:\ + *.xspf=:\ + *.pdf=:\ + *.nix=: + ''; + }; + }; +} diff --git a/parts/features/apps/libation.nix b/parts/features/apps/libation.nix new file mode 100644 index 00000000..46e9fc01 --- /dev/null +++ b/parts/features/apps/libation.nix @@ -0,0 +1,18 @@ +# Audible liberator +{...}: { + flake.modules.homeManager.libation = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + "Libation" + ".local/share/Libation" + ]; + }; + + home.packages = [pkgs.libation]; + }; +} diff --git a/parts/features/apps/libreoffice.nix b/parts/features/apps/libreoffice.nix new file mode 100644 index 00000000..803cee37 --- /dev/null +++ b/parts/features/apps/libreoffice.nix @@ -0,0 +1,18 @@ +{...}: { + flake.modules.homeManager.libreoffice = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/libreoffice" + ]; + }; + + home.packages = [ + pkgs.libreoffice + ]; + }; +} diff --git a/parts/features/apps/lnav.nix b/parts/features/apps/lnav.nix new file mode 100644 index 00000000..9356241b --- /dev/null +++ b/parts/features/apps/lnav.nix @@ -0,0 +1,15 @@ +# Log File Navigator +{...}: { + flake.modules.homeManager.lnav = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".config/lnav"]; + }; + + home.packages = [pkgs.lnav]; + }; +} diff --git a/home/mic-levels-maintainer/scripts/mic-levels-maintainer-flexbox.sh b/parts/features/apps/mic-levels-maintainer/_scripts/mic-levels-maintainer-flexbox.sh similarity index 100% rename from home/mic-levels-maintainer/scripts/mic-levels-maintainer-flexbox.sh rename to parts/features/apps/mic-levels-maintainer/_scripts/mic-levels-maintainer-flexbox.sh diff --git a/home/mic-levels-maintainer/scripts/mic-levels-maintainer-numenor.sh b/parts/features/apps/mic-levels-maintainer/_scripts/mic-levels-maintainer-numenor.sh similarity index 100% rename from home/mic-levels-maintainer/scripts/mic-levels-maintainer-numenor.sh rename to parts/features/apps/mic-levels-maintainer/_scripts/mic-levels-maintainer-numenor.sh diff --git a/parts/features/apps/mic-levels-maintainer/default.nix b/parts/features/apps/mic-levels-maintainer/default.nix new file mode 100644 index 00000000..141d84d8 --- /dev/null +++ b/parts/features/apps/mic-levels-maintainer/default.nix @@ -0,0 +1,27 @@ +# Maintain input gain levels +{...}: { + flake.modules.homeManager.mic-levels-maintainer = {pkgs, osConfig, ...}: let + isNumenor = osConfig.dendrix.hostname == "numenor"; + mic-levels-maintainer = pkgs.writers.writeBashBin "mic-levels-maintainer" ( + if isNumenor + then (builtins.readFile ./_scripts/mic-levels-maintainer-numenor.sh) + else (builtins.readFile ./_scripts/mic-levels-maintainer-flexbox.sh) + ); + in { + home.packages = [mic-levels-maintainer]; + + systemd.user.services.mic-levels-maintainer = { + Unit = { + After = ["obs-mic.service"]; + Description = "Maintain input gain levels"; + Requires = ["obs-mic.service"]; + }; + Install.WantedBy = ["obs-mic.service"]; + Service = { + Environment = "PATH=$PATH:/run/current-system/sw/bin"; + ExecStart = "${mic-levels-maintainer}/bin/mic-levels-maintainer"; + Restart = "always"; + }; + }; + }; +} diff --git a/parts/features/apps/mpv.nix b/parts/features/apps/mpv.nix new file mode 100644 index 00000000..837e8e83 --- /dev/null +++ b/parts/features/apps/mpv.nix @@ -0,0 +1,20 @@ +# Media Player +{...}: { + flake.modules.homeManager.mpv = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/mpv" + ".cache/mpv" + ]; + }; + + home.packages = [ + pkgs.mpv + ]; + }; +} diff --git a/parts/features/apps/mullvad-browser.nix b/parts/features/apps/mullvad-browser.nix new file mode 100644 index 00000000..41473b1f --- /dev/null +++ b/parts/features/apps/mullvad-browser.nix @@ -0,0 +1,13 @@ +{...}: { + flake.modules.homeManager.mullvad-browser = {lib, osConfig, pkgs, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".mullvad" + ]; + }; + + home.packages = [ + pkgs.mullvad-browser + ]; + }; +} diff --git a/parts/features/apps/nautilus.nix b/parts/features/apps/nautilus.nix new file mode 100644 index 00000000..b0c7f5dd --- /dev/null +++ b/parts/features/apps/nautilus.nix @@ -0,0 +1,18 @@ +# Gnome File Manager +{...}: { + flake.modules.homeManager.nautilus = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/nautilus" + ".local/share/nautilus" + ]; + }; + + home.packages = [pkgs.nautilus]; + }; +} diff --git a/home/networkmanager-dmenu/config.ini b/parts/features/apps/networkmanager-dmenu/_config.ini similarity index 100% rename from home/networkmanager-dmenu/config.ini rename to parts/features/apps/networkmanager-dmenu/_config.ini diff --git a/parts/features/apps/networkmanager-dmenu/default.nix b/parts/features/apps/networkmanager-dmenu/default.nix new file mode 100644 index 00000000..411f0006 --- /dev/null +++ b/parts/features/apps/networkmanager-dmenu/default.nix @@ -0,0 +1,6 @@ +{...}: { + flake.modules.homeManager.networkmanager-dmenu = {pkgs, ...}: { + home.packages = [pkgs.networkmanager_dmenu]; + xdg.configFile."networkmanager-dmenu/config.ini".source = ./_config.ini; + }; +} diff --git a/parts/features/apps/nh.nix b/parts/features/apps/nh.nix new file mode 100644 index 00000000..f7bb4146 --- /dev/null +++ b/parts/features/apps/nh.nix @@ -0,0 +1,8 @@ +{...}: { + flake.modules.homeManager.nh = {...}: { + programs.nh = { + enable = true; + flake = "/home/farlion/code/nixos-config"; + }; + }; +} diff --git a/parts/features/apps/nix-index.nix b/parts/features/apps/nix-index.nix new file mode 100644 index 00000000..0196e43e --- /dev/null +++ b/parts/features/apps/nix-index.nix @@ -0,0 +1,17 @@ +{inputs, ...}: { + flake.modules.homeManager.nix-index = {lib, osConfig, ...}: { + imports = [ + inputs.nix-index-database.homeModules.nix-index + ]; + + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + files = [ + ".local/state/comma-choices" # For , + ]; + }; + + programs.nix-index = { + enable = true; + }; + }; +} diff --git a/parts/features/apps/nix-inspect.nix b/parts/features/apps/nix-inspect.nix new file mode 100644 index 00000000..65a0073c --- /dev/null +++ b/parts/features/apps/nix-inspect.nix @@ -0,0 +1,7 @@ +{...}: { + flake.modules.homeManager.nix-inspect = {pkgs, ...}: { + home.packages = [ + pkgs.nix-inspect + ]; + }; +} diff --git a/home/nushell/syncthing/stignore-nushell b/parts/features/apps/nushell/_syncthing/stignore-nushell similarity index 100% rename from home/nushell/syncthing/stignore-nushell rename to parts/features/apps/nushell/_syncthing/stignore-nushell diff --git a/home/nushell/config.nu b/parts/features/apps/nushell/config.nu similarity index 100% rename from home/nushell/config.nu rename to parts/features/apps/nushell/config.nu diff --git a/parts/features/apps/nushell/default.nix b/parts/features/apps/nushell/default.nix new file mode 100644 index 00000000..e0c96743 --- /dev/null +++ b/parts/features/apps/nushell/default.nix @@ -0,0 +1,30 @@ +{...}: { + flake.modules.homeManager.nushell = { + osConfig, + lib, + pkgs, + ... + }: let + # Custom nushell build with system-clipboard support for Ctrl+X keybinding + nushellWithClipboard = pkgs.unstable.nushell.overrideAttrs (oldAttrs: { + cargoBuildFeatures = (oldAttrs.cargoBuildFeatures or []) ++ ["system-clipboard"]; + }); + in { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/nushell" + ]; + }; + home.file = { + ".config/nushell/.stignore" = { + source = ./_syncthing/stignore-nushell; + }; + }; + programs.nushell = { + enable = true; + configFile.source = ./config.nu; + envFile.source = ./env.nu; + package = nushellWithClipboard; + }; + }; +} diff --git a/home/nushell/env.nu b/parts/features/apps/nushell/env.nu similarity index 100% rename from home/nushell/env.nu rename to parts/features/apps/nushell/env.nu diff --git a/home/obs/scripts/obs-catcam-toggle.sh b/parts/features/apps/obs/_scripts/obs-catcam-toggle.sh similarity index 100% rename from home/obs/scripts/obs-catcam-toggle.sh rename to parts/features/apps/obs/_scripts/obs-catcam-toggle.sh diff --git a/home/obs/scripts/obs-main-scene.sh b/parts/features/apps/obs/_scripts/obs-main-scene.sh similarity index 100% rename from home/obs/scripts/obs-main-scene.sh rename to parts/features/apps/obs/_scripts/obs-main-scene.sh diff --git a/home/obs/scripts/obs-recording-pause.sh b/parts/features/apps/obs/_scripts/obs-recording-pause.sh similarity index 100% rename from home/obs/scripts/obs-recording-pause.sh rename to parts/features/apps/obs/_scripts/obs-recording-pause.sh diff --git a/home/obs/scripts/obs-recording-toggle.sh b/parts/features/apps/obs/_scripts/obs-recording-toggle.sh similarity index 100% rename from home/obs/scripts/obs-recording-toggle.sh rename to parts/features/apps/obs/_scripts/obs-recording-toggle.sh diff --git a/home/obs/scripts/obs-screensharing.sh b/parts/features/apps/obs/_scripts/obs-screensharing.sh similarity index 100% rename from home/obs/scripts/obs-screensharing.sh rename to parts/features/apps/obs/_scripts/obs-screensharing.sh diff --git a/home/obs/scripts/obs-webcam-toggle.sh b/parts/features/apps/obs/_scripts/obs-webcam-toggle.sh similarity index 100% rename from home/obs/scripts/obs-webcam-toggle.sh rename to parts/features/apps/obs/_scripts/obs-webcam-toggle.sh diff --git a/parts/features/apps/obs/default.nix b/parts/features/apps/obs/default.nix new file mode 100644 index 00000000..aef079d5 --- /dev/null +++ b/parts/features/apps/obs/default.nix @@ -0,0 +1,86 @@ +{...}: { + flake.modules.homeManager.obs = { + lib, + pkgs, + osConfig, + ... + }: let + isFlexbox = osConfig.dendrix.hostname == "flexbox"; + + # OBS Control Scripts + obsMainScene = pkgs.writeShellApplication { + name = "obs-main-scene"; + runtimeInputs = [pkgs.obs-cmd]; + text = builtins.readFile ./_scripts/obs-main-scene.sh; + }; + + obsScreensharing = pkgs.writeShellApplication { + name = "obs-screensharing"; + runtimeInputs = [pkgs.obs-cmd]; + text = builtins.readFile ./_scripts/obs-screensharing.sh; + }; + + obsCatcamToggle = pkgs.writeShellApplication { + name = "obs-catcam-toggle"; + runtimeInputs = [pkgs.obs-cmd]; + text = builtins.readFile ./_scripts/obs-catcam-toggle.sh; + }; + + obsRecordingToggle = pkgs.writeShellApplication { + name = "obs-recording-toggle"; + runtimeInputs = [pkgs.obs-cmd]; + text = builtins.readFile ./_scripts/obs-recording-toggle.sh; + }; + + obsRecordingPause = pkgs.writeShellApplication { + name = "obs-recording-pause"; + runtimeInputs = [pkgs.obs-cmd]; + text = builtins.readFile ./_scripts/obs-recording-pause.sh; + }; + + obsWebcamToggle = pkgs.writeShellApplication { + name = "obs-webcam-toggle"; + runtimeInputs = [pkgs.obs-cmd]; + text = builtins.readFile ./_scripts/obs-webcam-toggle.sh; + }; + in { + home.packages = [ + pkgs.obs-cmd + obsMainScene + obsScreensharing + obsCatcamToggle + obsRecordingToggle + obsRecordingPause + obsWebcamToggle + ]; + + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".config/obs-studio"]; + }; + + programs.obs-studio = { + enable = true; + plugins = with pkgs.obs-studio-plugins; [ + obs-backgroundremoval + pkgs.unstable.obs-studio-plugins.obs-noise + pkgs.unstable.obs-studio-plugins.pixel-art + pkgs.unstable.obs-studio-plugins.obs-recursion-effect + pkgs.unstable.obs-studio-plugins.obs-retro-effects + obs-vintage-filter + ]; + }; + + xdg.desktopEntries = lib.mkIf isFlexbox { + obs = { + name = "OBS Studio (NVIDIA GPU)"; + exec = "nvidia-offload obs"; + genericName = "Streaming/Recording Software"; + terminal = false; + type = "Application"; + categories = ["AudioVideo" "Recorder"]; + icon = "com.obsproject.Studio"; + startupNotify = true; + }; + }; + }; +} diff --git a/parts/features/apps/obsidian.nix b/parts/features/apps/obsidian.nix new file mode 100644 index 00000000..45ecbe94 --- /dev/null +++ b/parts/features/apps/obsidian.nix @@ -0,0 +1,19 @@ +{...}: { + flake.modules.homeManager.obsidian = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/obsidian" + "Obsidian" + ]; + }; + + home.packages = [ + pkgs.obsidian + ]; + }; +} diff --git a/parts/features/apps/onboard.nix b/parts/features/apps/onboard.nix new file mode 100644 index 00000000..c9b24d7c --- /dev/null +++ b/parts/features/apps/onboard.nix @@ -0,0 +1,6 @@ +# Onboard Keyboard Layout +{...}: { + flake.modules.homeManager.onboard = {pkgs, ...}: { + home.packages = [pkgs.onboard]; + }; +} diff --git a/parts/features/apps/pavucontrol.nix b/parts/features/apps/pavucontrol.nix new file mode 100644 index 00000000..3ab255b1 --- /dev/null +++ b/parts/features/apps/pavucontrol.nix @@ -0,0 +1,14 @@ +# Pulse Audio Volume Control GUI +{...}: { + flake.modules.homeManager.pavucontrol = {lib, osConfig, pkgs, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + files = [ + ".config/pavucontrol.ini" + ]; + }; + + home.packages = [ + pkgs.pavucontrol + ]; + }; +} diff --git a/parts/features/apps/pgcli.nix b/parts/features/apps/pgcli.nix new file mode 100644 index 00000000..2d0f18ae --- /dev/null +++ b/parts/features/apps/pgcli.nix @@ -0,0 +1,6 @@ +# Actually usable PostgreSQL CLI +{...}: { + flake.modules.homeManager.pgcli = {...}: { + programs.pgcli.enable = true; + }; +} diff --git a/parts/features/apps/pomodoro-gtk.nix b/parts/features/apps/pomodoro-gtk.nix new file mode 100644 index 00000000..d108f447 --- /dev/null +++ b/parts/features/apps/pomodoro-gtk.nix @@ -0,0 +1,5 @@ +{...}: { + flake.modules.homeManager.pomodoro-gtk = {pkgs, ...}: { + home.packages = [pkgs.pomodoro-gtk]; + }; +} diff --git a/parts/features/apps/portfolio-performance.nix b/parts/features/apps/portfolio-performance.nix new file mode 100644 index 00000000..40a7a49b --- /dev/null +++ b/parts/features/apps/portfolio-performance.nix @@ -0,0 +1,14 @@ +{...}: { + flake.modules.homeManager.portfolio-performance = { + lib, + pkgs, + osConfig, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".PortfolioPerformance"]; + }; + + home.packages = [pkgs.unstable.portfolio]; + }; +} diff --git a/home/psql/psqlrc b/parts/features/apps/psql/_psqlrc similarity index 100% rename from home/psql/psqlrc rename to parts/features/apps/psql/_psqlrc diff --git a/parts/features/apps/psql/default.nix b/parts/features/apps/psql/default.nix new file mode 100644 index 00000000..0d110ccf --- /dev/null +++ b/parts/features/apps/psql/default.nix @@ -0,0 +1,7 @@ +# Postgresql Client with nicer config +{...}: { + flake.modules.homeManager.psql = {pkgs, ...}: { + home.packages = [pkgs.postgresql]; + home.file.".psqlrc".source = ./_psqlrc; + }; +} diff --git a/parts/features/apps/qalculate.nix b/parts/features/apps/qalculate.nix new file mode 100644 index 00000000..75c8fd41 --- /dev/null +++ b/parts/features/apps/qalculate.nix @@ -0,0 +1,20 @@ +# Calculator +{...}: { + flake.modules.homeManager.qalculate = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/qalculate" + ".local/share/qalculate" + ]; + }; + + home.packages = [ + pkgs.qalculate-gtk + ]; + }; +} diff --git a/parts/features/apps/ripgrep-all.nix b/parts/features/apps/ripgrep-all.nix new file mode 100644 index 00000000..b23eddb7 --- /dev/null +++ b/parts/features/apps/ripgrep-all.nix @@ -0,0 +1,6 @@ +# Like rg, but also search in Office documents, PDFs etc...; rga-fzf is AMAZING! +{...}: { + flake.modules.homeManager.ripgrep-all = {...}: { + programs.ripgrep-all.enable = true; + }; +} diff --git a/parts/features/apps/ripgrep.nix b/parts/features/apps/ripgrep.nix new file mode 100644 index 00000000..95880ee1 --- /dev/null +++ b/parts/features/apps/ripgrep.nix @@ -0,0 +1,8 @@ +{...}: { + flake.modules.homeManager.ripgrep = {...}: { + programs.ripgrep = { + enable = true; + arguments = ["--smart-case"]; + }; + }; +} diff --git a/parts/features/apps/rofimoji/default.nix b/parts/features/apps/rofimoji/default.nix new file mode 100644 index 00000000..62dca9a1 --- /dev/null +++ b/parts/features/apps/rofimoji/default.nix @@ -0,0 +1,10 @@ +{...}: { + flake.modules.homeManager.rofimoji = {pkgs, ...}: { + home.packages = [ + pkgs.rofimoji + pkgs.wtype # insert emojis directly + ]; + + xdg.configFile."rofimoji.rc".source = ./rofimoji.rc; + }; +} diff --git a/home/rofimoji/rofimoji.rc b/parts/features/apps/rofimoji/rofimoji.rc similarity index 100% rename from home/rofimoji/rofimoji.rc rename to parts/features/apps/rofimoji/rofimoji.rc diff --git a/home/satty/scripts/satty-screenshot.sh b/parts/features/apps/satty/_scripts/satty-screenshot.sh similarity index 100% rename from home/satty/scripts/satty-screenshot.sh rename to parts/features/apps/satty/_scripts/satty-screenshot.sh diff --git a/parts/features/apps/satty/default.nix b/parts/features/apps/satty/default.nix new file mode 100644 index 00000000..ad1a751c --- /dev/null +++ b/parts/features/apps/satty/default.nix @@ -0,0 +1,39 @@ +# Screenshot Annotation tool written in Rust +{...}: { + flake.modules.homeManager.satty = { + osConfig, + lib, + pkgs, + ... + }: let + sattyScreenshot = pkgs.writeShellApplication { + name = "satty-screenshot"; + runtimeInputs = with pkgs; [ + satty + grim + wl-clipboard + jq + niri + ]; + text = builtins.readFile ./_scripts/satty-screenshot.sh; + }; + in { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".cache/satty" + ]; + }; + + programs.satty = { + enable = true; + settings.general = { + fullscreen = true; + initial-tool = "crop"; + }; + }; + + home.packages = [ + sattyScreenshot + ]; + }; +} diff --git a/parts/features/apps/showmethekey.nix b/parts/features/apps/showmethekey.nix new file mode 100644 index 00000000..63b565f2 --- /dev/null +++ b/parts/features/apps/showmethekey.nix @@ -0,0 +1,6 @@ +# screenkey for Wayland, show key presses +{...}: { + flake.modules.homeManager.showmethekey = {pkgs, ...}: { + home.packages = [pkgs.showmethekey]; + }; +} diff --git a/parts/features/apps/signal.nix b/parts/features/apps/signal.nix new file mode 100644 index 00000000..c2a47528 --- /dev/null +++ b/parts/features/apps/signal.nix @@ -0,0 +1,18 @@ +{...}: { + flake.modules.homeManager.signal = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/Signal" + ]; + }; + + home.packages = [ + pkgs.signal-desktop + ]; + }; +} diff --git a/parts/features/apps/solaar.nix b/parts/features/apps/solaar.nix new file mode 100644 index 00000000..fd25dee3 --- /dev/null +++ b/parts/features/apps/solaar.nix @@ -0,0 +1,18 @@ +# Linux devices manager for the Logitech Unifying Receiver +{...}: { + flake.modules.homeManager.solaar = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".config/solaar"]; + }; + + home.packages = [ + pkgs.hicolor-icon-theme + pkgs.solaar + ]; + }; +} diff --git a/home/sound-switcher/scripts/sound-switcher-flexbox.sh b/parts/features/apps/sound-switcher/_scripts/sound-switcher-flexbox.sh similarity index 100% rename from home/sound-switcher/scripts/sound-switcher-flexbox.sh rename to parts/features/apps/sound-switcher/_scripts/sound-switcher-flexbox.sh diff --git a/home/sound-switcher/scripts/sound-switcher-numenor.sh b/parts/features/apps/sound-switcher/_scripts/sound-switcher-numenor.sh similarity index 100% rename from home/sound-switcher/scripts/sound-switcher-numenor.sh rename to parts/features/apps/sound-switcher/_scripts/sound-switcher-numenor.sh diff --git a/parts/features/apps/sound-switcher/default.nix b/parts/features/apps/sound-switcher/default.nix new file mode 100644 index 00000000..8cc5652a --- /dev/null +++ b/parts/features/apps/sound-switcher/default.nix @@ -0,0 +1,13 @@ +# Rofi-based sound switcher +{...}: { + flake.modules.homeManager.sound-switcher = {pkgs, osConfig, ...}: let + isNumenor = osConfig.dendrix.hostname == "numenor"; + sound-switcher = pkgs.writers.writeBashBin "sound-switcher" ( + if isNumenor + then (builtins.readFile ./_scripts/sound-switcher-numenor.sh) + else (builtins.readFile ./_scripts/sound-switcher-flexbox.sh) + ); + in { + home.packages = [sound-switcher]; + }; +} diff --git a/home/ssh/ssh-add-all.sh b/parts/features/apps/ssh/_scripts/ssh-add-all.sh similarity index 100% rename from home/ssh/ssh-add-all.sh rename to parts/features/apps/ssh/_scripts/ssh-add-all.sh diff --git a/parts/features/apps/ssh/default.nix b/parts/features/apps/ssh/default.nix new file mode 100644 index 00000000..e3ad2195 --- /dev/null +++ b/parts/features/apps/ssh/default.nix @@ -0,0 +1,38 @@ +{...}: { + flake.modules.homeManager.ssh = { + osConfig, + lib, + pkgs, + ... + }: let + ssh-add-all = pkgs.writeShellApplication { + name = "ssh-add-all"; + runtimeInputs = with pkgs; [openssh coreutils]; + text = builtins.readFile ./_scripts/ssh-add-all.sh; + }; + in { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".ssh" + ]; + }; + + home.packages = [ssh-add-all]; + + programs.ssh = { + enable = true; + enableDefaultConfig = false; + matchBlocks."*" = { + addKeysToAgent = "yes"; + }; + }; + + # Disable default ssh-agent since we use gcr-ssh-agent (via services.gnome.gnome-keyring) + services.ssh-agent.enable = false; + + home.sessionVariables = { + # Point to the new gcr SSH agent socket (NixOS 25.11+) + SSH_AUTH_SOCK = "$XDG_RUNTIME_DIR/gcr/ssh"; + }; + }; +} diff --git a/parts/features/apps/starship.nix b/parts/features/apps/starship.nix new file mode 100644 index 00000000..b35acdff --- /dev/null +++ b/parts/features/apps/starship.nix @@ -0,0 +1,121 @@ +{...}: { + flake.modules.homeManager.starship = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".cache/starship"]; + }; + + programs.starship = { + enable = true; + enableTransience = true; + + settings = { + format = lib.concatStrings [ + "$username" + "$hostname" + "$localip" + "$shlvl" + "$singularity" + "$directory" + "$vcsh" + "$docker_context" + "$package" + "$buf" + "$c" + "$cmake" + "$cobol" + "$container" + "$daml" + "$dart" + "$deno" + "$dotnet" + "$elixir" + "$elm" + "$erlang" + "$golang" + "$haskell" + "$helm" + "$java" + "$julia" + "$kotlin" + "$lua" + "$nim" + "$nodejs" + "$ocaml" + "$perl" + "$php" + "$pulumi" + "$purescript" + "$python" + "$rlang" + "$red" + "$ruby" + "$rust" + "$scala" + "$swift" + "$terraform" + "$vlang" + "$vagrant" + "$zig" + "$nix_shell" + "$conda" + "$spack" + "$memory_usage" + "$aws" + "$gcloud" + "$kubernetes" + "$openstack" + "$azure" + "$env_var" + "$crystal" + "$custom" + "$sudo" + "$cmd_duration" + "$line_break" + "$jobs" + "$battery" + "$time" + "$status" + "$shell" + "$character" + ]; + + aws.disabled = true; + + gcloud = { + disabled = true; + format = "on [$symbol$account(@$domain)|($project)](green) "; + }; + + kubernetes = { + disabled = false; + style = "green"; + contexts = [ + { + "context_pattern" = "kind-kind"; + "context_alias" = "kind"; + } + ]; + }; + + shell = { + disabled = false; + fish_indicator = "🐟"; + bash_indicator = "💩"; + nu_indicator = "🦀"; + }; + + status = { + disabled = false; + map_symbol = true; + pipestatus = false; + }; + + nix_shell = { + disabled = true; + format = "via [$symbol$state]($style) "; + impure_msg = ""; + }; + }; + }; + }; +} diff --git a/home/systemd-errors-and-warnings-counter/scripts/systemd-errors-and-warnings-counter.sh b/parts/features/apps/systemd-errors-and-warnings-counter/_scripts/systemd-errors-and-warnings-counter.sh similarity index 100% rename from home/systemd-errors-and-warnings-counter/scripts/systemd-errors-and-warnings-counter.sh rename to parts/features/apps/systemd-errors-and-warnings-counter/_scripts/systemd-errors-and-warnings-counter.sh diff --git a/parts/features/apps/systemd-errors-and-warnings-counter/default.nix b/parts/features/apps/systemd-errors-and-warnings-counter/default.nix new file mode 100644 index 00000000..f9a619cf --- /dev/null +++ b/parts/features/apps/systemd-errors-and-warnings-counter/default.nix @@ -0,0 +1,12 @@ +# Display number of systemd errors and warnings in last 10 minutes +{...}: { + flake.modules.homeManager.systemd-errors-and-warnings-counter = {pkgs, ...}: let + systemd-errors-and-warnings-counter = pkgs.writeShellApplication { + name = "systemd-errors-and-warnings-counter"; + runtimeInputs = [pkgs.systemd pkgs.coreutils]; + text = builtins.readFile ./_scripts/systemd-errors-and-warnings-counter.sh; + }; + in { + home.packages = [systemd-errors-and-warnings-counter]; + }; +} diff --git a/parts/features/apps/tealdeer.nix b/parts/features/apps/tealdeer.nix new file mode 100644 index 00000000..893b2089 --- /dev/null +++ b/parts/features/apps/tealdeer.nix @@ -0,0 +1,12 @@ +{...}: { + flake.modules.homeManager.tealdeer = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".cache/tealdeer"]; + }; + + programs.tealdeer = { + enable = true; + settings.updates.auto_update = true; + }; + }; +} diff --git a/parts/features/apps/telegram.nix b/parts/features/apps/telegram.nix new file mode 100644 index 00000000..6f8d4765 --- /dev/null +++ b/parts/features/apps/telegram.nix @@ -0,0 +1,18 @@ +{...}: { + flake.modules.homeManager.telegram = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".local/share/TelegramDesktop" + ]; + }; + + home.packages = [ + pkgs.telegram-desktop + ]; + }; +} diff --git a/parts/features/apps/television.nix b/parts/features/apps/television.nix new file mode 100644 index 00000000..b77ce95a --- /dev/null +++ b/parts/features/apps/television.nix @@ -0,0 +1,17 @@ +# Fuzzy-finder in Rust with nixpkgs integration +{...}: { + flake.modules.homeManager.television = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/television/cable" + ]; + }; + + programs.television = { + enable = true; + }; + programs.nix-search-tv = { + enable = true; + }; + }; +} diff --git a/parts/features/apps/thunderbird.nix b/parts/features/apps/thunderbird.nix new file mode 100644 index 00000000..b9e431ed --- /dev/null +++ b/parts/features/apps/thunderbird.nix @@ -0,0 +1,24 @@ +{...}: { + flake.modules.homeManager.thunderbird = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".thunderbird" + ".cache/thunderbird" + ]; + }; + + programs.thunderbird = { + enable = true; + profiles = { + "main" = { + isDefault = true; + settings = { + "calendar.alarms.showmissed" = false; + "calendar.alarms.playsound" = false; + "calendar.alarms.show" = false; + }; + }; + }; + }; + }; +} diff --git a/parts/features/apps/tomat.nix b/parts/features/apps/tomat.nix new file mode 100644 index 00000000..1ff37482 --- /dev/null +++ b/parts/features/apps/tomat.nix @@ -0,0 +1,7 @@ +{...}: { + flake.modules.homeManager.tomat = {...}: { + services.tomat = { + enable = true; + }; + }; +} diff --git a/parts/features/apps/trash-cli.nix b/parts/features/apps/trash-cli.nix new file mode 100644 index 00000000..a4322bf9 --- /dev/null +++ b/parts/features/apps/trash-cli.nix @@ -0,0 +1,5 @@ +{...}: { + flake.modules.homeManager.trash-cli = {pkgs, ...}: { + home.packages = [pkgs.trash-cli]; + }; +} diff --git a/parts/features/apps/tray-tui.nix b/parts/features/apps/tray-tui.nix new file mode 100644 index 00000000..27808e40 --- /dev/null +++ b/parts/features/apps/tray-tui.nix @@ -0,0 +1,8 @@ +# TUI for tray icons +{...}: { + flake.modules.homeManager.tray-tui = {...}: { + programs.tray-tui = { + enable = true; + }; + }; +} diff --git a/parts/features/apps/urxvt.nix b/parts/features/apps/urxvt.nix new file mode 100644 index 00000000..e720079c --- /dev/null +++ b/parts/features/apps/urxvt.nix @@ -0,0 +1,32 @@ +{...}: { + flake.modules.homeManager.urxvt = {pkgs, ...}: { + programs.urxvt = { + enable = true; + + # Perl extensions + extraConfig = { + perl-ext-common = "default,matcher,resize-font,vtwheel,keyboard-select,-searchable-scrollback"; + # Matcher (clickable URLs) + url-launcher = "${pkgs.xdg-utils}/bin/xdg-open"; + "matcher.button" = 1; + # Messes with CTRL+SHIFT Keybindings, see https://wiki.archlinux.org/index.php/Rxvt-unicode#Perl_extensions + iso14755_52 = false; + # https://github.com/muennich/urxvt-perls#keyboard-select + "keyboard-select.clipboard" = true; + }; + + fonts = ["xft:FiraCode Nerd Font Mono:size=8"]; + iso14755 = false; + + keybindings = { + "Shift-Control-C" = "eval:selection_to_clipboard"; + "Shift-Control-V" = "eval:paste_clipboard"; + "Meta-Escape" = "perl:keyboard-select:activate"; + "Meta-Shift-S" = "perl:keyboard-select:search"; + }; + + package = pkgs.rxvt-unicode; + scroll.bar.enable = false; + }; + }; +} diff --git a/home/variety/variety.conf b/parts/features/apps/variety/_variety.conf similarity index 100% rename from home/variety/variety.conf rename to parts/features/apps/variety/_variety.conf diff --git a/parts/features/apps/variety/default.nix b/parts/features/apps/variety/default.nix new file mode 100644 index 00000000..a9f3fea4 --- /dev/null +++ b/parts/features/apps/variety/default.nix @@ -0,0 +1,7 @@ +# Wallpaper Switcher/Randomizer with Quotes +{...}: { + flake.modules.homeManager.variety = {pkgs, ...}: { + home.packages = [pkgs.variety]; + home.file.".config/variety/variety.conf".source = ./_variety.conf; + }; +} diff --git a/parts/features/apps/virtual-cable/default.nix b/parts/features/apps/virtual-cable/default.nix new file mode 100644 index 00000000..59ff2aca --- /dev/null +++ b/parts/features/apps/virtual-cable/default.nix @@ -0,0 +1,23 @@ +# Virtual inputs/outputs via Pipewire (for OBS and beyond) +{...}: { + flake.modules.homeManager.virtual-cable = {pkgs, ...}: let + obs-mic = pkgs.writers.writeBashBin "obs-mic" (builtins.readFile ./scripts/obs-mic.sh); + in { + home.packages = [obs-mic]; + + systemd.user.services.obs-mic = { + Unit = { + After = ["wireplumber.service"]; + Description = "Set up virtualMic and virtualSpeaker for OBS"; + Requires = ["wireplumber.service"]; + }; + Install.WantedBy = ["wireplumber.service"]; + Service = { + Environment = "PATH=$PATH:/run/current-system/sw/bin"; + ExecStartPre = "${pkgs.coreutils}/bin/sleep 5"; # TODO: Find a better way to wait for WirePlumber to fully start + ExecStart = "${obs-mic}/bin/obs-mic"; + Type = "oneshot"; + }; + }; + }; +} diff --git a/home/virtual-cable/scripts/obs-mic.sh b/parts/features/apps/virtual-cable/scripts/obs-mic.sh similarity index 100% rename from home/virtual-cable/scripts/obs-mic.sh rename to parts/features/apps/virtual-cable/scripts/obs-mic.sh diff --git a/parts/features/apps/vlc.nix b/parts/features/apps/vlc.nix new file mode 100644 index 00000000..b58423b2 --- /dev/null +++ b/parts/features/apps/vlc.nix @@ -0,0 +1,19 @@ +{...}: { + flake.modules.homeManager.vlc = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/vlc" + ".local/share/vlc" + ]; + }; + + home.packages = [ + pkgs.vlc + ]; + }; +} diff --git a/parts/features/apps/wireshark.nix b/parts/features/apps/wireshark.nix new file mode 100644 index 00000000..f6ff67fe --- /dev/null +++ b/parts/features/apps/wireshark.nix @@ -0,0 +1,7 @@ +{...}: { + flake.modules.nixos.wireshark = {pkgs, ...}: { + environment.systemPackages = [pkgs.wireshark]; + programs.wireshark.enable = true; + users.users.farlion.extraGroups = ["wireshark"]; + }; +} diff --git a/parts/features/apps/witr.nix b/parts/features/apps/witr.nix new file mode 100644 index 00000000..18c51af7 --- /dev/null +++ b/parts/features/apps/witr.nix @@ -0,0 +1,6 @@ +# Why is this running? +{...}: { + flake.modules.homeManager.witr = {pkgs, ...}: { + home.packages = [pkgs.unstable.witr]; + }; +} diff --git a/parts/features/apps/wluma.nix b/parts/features/apps/wluma.nix new file mode 100644 index 00000000..498c6e10 --- /dev/null +++ b/parts/features/apps/wluma.nix @@ -0,0 +1,8 @@ +# Automatic brightness adjustment based on screen contents and ALS +{...}: { + flake.modules.homeManager.wluma = {lib, osConfig, ...}: { + services.wluma = lib.mkIf osConfig.dendrix.isLaptop { + enable = true; + }; + }; +} diff --git a/parts/features/apps/xdg.nix b/parts/features/apps/xdg.nix new file mode 100644 index 00000000..085480d9 --- /dev/null +++ b/parts/features/apps/xdg.nix @@ -0,0 +1,64 @@ +{...}: { + flake.modules.homeManager.xdg = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".xournal" # Other recently used files + ]; + # TODO: This somehow tries to replace the file after it's created by something else + # See https://github.com/nix-community/impermanence/issues/147 + # files = [ + # ".local/share/recently-used.xbel" # Recently used files + # ]; + }; + + home.packages = [ + pkgs.selectdefaultapplication # GUI XDG Default Application Chooser + ]; + + home.sessionVariables = { + XDG_CONFIG_HOME = "/home/farlion/.config"; + }; + + home.preferXdgDirectories = true; + + xdg = { + mimeApps = { + associations = { + added = { + "x-scheme-handler/tg" = "org.telegram.desktop.desktop"; + }; + }; + enable = true; + defaultApplications = { + "application/pdf" = ["okular.desktop"]; + "applications/x-www-browser" = ["brave-browser.desktop"]; + "image/bmp" = ["oculante.desktop"]; + "image/gif" = ["oculante.desktop"]; + "image/jpeg" = ["oculante.desktop"]; + "image/png" = ["oculante.desktop"]; + "image/svg+xml" = ["oculante.desktop"]; + "image/tiff" = ["oculante.desktop"]; + "image/webp" = ["oculante.desktop"]; + "inode/directory" = ["lf.desktop"]; + "text/html" = ["brave-browser.desktop"]; + "text/plain" = ["nvim.desktop"]; + "x-scheme-handler/about" = ["brave-browser.desktop"]; + "x-scheme-handler/http" = ["brave-browser.desktop"]; + "x-scheme-handler/https" = ["brave-browser.desktop"]; + "x-scheme-handler/mailto" = ["brave-browser.desktop"]; + "x-scheme-handler/msteams" = ["teams-for-linux.desktop"]; + "x-scheme-handler/slack" = ["slack.desktop"]; + "x-scheme-handler/tg" = ["org.telegram.desktop.desktop"]; + "x-scheme-handler/tonsite" = ["org.telegram.desktop.desktop"]; + "x-scheme-handler/unknown" = ["brave-browser.desktop"]; + "x-scheme-handler/webcal" = ["brave-browser.desktop"]; + }; + }; + }; + }; +} diff --git a/parts/features/apps/ytmdesktop.nix b/parts/features/apps/ytmdesktop.nix new file mode 100644 index 00000000..ada9439f --- /dev/null +++ b/parts/features/apps/ytmdesktop.nix @@ -0,0 +1,28 @@ +{...}: { + flake.modules.homeManager.ytmdesktop = {lib, osConfig, pkgs, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/YouTube Music Desktop App" + ]; + }; + + home.packages = [ + pkgs.unstable.ytmdesktop + ]; + + # Override the desktop file to add --password-store flag for Last.fm integration + # See: https://github.com/ytmdesktop/ytmdesktop/issues/1428 + xdg.desktopEntries.ytmdesktop = { + name = "YouTube Music Desktop App"; + genericName = "Music Player"; + comment = "YouTube Music Desktop App"; + exec = "${pkgs.unstable.ytmdesktop}/bin/ytmdesktop --password-store=\"gnome-libsecret\" %U"; + icon = "ytmdesktop"; + terminal = false; + type = "Application"; + categories = ["Audio" "AudioVideo" "Player"]; + mimeType = ["x-scheme-handler/ytmd"]; + startupNotify = true; + }; + }; +} diff --git a/parts/features/apps/zen.nix b/parts/features/apps/zen.nix new file mode 100644 index 00000000..a537d151 --- /dev/null +++ b/parts/features/apps/zen.nix @@ -0,0 +1,55 @@ +{inputs, ...}: { + flake.modules.homeManager.zen = { + config, + lib, + osConfig, + pkgs, + ... + }: let + # Try to focus an existing Zen window on link open so the workspace comes to the foreground + zenNiriOpen = pkgs.writeShellApplication { + name = "zen-niri-open"; + runtimeInputs = [pkgs.niri pkgs.jq pkgs.coreutils config.programs.zen-browser.package]; + text = '' + zen_id=$(niri msg --json windows | jq -r '.[] | select(.app_id == "zen-browser" or .app_id == "zen") | .id' | head -n1 || true) + if [ -n "''${zen_id:-}" ]; then + niri msg action focus-window --id "$zen_id" >/dev/null 2>&1 || true + fi + exec ${config.programs.zen-browser.package}/bin/zen "$@" + ''; + }; + in { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".zen" + ]; + }; + + home.packages = [zenNiriOpen]; + + imports = [ + inputs.zen-browser.homeModules.beta + ]; + + programs.zen-browser = { + enable = true; + profiles = { + main = { + extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [ + bitwarden + darkreader + ublock-origin + ]; + }; + }; + }; + + stylix.targets.zen-browser.profileNames = ["main"]; + + home.sessionVariables = { + BROWSER = "${zenNiriOpen}/bin/zen-niri-open"; + DEFAULT_BROWSER = "${zenNiriOpen}/bin/zen-niri-open"; + MOZ_LEGACY_PROFILES = 1; # Temporary fix, see https://github.com/0xc000022070/zen-browser-flake/issues/179 + }; + }; +} diff --git a/parts/features/apps/zoom.nix b/parts/features/apps/zoom.nix new file mode 100644 index 00000000..254ef73d --- /dev/null +++ b/parts/features/apps/zoom.nix @@ -0,0 +1,23 @@ +{...}: { + flake.modules.homeManager.zoom = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + files = [ + ".config/zoom.conf" + ".config/zoomus.conf" + ]; + directories = [ + ".zoom" + ".cache/zoom" + ]; + }; + + home.packages = [ + pkgs.zoom-us + ]; + }; +} diff --git a/parts/features/apps/zoxide.nix b/parts/features/apps/zoxide.nix new file mode 100644 index 00000000..3f7a33e1 --- /dev/null +++ b/parts/features/apps/zoxide.nix @@ -0,0 +1,12 @@ +{...}: { + flake.modules.homeManager.zoxide = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".cache/zoxide" # Some stuff for nushell + ".local/share/zoxide" # Zoxide DB + ]; + }; + + programs.zoxide.enable = true; + }; +} diff --git a/parts/features/core/boot.nix b/parts/features/core/boot.nix new file mode 100644 index 00000000..dceaccd9 --- /dev/null +++ b/parts/features/core/boot.nix @@ -0,0 +1,16 @@ +{...}: { + flake.modules.nixos.boot = {...}: { + boot = { + loader.systemd-boot = { + enable = true; + memtest86.enable = true; + }; + loader.efi.canTouchEfiVariables = true; + consoleLogLevel = 7; + + initrd = { + systemd.enable = true; + }; + }; + }; +} diff --git a/parts/features/core/cachix.nix b/parts/features/core/cachix.nix new file mode 100644 index 00000000..d9984b78 --- /dev/null +++ b/parts/features/core/cachix.nix @@ -0,0 +1,5 @@ +{...}: { + flake.modules.nixos.cachix = {pkgs, ...}: { + environment.systemPackages = [pkgs.cachix]; + }; +} diff --git a/parts/features/core/dns.nix b/parts/features/core/dns.nix new file mode 100644 index 00000000..ef5c511a --- /dev/null +++ b/parts/features/core/dns.nix @@ -0,0 +1,14 @@ +{...}: { + flake.modules.nixos.dns = {...}: { + services.resolved = { + enable = true; + llmnr = "false"; # https://www.blackhillsinfosec.com/how-to-disable-llmnr-why-you-want-to/ + extraConfig = '' + MulticastDNS=false + DNSStubListenerExtra=172.17.0.1 + ''; + fallbackDns = []; # Ensure we always go through the configured DNS, no magic defaults + }; + networking.networkmanager.dns = "systemd-resolved"; + }; +} diff --git a/system/fonts/scripts/patch-fira-with-fa6-pro.sh b/parts/features/core/fonts/_scripts/patch-fira-with-fa6-pro.sh similarity index 100% rename from system/fonts/scripts/patch-fira-with-fa6-pro.sh rename to parts/features/core/fonts/_scripts/patch-fira-with-fa6-pro.sh diff --git a/parts/features/core/fonts/default.nix b/parts/features/core/fonts/default.nix new file mode 100644 index 00000000..fc99dbb4 --- /dev/null +++ b/parts/features/core/fonts/default.nix @@ -0,0 +1,77 @@ +{...}: { + flake.modules.nixos.fonts = {pkgs, ...}: let + # Looted from https://gist.github.com/elijahmanor/c10e5787bf9ac6b8c276e47e6745826c, much obliged + fontSmokeTest = pkgs.writers.writeBashBin "font-smoke-test" '' + set -e + + printf "%b\n" "Normal" + printf "%b\n" "\033[1mBold\033[22m" + printf "%b\n" "\033[3mItalic\033[23m" + printf "%b\n" "\033[3;1mBold Italic\033[0m" + printf "%b\n" "\033[4mUnderline\033[24m" + printf "%b\n" "== === !== >= <= =>" + printf "%b\n" " 󰾆 󱑥 󰒲 󰗼" + ''; + # Patch Fira Code with Nerd Fonts plus local Font Awesome 6 Pro glyphs into ~/.local/share/fonts + patchFiraWithFA6Pro = pkgs.writeShellApplication { + name = "patch-fira-with-fa6-pro"; + runtimeInputs = [ + pkgs.fira-code + pkgs.fontforge + pkgs.findutils + pkgs.coreutils + pkgs.fontconfig + pkgs.nerd-font-patcher + ]; + runtimeEnv = { + SRC_DIR = "${pkgs.fira-code}/share/fonts/truetype"; + }; + text = builtins.readFile ./_scripts/patch-fira-with-fa6-pro.sh; + }; + in { + environment.systemPackages = [ + fontSmokeTest + patchFiraWithFA6Pro + ]; + + # Run the patcher during Home Manager activation so that fonts are ready after switch + home-manager.users.farlion.home.activation.patchFiraWithFA6Pro = '' + OUT_DIR="$HOME/.local/share/fonts/NerdPatched/FiraCodeFAPro" + if [ ! -d "$OUT_DIR" ] || [ -z "$(ls -A "$OUT_DIR" 2>/dev/null)" ]; then + echo "[patch-fira-with-fa6-pro] Patched fonts not found or directory empty, running patcher..." + "${patchFiraWithFA6Pro}/bin/patch-fira-with-fa6-pro" + else + echo "[patch-fira-with-fa6-pro] Patched fonts already exist in $OUT_DIR, skipping..." + fi + ''; + + fonts = { + enableDefaultPackages = false; + packages = [ + pkgs.fira-code + pkgs.fira-code-symbols + pkgs.dejavu_fonts + pkgs.font-awesome_5 + pkgs.font-awesome_6 + pkgs.unstable.font-awesome + pkgs.noto-fonts-color-emoji # emoji font + ]; + fontconfig = { + defaultFonts = { + sansSerif = ["DejaVu Sans"]; + serif = ["DejaVu Serif"]; + monospace = ["FiraCode Nerd Font" "Fira Code"]; + }; + }; + }; + }; + + flake.modules.homeManager.fonts = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".local/share/fonts" # Locally persisted fonts (not nixos-managed) + ".cache/fontconfig" # Fontconfig cache + ]; + }; + }; +} diff --git a/parts/features/core/home-cleanup-todo.nix b/parts/features/core/home-cleanup-todo.nix new file mode 100644 index 00000000..32c9132a --- /dev/null +++ b/parts/features/core/home-cleanup-todo.nix @@ -0,0 +1,115 @@ +{inputs, ...}: { + flake.modules.homeManager.home-cleanup-todo = { + config, + lib, + osConfig, + pkgs, + ... + }: { + # Symlink flake for `home-manager news` CLI to find homeConfigurations + home.file."nixos-config" = { + source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/code/nixos-config"; + target = "nixos-config"; + }; + + home.file.".config/home-manager/flake.nix" = { + source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/code/nixos-config/flake.nix"; + }; + + home.packages = with pkgs; [ + alejandra # Nix Formatter + ast-grep # Pure Magic + bc # calculator + bind # Provides dig + dconf # Gnome configuration database + difftastic # structural diff difft, see https://github.com/Wilfred/difftastic + dive # Analyze docker images + dmidecode # Hardware info read from Bios + dnstracer + efivar # Tools and Libraries to manipulate EFI variables + fast-cli # Fast.com CLI `fast` + fastfetch # neofetch sucessor, system information tool + fd # Better find, written in Rust + ffmpeg-full + file # CLI program to show the type of a file + find-cursor + fortune + glow # Terminal markdown renderer + gomatrix # The Matrix + google-chrome + gucharmap # Unicode Character Map + hardinfo2 # Hardware/System Info + home-manager # CLI for managing home-manager, needed for `home-manager news` + httpie + iftop # Net top tool, see also nethogs + imagemagick + iotop-c + jq + kind # Kubernetes In Docker + kdePackages.kruler # Screen ruler + lazydocker # kind for vanilla Docker, kind of + libnotify # Provides notify-send + libsecret # `secret-tool` for interacting with gnome-keyring + lm_sensors # Tools for reading hardware sensors + lolcat # Pipe and See + lsof # Tool to list open file + ncdu # Disk Space Usage Visualization + nmap # Port Scanner + nethogs # Net top tool, see also iftop + net-tools # Things like arp, ifconfig, route, netstat etc... + neo-cowsay + nix-tree + oculante # img viewer written in Rust + kdePackages.okular # KDE document viewer + openssl + pdftk # PDF Manipulation Toolkit + pstree # Show the set of running processes as a tree + q-text-as-data # https://github.com/harelba/q + inputs.rmob.defaultPackage.x86_64-linux + screenkey # Screencast tool to display your keys inspired by Screenflick + smartmontools # Tools for monitoring the health of hard drives + s-tui # Processor monitor/stress test + stress # Simple workload generator for POSIX systems. It imposes a configurable amount of CPU, memory, I/O, and disk stress on the system + tcpdump + traceroute + tree + unzip + usbutils # Provides lsusb + wdisplays # arandr for wayland - external display/screen GUI + wf-recorder # Screen recorder for Wayland, useful for quick testing screen stuff + wget + wireguard-tools + whois + wl-clipboard + xournalpp # PDF Annotations, useful for saving Okular annotations as well + yq # Command-line YAML/XML/TOML processor - jq wrapper for YAML, XML, TOML documents + yt-dlp + zip + ]; + + home.sessionVariables = + { + PATH = "$HOME/bin:$PATH"; + NIXOS_CONFIG = "$HOME/code/nixos-config/"; + GC_INITIAL_HEAP_SIZE = "8G"; # Slightly improve nix eval times + DIRENV_LOG_FORMAT = ""; # Disable verbose direnv output showing env variables changed + NIXOS_OZONE_WL = "1"; # Enable Ozone-Wayland for Electron apps and Chromium + } + // lib.optionalAttrs osConfig.dendrix.hasNvidia { + LIBVA_DRIVER_NAME = "nvidia"; + }; + + programs.bat = { + enable = true; + }; + + programs.man = { + enable = true; + generateCaches = false; # Speed up builds + }; + + programs.vscode = { + enable = true; + }; + }; +} diff --git a/parts/features/core/impermanence.nix b/parts/features/core/impermanence.nix new file mode 100644 index 00000000..36812518 --- /dev/null +++ b/parts/features/core/impermanence.nix @@ -0,0 +1,124 @@ +# General impermanence setup +# Note: specifics should live with their respective modules, where possible! +{...}: { + flake.modules.homeManager.impermanence = { + osConfig, + lib, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".cache/nix" + ".config/helm" # Helm repositories + ".config/nix" # cachix repositories and such + ".local/share/home-manager" # home-manager news read state + ".local/share/nix" # Nix Repl History + ".local/state/home-manager" # home-manager generations and GC roots + ]; + }; + }; + + flake.modules.nixos.impermanence = { + config, + lib, + pkgs, + ... + }: let + rootExplosion = '' + echo "Time to 🧨" >/dev/kmsg + mkdir /btrfs_tmp + mount /dev/mapper/nixos--vg-root /btrfs_tmp + + # Root impermanence + if [[ -e /btrfs_tmp/root ]]; then + mkdir -p /btrfs_tmp/persist/old_roots + timestamp=$(date --date="@$(stat -c %Y /btrfs_tmp/root)" "+%Y-%m-%d_%H:%M:%S") + if [[ ! -e /btrfs_tmp/persist/old_roots/$timestamp ]]; then + mv /btrfs_tmp/root "/btrfs_tmp/persist/old_roots/$timestamp" + else + btrfs subvolume delete /btrfs_tmp/root + fi + fi + + ### + # GC + ### + latest_snapshot=$(find /btrfs_tmp/persist/old_roots/ -mindepth 1 -maxdepth 1 -type d | sort -r | head -n 1) + # Only delete old snapshots if there's at least one that will remain after deletion + if [ -n "$latest_snapshot" ]; then + for i in $(find /btrfs_tmp/persist/old_roots/ -mindepth 1 -maxdepth 1 -mtime +30 | grep -v -e "$latest_snapshot"); do + btrfs subvolume delete -R "$i" + done + fi + + btrfs subvolume create /btrfs_tmp/root + umount /btrfs_tmp + echo "Done with 🧨. Au revoir!" >/dev/kmsg + ''; + in + lib.mkIf config.dendrix.isImpermanent { + # Explode / on every boot and resume, see https://grahamc.com/blog/erase-your-darlings/ + boot.initrd.systemd = { + extraBin = { + grep = "${pkgs.gnugrep}/bin/grep"; + }; + services = { + root-explode = { + enableStrictShellChecks = false; + wantedBy = ["initrd-root-device.target"]; + wants = ["lvm2-activation.service"]; + # See https://github.com/nix-community/impermanence/issues/250#issuecomment-2603848867 + after = ["lvm2-activation.service" "local-fs-pre.target"]; + before = ["sysroot.mount"]; + # Run on cold boot only, never on resume from hibernation + unitConfig = { + ConditionKernelCommandLine = ["!resume="]; + RequiresMountsFor = ["/dev/mapper/nixos--vg-root"]; + }; + serviceConfig = { + StandardOutput = "journal+console"; + StandardError = "journal+console"; + Type = "oneshot"; + }; + script = rootExplosion; + }; + }; + }; + + boot.tmp.cleanOnBoot = true; + + fileSystems."/persist".neededForBoot = true; + + environment.persistence."/persist/system" = { + enable = true; + hideMounts = true; + directories = [ + "/root/.cache/nix" + "/var/lib/logrotate" # See https://github.com/nix-community/impermanence/issues/270 + "/var/lib/nixos" + "/var/lib/systemd/coredump" + "/var/lib/systemd/timers" + "/var/lib/udisks2" + "/var/log" + ]; + files = ["/etc/machine-id"]; + }; + # Workaround for /etc/ file timings not working with impermanence + environment.etc = { + # Timezone data linked by tzupdate + "localtime".source = "/persist/system/etc/localtime"; + }; + + # Woraround for logrotate, see https://github.com/nix-community/impermanence/issues/270 + services.logrotate.extraArgs = lib.mkAfter ["--state" "/var/lib/logrotate/logrotate.status"]; + + # home-manager's impermanence module doesn't have permissions to bootstrap these dirs, so we do it here: + system.activationScripts.bootstrapPersistHome.text = '' + mkdir -p /persist/home/farlion + chown farlion:users /persist/home/farlion + chmod 0700 /persist/home/farlion + ''; + + programs.fuse.userAllowOther = true; # Needed for home-manager's impermanence allowOther option to work + }; +} diff --git a/parts/features/core/kernel.nix b/parts/features/core/kernel.nix new file mode 100644 index 00000000..e0a753a1 --- /dev/null +++ b/parts/features/core/kernel.nix @@ -0,0 +1,24 @@ +{...}: { + flake.modules.nixos.kernel = {pkgs, ...}: { + # Writes to /etc/sysctl.d/60-nixos.conf + boot.kernel.sysctl = { + # Enable all magic sysrq commands (NixOS sets this to 16, which enables sync only) + "kernel.sysrq" = 1; + "vm.swappiness" = 20; # balanced setting favoring RAM usage, Default=60 + }; + + boot.kernelPackages = pkgs.linuxPackages_zen; # Optimized for desktop use + environment.systemPackages = with pkgs; [ + perf + linuxKernel.packages.linux_zen.cpupower + ]; + + boot.initrd.verbose = true; + # Keep console visible during teardown + boot.kernelParams = [ + "systemd.show_status=1" + "i915.enable_psr=0" # Intel PSR often blanks the console on transitions + "i915.fastboot=0" # avoid early/quiet KMS handover + ]; + }; +} diff --git a/parts/features/core/locale.nix b/parts/features/core/locale.nix new file mode 100644 index 00000000..8116ec3c --- /dev/null +++ b/parts/features/core/locale.nix @@ -0,0 +1,25 @@ +{...}: { + flake.modules.nixos.locale = {pkgs, ...}: { + environment.systemPackages = [pkgs.comma]; + + services.atd.enable = true; + + boot.supportedFilesystems = ["ntfs"]; + + services.tzupdate = { + enable = true; + timer.enable = false; + }; + systemd.services.tzupdate = { + after = ["network-online.target"]; + wants = ["network-online.target"]; + serviceConfig = { + Restart = "on-failure"; + RestartSec = "30s"; + RestartMode = "direct"; + }; + }; + + i18n.defaultLocale = "en_US.UTF-8"; + }; +} diff --git a/parts/features/core/networking/_scripts/tailscale-ip.sh b/parts/features/core/networking/_scripts/tailscale-ip.sh new file mode 100644 index 00000000..dd380671 --- /dev/null +++ b/parts/features/core/networking/_scripts/tailscale-ip.sh @@ -0,0 +1,9 @@ +set -euo pipefail + +isOnline=$(tailscale status --json | jq -r '.Self.Online') +if [[ "$isOnline" == "true" ]]; then + tailscaleIp=$(tailscale status --json | jq -r '.Self.TailscaleIPs[0]') + echo "{\"icon\": \"tailscale_up\", \"text\": \"$tailscaleIp\", \"state\": \"Good\"}" +else + echo "{\"icon\": \"tailscale_down\", \"text\": \"\", \"state\": \"Idle\"}" +fi diff --git a/parts/features/core/networking/default.nix b/parts/features/core/networking/default.nix new file mode 100644 index 00000000..148331a8 --- /dev/null +++ b/parts/features/core/networking/default.nix @@ -0,0 +1,85 @@ +{...}: { + flake.modules.nixos.networking = { + config, + lib, + pkgs, + ... + }: let + # Get the current tailscale ip if tailscale is up + tailscale-ip = pkgs.writers.writeBashBin "tailscale-ip" ( + builtins.readFile ./_scripts/tailscale-ip.sh + ); + in { + environment.persistence."/persist/system" = lib.mkIf config.dendrix.isImpermanent { + directories = [ + "/etc/NetworkManager/system-connections" + "/var/lib/tailscale" + "/var/lib/NetworkManager" + ]; + }; + + environment.systemPackages = [ + pkgs.pwru # eBPF-based linux kernel networking debugger + tailscale-ip # Get the current tailscale IP if tailscale is up + ]; + + networking.firewall = { + # if packets are dropped, they will show up in dmesg + logReversePathDrops = true; + logRefusedPackets = true; + # logRefusedUnicastsOnly = false; + }; + + # Tailscale + services.tailscale = { + enable = true; + package = pkgs.unstable.tailscale; + useRoutingFeatures = "client"; + }; + + # Allow for dynamic hosts file override (by root) + environment.etc.hosts.mode = "0644"; + + networking.firewall.allowedTCPPorts = [ + 22000 # Syncthing TCP + ]; + networking.firewall.allowedUDPPorts = [ + 22000 # Syncthing QUIC + 21027 # Syncthing discovery broadcasts on IPv4 and multicasts on IPv6 + ]; + + # BBR -> Better performance over weak/jittery links + boot.kernel.sysctl = { + "net.core.default_qdisc" = "fq"; + "net.ipv4.tcp_congestion_control" = "bbr"; + }; + + networking.networkmanager.enable = true; + users.users.farlion.extraGroups = ["networkmanager"]; + + # IPv6 + # TODO: Temporarily enabled to allow buggy Hoppscotch to work + #networking.enableIPv6 = false; + #boot.kernelParams = ["ipv6.disable=1"]; + + # Disabling DHCPCD in favor of NetworkManager + networking.dhcpcd.enable = false; + + # Captive Browser + programs.captive-browser = lib.mkIf config.dendrix.isLaptop { + enable = true; + bindInterface = false; + }; + + # Only wait for a single interface to come up + systemd.network.wait-online.anyInterface = true; + }; + + flake.modules.homeManager.networking = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/tailscale" # Tailscale known hosts + ]; + }; + }; +} diff --git a/parts/features/core/nix-ld.nix b/parts/features/core/nix-ld.nix new file mode 100644 index 00000000..466b1b85 --- /dev/null +++ b/parts/features/core/nix-ld.nix @@ -0,0 +1,5 @@ +{...}: { + flake.modules.nixos.nix-ld = {...}: { + programs.nix-ld.enable = true; + }; +} diff --git a/parts/features/core/nix-settings.nix b/parts/features/core/nix-settings.nix new file mode 100644 index 00000000..d6307cd0 --- /dev/null +++ b/parts/features/core/nix-settings.nix @@ -0,0 +1,22 @@ +{...}: { + flake.modules.nixos.nix-settings = {pkgs, ...}: { + nix = { + settings = { + trusted-users = ["root" "farlion" "@wheel" "@sudo"]; + substituters = ["https://cache.nixos.org/"]; + trusted-public-keys = []; + }; + + extraOptions = '' + experimental-features = nix-command flakes + ''; + + nixPath = [ + "nixpkgs=${pkgs.path}" + "nixos-unstable=${pkgs.unstable.path}" + ]; + }; + + nixpkgs.config.allowUnfree = true; + }; +} diff --git a/parts/features/core/performance.nix b/parts/features/core/performance.nix new file mode 100644 index 00000000..1524bdad --- /dev/null +++ b/parts/features/core/performance.nix @@ -0,0 +1,8 @@ +{...}: { + flake.modules.nixos.performance = {...}: { + documentation.man = { + enable = true; + generateCaches = false; # Used for apropos and the -k option of man, but significantly slows down builds + }; + }; +} diff --git a/parts/features/core/smb.nix b/parts/features/core/smb.nix new file mode 100644 index 00000000..9925b5c5 --- /dev/null +++ b/parts/features/core/smb.nix @@ -0,0 +1,6 @@ +{...}: { + flake.modules.nixos.smb = {pkgs, ...}: { + boot.supportedFilesystems = ["cifs"]; + environment.systemPackages = [pkgs.cifs-utils]; + }; +} diff --git a/parts/features/core/systemd.nix b/parts/features/core/systemd.nix new file mode 100644 index 00000000..098a0954 --- /dev/null +++ b/parts/features/core/systemd.nix @@ -0,0 +1,5 @@ +{...}: { + flake.modules.nixos.systemd = {...}: { + systemd.enableStrictShellChecks = true; + }; +} diff --git a/parts/features/core/users.nix b/parts/features/core/users.nix new file mode 100644 index 00000000..13b30e6d --- /dev/null +++ b/parts/features/core/users.nix @@ -0,0 +1,26 @@ +{...}: { + flake.modules.nixos.users = { + lib, + pkgs, + ... + }: { + users.mutableUsers = false; + + users.users.farlion = { + description = "Florian Peter"; + extraGroups = ["disk"]; + group = "users"; + hashedPassword = lib.mkDefault ""; # For CI, otherwise gets overwritten by parent `secrets` flake + isNormalUser = true; + shell = pkgs.fish; + }; + + # Default editor for root + programs.vim = { + defaultEditor = true; + enable = true; + }; + # Enable fish for root + programs.fish.enable = true; + }; +} diff --git a/parts/features/desktop/cliphist.nix b/parts/features/desktop/cliphist.nix new file mode 100644 index 00000000..0a5c084e --- /dev/null +++ b/parts/features/desktop/cliphist.nix @@ -0,0 +1,32 @@ +{...}: { + flake.modules.homeManager.cliphist = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".cache/cliphist" + ]; + }; + + services.cliphist = { + enable = true; + }; + + # Fix cliphist systemd service to start after Niri is ready + systemd.user.services.cliphist = { + Install.WantedBy = lib.mkForce ["niri.service"]; + Unit.Requires = ["niri.service"]; + Unit.After = ["niri.service"]; + }; + systemd.user.services.cliphist-images = { + Install.WantedBy = lib.mkForce ["niri.service"]; + Unit.Requires = ["niri.service"]; + Unit.After = ["niri.service"]; + }; + + home.packages = [pkgs.xdg-utils]; # For image copy/pasting + }; +} diff --git a/parts/features/desktop/dconf.nix b/parts/features/desktop/dconf.nix new file mode 100644 index 00000000..877d7b8f --- /dev/null +++ b/parts/features/desktop/dconf.nix @@ -0,0 +1,9 @@ +{...}: { + flake.modules.homeManager.dconf = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/dconf" + ]; + }; + }; +} diff --git a/parts/features/desktop/display-manager.nix b/parts/features/desktop/display-manager.nix new file mode 100644 index 00000000..3605a897 --- /dev/null +++ b/parts/features/desktop/display-manager.nix @@ -0,0 +1,61 @@ +{...}: { + flake.modules.nixos.display-manager = { + config, + lib, + pkgs, + ... + }: { + environment.persistence."/persist/system" = lib.mkIf config.dendrix.isImpermanent { + files = ["/etc/ly/save.ini"]; + }; + + services.displayManager = { + defaultSession = "niri"; + ly = { + enable = true; + settings = { + animation = "doom"; + hide_borders = true; + }; + }; + }; + + services.gvfs.enable = true; + + programs.niri.enable = true; + + xdg.portal = { + enable = true; + config = { + common = { + default = ["gnome" "gtk"]; + }; + niri = { + default = ["gnome" "gtk"]; + "org.freedesktop.impl.portal.ScreenCast" = ["gnome"]; + "org.freedesktop.impl.portal.Screenshot" = ["gnome"]; + "org.freedesktop.impl.portal.FileChooser" = ["gtk"]; + }; + }; + extraPortals = [ + pkgs.xdg-desktop-portal-gnome + pkgs.xdg-desktop-portal-gtk + ]; + }; + + systemd.user.services.xdg-desktop-portal = { + after = ["xdg-desktop-autostart.target"]; + }; + systemd.user.services.xdg-desktop-portal-gtk = { + after = ["xdg-desktop-autostart.target"]; + }; + systemd.user.services.xdg-desktop-portal-gnome = { + after = ["xdg-desktop-autostart.target"]; + }; + systemd.user.services.niri-flake-polkit = { + after = ["xdg-desktop-autostart.target"]; + }; + + programs.sway.enable = true; + }; +} diff --git a/parts/features/desktop/dunst.nix b/parts/features/desktop/dunst.nix new file mode 100644 index 00000000..6ab80f87 --- /dev/null +++ b/parts/features/desktop/dunst.nix @@ -0,0 +1,20 @@ +{...}: { + flake.modules.homeManager.dunst = {pkgs, ...}: { + services.dunst = { + enable = true; + + iconTheme = { + name = "Papirus-Dark"; + package = pkgs.papirus-icon-theme; + }; + + settings = { + global = { + browser = "brave"; + dmenu = "fuzzel --dmenu"; + follow = "mouse"; + }; + }; + }; + }; +} diff --git a/parts/features/desktop/fuzzel.nix b/parts/features/desktop/fuzzel.nix new file mode 100644 index 00000000..424709c8 --- /dev/null +++ b/parts/features/desktop/fuzzel.nix @@ -0,0 +1,8 @@ +{...}: { + flake.modules.homeManager.fuzzel = {pkgs, ...}: { + programs.fuzzel = { + enable = true; + package = pkgs.unstable.fuzzel; + }; + }; +} diff --git a/parts/features/desktop/gtk-qt.nix b/parts/features/desktop/gtk-qt.nix new file mode 100644 index 00000000..0a71ba79 --- /dev/null +++ b/parts/features/desktop/gtk-qt.nix @@ -0,0 +1,41 @@ +{...}: { + flake.modules.homeManager.gtk-qt = { + lib, + pkgs, + osConfig, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + files = [ + ".config/QtProject.conf" # Stuff like history and lastVisited + ]; + }; + + gtk = { + enable = true; + gtk3 = { + extraConfig = { + gtk-application-prefer-dark-theme = true; + }; + }; + }; + + qt = { + enable = true; + platformTheme.name = "qtct"; + style.name = "kvantum"; + }; + + home.packages = with pkgs; [ + lxappearance # GTK Theme testing + tweaking + libsForQt5.qt5ct # Qt 5 Theme testing + tweaking + qt6Packages.qt6ct # Qt 6 Theme testing + tweaking + ]; + + # https://wiki.archlinux.org/title/HiDPI + home.sessionVariables = { + QT_AUTO_SCREEN_SCALE_FACTOR = "1"; + QT_ENABLE_HIGHDPI_SCALING = "1"; + }; + }; +} diff --git a/parts/features/desktop/kanshi.nix b/parts/features/desktop/kanshi.nix new file mode 100644 index 00000000..5d8ed467 --- /dev/null +++ b/parts/features/desktop/kanshi.nix @@ -0,0 +1,81 @@ +# Wayland autorandr +{...}: { + flake.modules.homeManager.kanshi = {...}: { + services.kanshi = { + enable = true; + settings = [ + { + output = { + alias = "leftLG27"; + criteria = "HDMI-A-2"; + mode = "3840x2160@60.000Hz"; + position = "0,0"; + scale = 2.0; + transform = "90"; + }; + } + { + output = { + alias = "middleLG34"; + criteria = "DP-1"; + mode = "3840x2160@144.050Hz"; + position = "1080,208"; + scale = 1.5; + transform = "normal"; + }; + } + { + output = { + alias = "rightLG27"; + criteria = "HDMI-A-1"; + mode = "3840x2160@60.000Hz"; + position = "3640,0"; + scale = 2.0; + transform = "90"; + }; + } + { + profile = { + name = "numenor"; + outputs = [ + { + criteria = "$leftLG27"; + status = "enable"; + } + { + criteria = "$middleLG34"; + status = "enable"; + } + { + criteria = "$rightLG27"; + status = "enable"; + } + ]; + }; + } + { + profile = { + name = "numenor-movie"; + outputs = [ + { + criteria = "$leftLG27"; + status = "enable"; + position = "0,272"; + transform = "normal"; + } + { + criteria = "$middleLG34"; + status = "enable"; + position = "1920,132"; + } + { + criteria = "$rightLG27"; + status = "disable"; + } + ]; + }; + } + ]; + }; + }; +} diff --git a/home/niri/scripts/niri-auto-column.sh b/parts/features/desktop/niri/_scripts/niri-auto-column.sh similarity index 100% rename from home/niri/scripts/niri-auto-column.sh rename to parts/features/desktop/niri/_scripts/niri-auto-column.sh diff --git a/home/niri/scripts/niri-open-on-workspace.sh b/parts/features/desktop/niri/_scripts/niri-open-on-workspace.sh similarity index 100% rename from home/niri/scripts/niri-open-on-workspace.sh rename to parts/features/desktop/niri/_scripts/niri-open-on-workspace.sh diff --git a/home/niri/scripts/niri-pick-window.sh b/parts/features/desktop/niri/_scripts/niri-pick-window.sh similarity index 100% rename from home/niri/scripts/niri-pick-window.sh rename to parts/features/desktop/niri/_scripts/niri-pick-window.sh diff --git a/home/niri/scripts/niri-qalc.sh b/parts/features/desktop/niri/_scripts/niri-qalc.sh similarity index 100% rename from home/niri/scripts/niri-qalc.sh rename to parts/features/desktop/niri/_scripts/niri-qalc.sh diff --git a/home/niri/scripts/niri-reorder-workspaces.sh b/parts/features/desktop/niri/_scripts/niri-reorder-workspaces.sh similarity index 100% rename from home/niri/scripts/niri-reorder-workspaces.sh rename to parts/features/desktop/niri/_scripts/niri-reorder-workspaces.sh diff --git a/home/niri/scripts/niri-set-wallpaper.sh b/parts/features/desktop/niri/_scripts/niri-set-wallpaper.sh similarity index 100% rename from home/niri/scripts/niri-set-wallpaper.sh rename to parts/features/desktop/niri/_scripts/niri-set-wallpaper.sh diff --git a/home/niri/wallpapers/gruvbox-dark-rainbow.png b/parts/features/desktop/niri/_wallpapers/gruvbox-dark-rainbow.png similarity index 100% rename from home/niri/wallpapers/gruvbox-dark-rainbow.png rename to parts/features/desktop/niri/_wallpapers/gruvbox-dark-rainbow.png diff --git a/home/niri/wallpapers/gruvbox-light-rainbow.png b/parts/features/desktop/niri/_wallpapers/gruvbox-light-rainbow.png similarity index 100% rename from home/niri/wallpapers/gruvbox-light-rainbow.png rename to parts/features/desktop/niri/_wallpapers/gruvbox-light-rainbow.png diff --git a/parts/features/desktop/niri/default.nix b/parts/features/desktop/niri/default.nix new file mode 100644 index 00000000..ff591ba8 --- /dev/null +++ b/parts/features/desktop/niri/default.nix @@ -0,0 +1,638 @@ +{...}: { + flake.modules.homeManager.niri = { + osConfig, + config, + lib, + pkgs, + ... + }: + with lib; let + isNumenor = osConfig.dendrix.hostname == "numenor"; + isNvidia = osConfig.dendrix.hasNvidia; + + leftScreen = + if isNumenor + then "HDMI-A-2" + else null; + mainScreen = + if isNumenor + then "DP-1" + else "eDP-1"; + rightScreen = + if isNumenor + then "HDMI-A-1" + else null; + + locker = "${pkgs.bash}/bin/bash -c '${pkgs.procps}/bin/pgrep -x swaylock || ${pkgs.swaylock-effects}/bin/swaylock --daemonize'"; + suspender = "${pkgs.systemd}/bin/systemctl suspend-then-hibernate"; + + # Wallpaper, until stylix supports it :) + wallpaperSetter = pkgs.writeShellApplication { + name = "niri-set-wallpaper"; + runtimeInputs = [pkgs.swaybg pkgs.procps]; + text = builtins.readFile ./_scripts/niri-set-wallpaper.sh; + }; + + # Window Picker a la rofi + windowPicker = pkgs.writeShellApplication { + name = "niri-pick-window"; + runtimeInputs = [pkgs.niri pkgs.unstable.fuzzel pkgs.jq]; + text = builtins.readFile ./_scripts/niri-pick-window.sh; + }; + + # Calculator via fuzzel + qalc + fuzzelCalc = pkgs.writeShellApplication { + name = "niri-qalc"; + runtimeInputs = [pkgs.unstable.fuzzel pkgs.libqalculate pkgs.wl-clipboard pkgs.libnotify]; + text = builtins.readFile ./_scripts/niri-qalc.sh; + }; + + # Workspace reorderer - maintains logical order after moving workspaces between monitors + workspaceReorderer = pkgs.writeShellApplication { + name = "niri-reorder-workspaces"; + runtimeInputs = [pkgs.niri pkgs.jq]; + text = builtins.readFile ./_scripts/niri-reorder-workspaces.sh; + }; + + # Auto-column - consumes new windows into columns on vertical monitors + autoColumn = pkgs.writeShellApplication { + name = "niri-auto-column"; + runtimeInputs = [pkgs.niri pkgs.jq pkgs.coreutils]; + text = builtins.readFile ./_scripts/niri-auto-column.sh; + }; + + # Open a command and move its window to a workspace once title matches + openOnWorkspace = pkgs.writeShellApplication { + name = "niri-open-on-workspace"; + runtimeInputs = [pkgs.niri pkgs.jq]; + text = builtins.readFile ./_scripts/niri-open-on-workspace.sh; + }; + + niriBinds = { + suffixes, + prefixes, + substitutions ? {}, + }: let + replacer = replaceStrings (attrNames substitutions) (attrValues substitutions); + format = prefix: suffix: let + actual-suffix = + if isList suffix.action + then { + action = head suffix.action; + args = tail suffix.action; + } + else { + inherit (suffix) action; + args = []; + }; + action = replacer "${prefix.action}-${actual-suffix.action}"; + in { + name = "${prefix.key}+${suffix.key}"; + value.action.${action} = actual-suffix.args; + }; + pairs = attrs: fn: + concatMap ( + key: + fn { + inherit key; + action = attrs.${key}; + } + ) (attrNames attrs); + in + listToAttrs (pairs prefixes (prefix: pairs suffixes (suffix: [(format prefix suffix)]))); + in { + home.packages = with pkgs; [ + brightnessctl # For brightness +/- keys + fuzzelCalc # niri-qalc + hyprmagnifier # Screen magnifier for Wayland + playerctl # For play/pause etc... controlling media players that implement MPRIS + qt5.qtwayland # Needed for QT_QPA_PLATFORM=wayland + swaybg # Minmal wallpaper setter for Sway + wallpaperSetter # Specialization-aware wallpaper setting + windowPicker # niri-pick-window + workspaceReorderer # niri-reorder-workspaces + xwayland-satellite # For apps that need Xwayland + ]; + + programs.swaylock = { + enable = true; + package = pkgs.swaylock-effects; + settings = { + debug = false; + show-failed-attempts = true; + ignore-empty-password = true; + screenshots = true; + effect-pixelate = 10; # Pixellation level (higher = more pixelated) + effect-blur = "7x5"; + }; + }; + + services.swayidle = { + enable = true; + events = [ + { + event = "before-sleep"; + command = "${locker}"; + } + { + event = "lock"; + command = "${locker}"; + } + ]; + timeouts = [ + { + timeout = 360; + command = "${locker}"; + } + { + timeout = 370; + command = "/run/current-system/sw/bin/niri msg action power-off-monitors"; + resumeCommand = "${pkgs.coreutils}/bin/sleep 1; /run/current-system/sw/bin/niri msg action power-on-monitors"; + } + { + timeout = 1800; + command = "${suspender}"; + } + ]; + }; + + # Fix swayidle service dependencies for Niri/Wayland session + # Fails to boot with default settings + systemd.user.services.swayidle = { + Unit = { + After = ["niri.service" "graphical-session.target"]; + Wants = ["graphical-session.target"]; + # Override the default ConditionEnvironment to be less strict + ConditionEnvironment = lib.mkForce []; + }; + Service = { + # Add a small delay to double-ensure Wayland display is ready + ExecStartPre = "${pkgs.coreutils}/bin/sleep 2"; + # Restart the service if it fails (useful for session restarts) + Restart = lib.mkForce "on-failure"; + RestartSec = "5"; + }; + }; + + # Auto-column service for vertical monitors + systemd.user.services.niri-auto-column = lib.mkIf isNumenor { + Unit = { + Description = "Auto-consume windows into columns on vertical monitors"; + After = ["niri.service" "graphical-session.target"]; + Wants = ["graphical-session.target"]; + }; + Service = { + ExecStart = "${autoColumn}/bin/niri-auto-column"; + Restart = "on-failure"; + RestartSec = "5"; + }; + Install = { + WantedBy = ["graphical-session.target"]; + }; + }; + + programs.wleave.enable = true; + + # Wallpaper, until stylix supports it :) + home.file.".local/share/wallpapers/gruvbox-light.png".source = ./_wallpapers/gruvbox-light-rainbow.png; + home.file.".local/share/wallpapers/gruvbox-dark.png".source = ./_wallpapers/gruvbox-dark-rainbow.png; + + # TODO: Activate once the Niri flake supports niri 25.11 + # Per-output layout settings for vertical monitors (raw KDL - not exposed in niri-flake settings) + # programs.niri.config = + # lib.optionalString (leftScreen != null) '' + # output "${leftScreen}" { + # layout { + # default-column-width { proportion 1.0; } + # } + # } + # '' + # + lib.optionalString (rightScreen != null) '' + # output "${rightScreen}" { + # layout { + # default-column-width { proportion 1.0; } + # } + # } + # ''; + programs.niri.settings = rec { + # Environment + environment = { + NIXOS_OZONE_WL = "1"; # Enable Ozone-Wayland for Electron apps and Chromium, see https://nixos.wiki/wiki/Wayland + }; + + # Input Settings + input = { + keyboard = { + xkb = { + layout = "us,de,ua,pt"; + options = "eurosign:e,terminate:ctrl_alt_bksp"; + }; + }; + touchpad = { + dwt = true; # Disable touchpad while typing + disabled-on-external-mouse = false; + natural-scroll = false; + tap = true; + tap-button-map = "left-right-middle"; + }; + focus-follows-mouse.enable = true; + }; + + # Cursor Settings + cursor = { + hide-after-inactive-ms = 3000; + hide-when-typing = true; + }; + + # Startup + spawn-at-startup = [ + {command = ["${pkgs.bash}/bin/bash" "-c" "sleep 10 && systemctl --user restart xdg-desktop-portal"];} # Hacks around a timing prob with xdg-desktop-portal on first boot, see https://github.com/sodiboo/niri-flake/issues/509 + {command = ["systemctl" "--user" "restart" "kanshi"];} + {command = ["systemctl" "--user" "restart" "app-blueman@autostart"];} + {command = ["systemctl" "--user" "start" "gnome-keyring-ssh"];} # Start GNOME Keyring SSH agent + {command = ["obsidian"];} + {command = ["ytmdesktop" "--password-store=gnome-libsecret"];} + # {command = ["seahorse"];} # To unlock keyring + {command = ["${wallpaperSetter}/bin/niri-set-wallpaper"];} # Set wallpaper + {command = ["wlsunset-waybar"];} + {command = ["${openOnWorkspace}/bin/niri-open-on-workspace" "${workspaces."00".name}" "ChatGPT" "zen" "--new-window" "https://chatgpt.com/"];} + {command = ["${openOnWorkspace}/bin/niri-open-on-workspace" "${workspaces."09".name}" "[Vv]ikunja" "zen" "--new-window" "https://vikunja.hyena-byzantine.ts.net/"];} + ]; + + # Window Rules + # Find app_id or title with `niri msg windows` + window-rules = [ + { + matches = [ + {app-id = "^obsidian$";} + ]; + open-on-workspace = " 7"; + } + { + matches = [ + {app-id = "^signal$";} + {app-id = "^teams-for-linux$";} + {app-id = "^org.telegram.desktop$";} + ]; + open-on-workspace = " 8"; + } + { + matches = [ + {app-id = "^YouTube Music Desktop App$";} + ]; + open-on-workspace = " 9"; + } + { + matches = [ + {title = ".*[Vv]ikunja.*";} + ]; + open-on-workspace = " 9"; + } + { + matches = [ + {title = ".*ChatGPT.*";} + ]; + open-on-workspace = " a"; + } + # Floating windows + { + matches = [ + {title = ".*Pavucontrol.*";} + {title = ".*zoom.*";} + ]; + open-floating = true; + } + # Block from screencasting + { + matches = [ + {app-id = "^Bitwarden$";} + {app-id = "^com.obsproject.Studio$";} + ]; + block-out-from = "screen-capture"; + } + # Screen Cast Target Highlight + { + matches = [ + {is-window-cast-target = true;} + ]; + border = { + active = {color = "#f38ba8";}; + inactive = {color = "#7d0d2d";}; + }; + shadow = { + color = "#7d0d2d70"; + }; + tab-indicator = { + active = {color = "#f38ba8";}; + inactive = {color = "#7d0d2d";}; + }; + } + ]; + + # Named Workspaces + workspaces = { + "00" = { + name = " a"; + open-on-output = rightScreen; + }; + "01" = { + name = " 1"; + open-on-output = leftScreen; + }; + "02" = { + name = " 2"; + open-on-output = mainScreen; + }; + "03" = { + name = " 3"; + open-on-output = rightScreen; + }; + "04" = { + name = " 4"; + open-on-output = mainScreen; + }; + "05" = { + name = " 5"; + open-on-output = mainScreen; + }; + "06" = { + name = " 6"; + open-on-output = mainScreen; + }; + "07" = { + name = " 7"; + open-on-output = rightScreen; + }; + "08" = { + name = " 8"; + open-on-output = mainScreen; + }; + "09" = { + name = " 9"; + open-on-output = leftScreen; + }; + "10" = { + name = " 10"; + open-on-output = mainScreen; + }; + }; + + # Layout + layout = { + border = { + enable = true; + width = 2; # Default 4 + }; + + default-column-width.proportion = 1. / 2.; + + gaps = 4; # Default 16 + + preset-column-widths = [ + {proportion = 1. / 2.;} + {proportion = 1. / 3.;} + {proportion = 2. / 3.;} + ]; + + shadow = { + enable = true; + }; + }; + + # Style + prefer-no-csd = true; + + # Animations + animations = { + workspace-switch.enable = false; + }; + + # Keybindings + hotkey-overlay.skip-at-startup = true; + binds = with config.lib.niri.actions; + lib.attrsets.mergeAttrsList [ + { + "Mod+Shift+Slash".action = show-hotkey-overlay; + + "Mod+Return".action = spawn "alacritty"; + "Mod+Return".hotkey-overlay.title = "Open a Terminal: alacritty"; + "Mod+D".action = spawn "fuzzel"; + "Mod+D".hotkey-overlay.title = "Run an Application: fuzzel"; + "Mod+Shift+D".action = spawn "${windowPicker}/bin/niri-pick-window"; + "Mod+Shift+D".hotkey-overlay.title = "Pick a Window: niri-pick-window"; + "Mod+Shift+X".action = spawn-sh "swaylock --daemonize && niri msg action power-off-monitors"; + "Mod+Shift+X".hotkey-overlay.title = "Lock screen and turn off monitors"; + "Mod+z".action = spawn "hyprmagnifier"; + "Mod+z".hotkey-overlay.title = "Screen magnifier"; + "Mod+Shift+z".action = power-off-monitors; + "Mod+Shift+z".hotkey-overlay.title = "Power off Monitors"; + + "XF86AudioRaiseVolume".action = spawn-sh "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1+"; + "XF86AudioRaiseVolume".allow-when-locked = true; + "XF86AudioLowerVolume".action = spawn-sh "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1-"; + "XF86AudioLowerVolume".allow-when-locked = true; + "XF86AudioMute".action = spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"; + "XF86AudioMute".allow-when-locked = true; + "XF86AudioMicMute".action = spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"; + "XF86AudioMicMute".allow-when-locked = true; + "XF86MonBrightnessUp".action = spawn-sh "brightnessctl --class=backlight set 10%+"; + "XF86MonBrightnessUp".allow-when-locked = true; + + "XF86MonBrightnessDown".action = spawn-sh "brightnessctl --class=backlight set 10%-"; + "XF86MonBrightnessDown".allow-when-locked = true; + + "Mod+Shift+Q".action = close-window; + "Mod+Shift+Q".repeat = false; + } + (niriBinds { + suffixes."Left" = "column-left"; + suffixes."j" = "column-left"; + suffixes."Down" = "window-down"; + suffixes."k" = "window-down"; + suffixes."Up" = "window-up"; + suffixes."l" = "window-up"; + suffixes."Right" = "column-right"; + suffixes."semicolon" = "column-right"; + prefixes."Mod" = "focus"; + prefixes."Mod+Ctrl" = "move"; + prefixes."Mod+Shift" = "focus-monitor"; + prefixes."Mod+Shift+Ctrl" = "move-workspace-to-monitor"; + substitutions."monitor-column" = "monitor"; + substitutions."monitor-window" = "monitor"; + }) + (niriBinds { + suffixes."Home" = "first"; + suffixes."End" = "last"; + prefixes."Mod" = "focus-column"; + prefixes."Mod+Ctrl" = "move-column-to"; + }) + (niriBinds { + suffixes."U" = "workspace-down"; + suffixes."Page_Down" = "workspace-down"; + suffixes."I" = "workspace-up"; + suffixes."Page_Up" = "workspace-up"; + prefixes."Mod" = "focus"; + prefixes."Mod+Ctrl" = "move-window-to"; + prefixes."Mod+Shift" = "move"; + }) + (niriBinds { + suffixes = { + "a" = ["workspace" "${workspaces."00".name}"]; + "1" = ["workspace" "${workspaces."01".name}"]; + "2" = ["workspace" "${workspaces."02".name}"]; + "3" = ["workspace" "${workspaces."03".name}"]; + "4" = ["workspace" "${workspaces."04".name}"]; + "5" = ["workspace" "${workspaces."05".name}"]; + "6" = ["workspace" "${workspaces."06".name}"]; + "7" = ["workspace" "${workspaces."07".name}"]; + "8" = ["workspace" "${workspaces."08".name}"]; + "9" = ["workspace" "${workspaces."09".name}"]; + "0" = ["workspace" "${workspaces."10".name}"]; + }; + prefixes."Mod" = "focus"; + prefixes."Mod+Ctrl" = "move-column-to"; + }) + { + "Mod+BracketLeft".action = consume-or-expel-window-left; + "Mod+BracketRight".action = consume-or-expel-window-right; + + "Mod+Comma".action = consume-window-into-column; + "Mod+Period".action = expel-window-from-column; + + "Mod+R".action = switch-preset-column-width; + "Mod+Shift+R".action = switch-preset-window-height; + "Mod+Ctrl+R".action = reset-window-height; + + "Mod+F".action = maximize-column; + "Mod+Shift+F".action = fullscreen-window; + "Mod+Ctrl+F".action = expand-column-to-available-width; + + "Mod+C".action = center-column; + "Mod+Ctrl+C".action = center-visible-columns; + + "Mod+Minus".action = set-column-width "-10%"; + "Mod+Equal".action = set-column-width "+10%"; + "Mod+Shift+Minus".action = set-window-height "-10%"; + "Mod+Shift+Equal".action = set-window-height "+10%"; + + "Mod+V".action = toggle-window-floating; + "Mod+Shift+V".action = switch-focus-between-floating-and-tiling; + + "Mod+Shift+W".action = spawn-sh ( + builtins.concatStringsSep "; " [ + "systemctl --user restart waybar.service" + "${wallpaperSetter}/bin/niri-set-wallpaper" + ] + ); + "Mod+Shift+W".hotkey-overlay.title = "Restart Waybar"; + + "Mod+Space".action = switch-layout "next"; + "Mod+Shift+Space".action = switch-layout "prev"; + + "Print".action.screenshot = []; + "Print".hotkey-overlay.title = "Screenshot via Niri"; + "Mod+Print".action = spawn "satty-screenshot"; + "Mod+Print".hotkey-overlay.title = "Screenshot via Satty"; + "Mod+Shift+Print".action.screenshot-screen = []; + "Mod+Shift+Print".hotkey-overlay.title = "Instant Screenshot"; + + # Applications such as remote-desktop clients and software KVM switches may + # request that niri stops processing the keyboard shortcuts defined here + # so they may, for example, forward the key presses as-is to a remote machine. + # It's a good idea to bind an escape hatch to toggle the inhibitor, + # so a buggy application can't hold your session hostage. + # + # The allow-inhibiting=false property can be applied to other binds as well, + # which ensures niri always processes them, even when an inhibitor is active. + "Mod+Shift+Escape".action = toggle-keyboard-shortcuts-inhibit; + "Mod+Shift+Escape".allow-inhibiting = false; + + "Ctrl+Alt+Delete".action = quit; + } + + { + # Dynamic Cast ([G]rab Window or Screen) + "Mod+G".action = set-dynamic-cast-window; + "Mod+Shift+G".action = set-dynamic-cast-monitor; + "Mod+Delete".action = clear-dynamic-cast-target; + + # Fancy Moving + "Mod+Tab".action = focus-window-down-or-column-right; + "Mod+Shift+Tab".action = focus-window-up-or-column-left; + } + { + # Browser + "Mod+b".action = spawn-sh ( + if isNvidia + then "brave --profile-directory='Default' --enable-features=VaapiVideoDecoder,VaapiVideoEncoder --password-store=seahorse" + else "brave --profile-directory='Default' --password-store=seahorse" + ); + "Mod+b".hotkey-overlay.hidden = true; + "Mod+Shift+b".action = spawn "zen"; + "Mod+Shift+b".hotkey-overlay.hidden = true; + "Mod+h".action = spawn-sh ( + if isNvidia + then "brave --profile-directory='Profile 1' --enable-features=VaapiVideoDecoder,VaapiVideoEncoder --password-store=seahorse" + else "brave --profile-directory='Profile 1' --password-store=seahorse" + ); + "Mod+h".hotkey-overlay.hidden = true; + + # Cliphist via fuzzel + "Mod+p".action = spawn "cliphist-fuzzel-img"; + "Mod+p".hotkey-overlay.hidden = true; + # Single item clearing + "Mod+Shift+p".action = spawn-sh "cliphist list | fuzzel --dmenu | cliphist delete"; + "Mod+Shift+p".hotkey-overlay.hidden = true; + + # File Manager [n]avigate + "Mod+n".action = spawn-sh "alacritty -e fish -ic lf"; + "Mod+n".hotkey-overlay.hidden = true; + + # Calcu[M]athlator + "Mod+m".action = spawn "${fuzzelCalc}/bin/niri-qalc"; + "Mod+m".hotkey-overlay.title = "Calcu[M]athalor via qalculate"; + + # Logout and Power Menu + "Mod+Pause".action = spawn "wleave"; + + # Network ([W]ifi) Selection + "Mod+w".action = spawn "${pkgs.networkmanager_dmenu}/bin/networkmanager_dmenu"; + "Mod+w".hotkey-overlay.hidden = true; + + # Overview + "Mod+o".action = toggle-overview; + "Mod+o".repeat = false; + + # Reorder Workspaces (after moving them around) + "Mod+Shift+o".action = spawn "${workspaceReorderer}/bin/niri-reorder-workspaces"; + "Mod+Shift+o".hotkey-overlay.title = "Re[o]rder workspaces to maintain logical order"; + + # Rofi[e]moji + "Mod+e".action = spawn "rofimoji"; + "Mod+e".hotkey-overlay.hidden = true; + "Mod+Shift+e".action = spawn ["rofimoji" "--action" "clipboard"]; + "Mod+Shift+e".hotkey-overlay.hidden = true; + + # [S]ound Switcher + "Mod+s".action = spawn "sound-switcher"; + "Mod+s".hotkey-overlay.hidden = true; + } + { + # OBS Studio Controls + "Alt+F1".action = spawn "obs-main-scene"; + "Alt+F1".hotkey-overlay.title = "OBS: Switch to Main Scene"; + "Alt+F2".action = spawn "obs-screensharing"; + "Alt+F2".hotkey-overlay.title = "OBS: Switch to Screensharing"; + "Alt+F3".action = spawn "obs-catcam-toggle"; + "Alt+F3".hotkey-overlay.title = "OBS: Toggle Catcam"; + "Alt+F4".action = spawn "obs-recording-toggle"; + "Alt+F4".hotkey-overlay.title = "OBS: Start/Stop Recording"; + "Alt+F5".action = spawn "obs-recording-pause"; + "Alt+F5".hotkey-overlay.title = "OBS: Pause/Unpause Recording"; + "Alt+F6".action = spawn "obs-webcam-toggle"; + "Alt+F6".hotkey-overlay.title = "OBS: Toggle Webcam"; + } + ]; + }; + }; +} diff --git a/parts/features/desktop/theming.nix b/parts/features/desktop/theming.nix new file mode 100644 index 00000000..c572b2fb --- /dev/null +++ b/parts/features/desktop/theming.nix @@ -0,0 +1,102 @@ +{...}: { + flake.modules.nixos.theming = { + pkgs, + lib, + ... + }: { + stylix = { + enable = true; + base16Scheme = "${pkgs.base16-schemes}/share/themes/gruvbox-dark-medium.yaml"; + cursor = { + name = "Bibata-Modern-Ice"; + package = pkgs.bibata-cursors; + size = 24; + }; + fonts = { + emoji = { + package = pkgs.noto-fonts-color-emoji; + name = "Noto Color Emoji"; + }; + monospace = { + package = pkgs.fira-code; + name = "Fira Code"; + }; + serif = { + package = pkgs.dejavu_fonts; + name = "DejaVu Serif"; + }; + sansSerif = { + package = pkgs.dejavu_fonts; + name = "DejaVu Sans"; + }; + sizes = { + applications = 9; + desktop = 9; + terminal = 8; + popups = 9; + }; + }; + image = ./theming/_wallpapers/gruvbox-dark-rainbow.png; + polarity = "dark"; + }; + + specialisation.light.configuration = { + environment.etc."specialisation".text = "light"; + + stylix = { + base16Scheme = lib.mkForce "${pkgs.base16-schemes}/share/themes/catppuccin-latte.yaml"; + image = lib.mkForce ./theming/_wallpapers/gruvbox-light-rainbow.png; + polarity = lib.mkForce "light"; + }; + + home-manager.users.farlion = { + home.sessionVariables = { + AICHAT_LIGHT_THEME = 1; + }; + + services.dunst.iconTheme.name = lib.mkForce "Papirus-Light"; + + gtk.gtk3.extraConfig.gtk-application-prefer-dark-theme = lib.mkForce false; + + qt.enable = lib.mkForce false; + + programs.neovim = { + extraLuaConfig = '' + -- Override lualine theme for light mode + if require('lualine') then + local lualine_config = require('lualine').get_config() + lualine_config.options.theme = 'gruvbox-light' + require('lualine').setup(lualine_config) + end + ''; + }; + + stylix.targets = { + neovim.enable = lib.mkForce true; + }; + + programs.k9s.settings.k9s.ui.skin = lib.mkForce "gruvbox-light"; + }; + }; + }; + + flake.modules.homeManager.theming = {pkgs, ...}: { + home.packages = [ + pkgs.papirus-icon-theme + ]; + stylix = { + iconTheme = { + enable = true; + package = pkgs.papirus-icon-theme; + light = "Papirus-Light"; + dark = "Papirus-Dark"; + }; + targets = { + firefox = { + profileNames = ["main"]; + }; + neovim.enable = false; + }; + }; + }; +} diff --git a/system/stylix/gruvbox-dark-rainbow.png b/parts/features/desktop/theming/_wallpapers/gruvbox-dark-rainbow.png similarity index 100% rename from system/stylix/gruvbox-dark-rainbow.png rename to parts/features/desktop/theming/_wallpapers/gruvbox-dark-rainbow.png diff --git a/system/stylix/gruvbox-light-rainbow.png b/parts/features/desktop/theming/_wallpapers/gruvbox-light-rainbow.png similarity index 100% rename from system/stylix/gruvbox-light-rainbow.png rename to parts/features/desktop/theming/_wallpapers/gruvbox-light-rainbow.png diff --git a/parts/features/desktop/udiskie.nix b/parts/features/desktop/udiskie.nix new file mode 100644 index 00000000..532a7d6a --- /dev/null +++ b/parts/features/desktop/udiskie.nix @@ -0,0 +1,29 @@ +{...}: { + flake.modules.homeManager.udiskie = { + lib, + pkgs, + ... + }: { + # Indicator icon for automounting USB drives + services.udiskie = { + enable = true; + automount = false; + }; + + # Ensure udiskie starts after the Niri session is up, to avoid tray race conditions + systemd.user.services.udiskie = { + Unit = { + After = ["niri.service" "graphical-session.target"]; + Wants = ["graphical-session.target"]; + # Relax environment conditions in case defaults are too strict for Niri + ConditionEnvironment = lib.mkForce []; + }; + Service = { + # Small delay to ensure Wayland environment and tray are ready + ExecStartPre = "${pkgs.coreutils}/bin/sleep 2"; + Restart = lib.mkForce "on-failure"; + RestartSec = "5"; + }; + }; + }; +} diff --git a/home/waybar/scripts/dunst-dnd-waybar.sh b/parts/features/desktop/waybar/_scripts/dunst-dnd-waybar.sh similarity index 100% rename from home/waybar/scripts/dunst-dnd-waybar.sh rename to parts/features/desktop/waybar/_scripts/dunst-dnd-waybar.sh diff --git a/parts/features/desktop/waybar/default.nix b/parts/features/desktop/waybar/default.nix new file mode 100644 index 00000000..d8f61047 --- /dev/null +++ b/parts/features/desktop/waybar/default.nix @@ -0,0 +1,579 @@ +{...}: { + flake.modules.homeManager.waybar = { + osConfig, + config, + lib, + pkgs, + ... + }: let + dunst-dnd-waybar = pkgs.writeShellApplication { + name = "dunst-dnd-waybar"; + runtimeInputs = [pkgs.dunst]; + text = builtins.readFile ./_scripts/dunst-dnd-waybar.sh; + }; + isNvidia = osConfig.dendrix.hasNvidia; + isNumenor = osConfig.dendrix.hostname == "numenor"; + isFlexbox = osConfig.dendrix.hostname == "flexbox"; + + leftSection = { + modules-left = [ + "niri/workspaces" + ]; + }; + + centerSection = { + modules-center = [ + "systemd-failed-units" + "group/cpu" + "memory" + "disk" + "group/gpu" + "group/network" + "group/screens" + "group/audio" + "bluetooth" + "group/power" + ]; + + systemd-failed-units = { + format = " {nr_failed} failed"; + format-ok = " 0"; + hide-on-ok = false; + on-click = "alacritty --command journalctl --pager-end --catalog --boot --priority 3..3 | lnav"; + on-click-right = "alacritty --command isd"; + }; + + "group/cpu" = { + modules = [ + "cpu" + "temperature#cpu" + "custom/cpu-profile-toggler" + ]; + orientation = "inherit"; + }; + + cpu = { + format = " {usage}%"; + states = { + info = 80; + }; + on-click = "alacritty --command btop"; + on-click-right = "alacritty --command btop"; + }; + + "temperature#cpu" = { + critical-threshold = 90; + format = " {temperatureC}°C"; + on-click = "alacritty --command btop"; + on-click-right = "alacritty --command btop"; + hwmon-path = + if isNumenor + then "/sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon2/temp1_input" + else "/sys/devices/platform/coretemp.0/hwmon/hwmon7/temp1_input"; + }; + + "custom/cpu-profile-toggler" = { + format = "{icon}"; + format-icons = { + performance = ""; + powersave = ""; + }; + exec = "cpu-profile-toggler"; + on-click = "auto-cpufreq-gtk"; + on-click-middle = "cpu-profile-toggler --toggle"; + on-click-right = "cpu-profile-toggler --reset"; + return-type = "json"; + interval = 5; + }; + + memory = { + format = " {percentage}%"; + states = { + warning = 60; + critical = 80; + }; + on-click = "alacritty --command btop"; + on-click-right = "alacritty --command btop"; + tooltip-format = "{used:0.1f}GiB mem used, {swapUsed:0.1f}GiB swap used"; + }; + + disk = { + format = " {percentage_used}%"; + on-click = "alacritty --command ncdu /"; + on-click-right = "alacritty --command btop"; + states = { + warning = 60; + critical = 80; + }; + }; + + "group/gpu" = { + modules = + [ + ( + if isNvidia + then "custom/nvidia" + else "custom/gpu-usage" + ) + ] + ++ lib.optional (!isNvidia) "temperature#gpu"; + orientation = "inherit"; + }; + + "custom/gpu-usage" = { + exec = "cat /sys/class/hwmon/hwmon1/device/gpu_busy_percent"; + format = " {}%"; + return-type = ""; + interval = 1; + on-click = "alacritty --command nvtop"; + on-click-right = "alacritty --command nvtop"; + }; + + "custom/nvidia" = { + exec = "nvidia-smi --query-gpu=utilization.gpu,temperature.gpu --format=csv,nounits,noheader | sed 's/\\([0-9]\\+\\), \\([0-9]\\+\\)/\\1%  \\2°C/g'"; + format = " {}"; + interval = 2; + on-click = "alacritty --command nvtop"; + on-click-right = "alacritty --command nvtop"; + }; + + "temperature#gpu" = { + critical-threshold = 90; + on-click = "alacritty --command nvtop"; + on-click-right = "alacritty --command nvtop"; + hwmon-path = + if isNumenor + then "/sys/devices/pci0000:00/0000:00:01.1/0000:01:00.0/0000:02:00.0/0000:03:00.0/hwmon/hwmon1/temp2_input" + else "TODO find me according to waybar docs similar to CPU temp hwmon path"; + }; + + "group/network" = { + modules = ["network" "network#tailscale" "custom/macgyver" "network#mullvad" "network#wireguard"]; + orientation = "inherit"; + }; + + "network" = { + interval = 3; + format = " "; + format-disconnected = " "; + format-ethernet = " "; + format-wifi = " "; + format-linked = " 🚧"; + format-alt = "{bandwidthDownBytes} {bandwidthUpBytes}"; + tooltip-format = "IF:{ifname} SSID:{essid} FREQ:{frequency} :{signalStrength} IP:{ipaddr} GW:{gwaddr} NM:{netmask} {bandwidthDownBytes} {bandwidthUpBytes}"; + tooltip-format-ethernet = "IF:{ifname} IP:{ipaddr} NM:{netmask} {bandwidthDownBytes} {bandwidthUpBytes}"; + tooltip-format-wifi = "IF:{ifname} SSID:{essid} FREQ:{frequency} :{signalStrength} IP:{ipaddr} GW:{gwaddr} NM:{netmask} {bandwidthDownBytes} {bandwidthUpBytes}"; + tooltip-format-linked = "IF:{ifname} IP:{ipaddr} Connected but no internet"; + on-click-right = "alacritty --command nmtui"; + }; + + "network#tailscale" = { + interface = "tailscale0"; + interval = 3; + format-linked = " "; + format = " "; + format-alt = " {bandwidthDownBytes} {bandwidthUpBytes}"; + tooltip-format = " Tailscale IP:{ipaddr} NM:{netmask} {bandwidthDownBytes} {bandwidthUpBytes}"; + tooltip-format-linked = " Tailscale down. Right click to connect."; + on-click-right = "tailscale up"; + on-click-middle = "tailscale down"; + }; + + "custom/macgyver" = { + exec = "macgyver-status"; + return-type = "json"; + interval = 3; + format = "{icon}"; + format-icons = { + up = ""; + down = ""; + error = "❌"; + }; + tooltip-format = " MacGyver is {text}"; + on-click-right = "sudo systemctl start macgyver"; + on-click-middle = "sudo systemctl stop macgyver"; + }; + + "network#mullvad" = { + interface = "wg0-mullvad"; + interval = 3; + format-disconnected = " "; + format-disabled = " "; + format-linked = " "; + format = " "; + format-alt = " {bandwidthDownBytes} {bandwidthUpBytes}"; + tooltip-format = " Mullvad IP:{ipaddr} NM:{netmask} {bandwidthDownBytes} {bandwidthUpBytes}"; + tooltip-format-linked = " Mullvad down. Right click to connect or double right click for GUI."; + tooltip-format-disabled = " Mullvad down. Rickt click to connect or double right click for GUI."; + on-click-right = "mullvad connect"; + on-double-click-right = "mullvad-gui"; + on-click-middle = "mullvad disconnect"; + }; + + "network#wireguard" = { + interface = "wg0"; + interval = 3; + format-disconnected = " "; + format-disabled = " "; + format-linked = " "; + format = " "; + format-alt = " {bandwidthDownBytes} {bandwidthUpBytes}"; + tooltip-format = " Wireguard IP:{ipaddr} GW:{gwaddr} NM:{netmask} {bandwidthDownBytes} {bandwidthUpBytes}"; + tooltip-format-linked = " Wireguard down."; + tooltip-format-disabled = " Wireguard down."; + }; + + "group/screens" = { + modules = + if isFlexbox + then ["backlight" "custom/wlsunset"] + else ["custom/ddc-backlight-left" "custom/ddc-backlight-middle" "custom/ddc-backlight-right" "custom/wlsunset"]; + orientation = "inherit"; + }; + + backlight = { + format = "{icon} {percent}%"; + format-icons = ["🌑" "🌘" "🌗" "🌖" "🌕"]; + }; + + # Direct ddcutil control - required for monitors that only expose VCP Feature 10 (Brightness) + # rather than the "Backlight Level White" feature that ddcci-driver-linux requires + # See https://gitlab.com/ddcci-driver-linux/ddcci-driver-linux + "custom/ddc-backlight-left" = { + format = "{icon} "; + tooltip-format = "Left {percentage}%"; + format-icons = ["🌑" "🌘" "🌗" "🌖" "🌕"]; + exec = "ddc-backlight 7"; # For i2c-7 + exec-on-event = false; + on-scroll-up = "flock /tmp/ddc_backlight.lock ddcutil setvcp 10 + 5 -b 7"; + on-scroll-down = "flock /tmp/ddc_backlight.lock ddcutil setvcp 10 - 5 -b 7"; + on-click = "kanshictl switch numenor-movie"; + on-click-middle = "wdisplays"; + on-click-right = "kanshictl switch numenor"; + return-type = "json"; + interval = 60; + }; + + "custom/ddc-backlight-middle" = { + format = "{icon} "; + tooltip-format = "Middle {percentage}%"; + format-icons = ["🌑" "🌘" "🌗" "🌖" "🌕"]; + exec = "ddc-backlight 9"; # For i2c-9 + exec-on-event = false; + on-scroll-up = "flock /tmp/ddc_backlight.lock ddcutil setvcp 10 + 5 -b 9"; + on-scroll-down = "flock /tmp/ddc_backlight.lock ddcutil setvcp 10 - 5 -b 9"; + on-click = "kanshictl switch numenor-movie"; + on-click-middle = "wdisplays"; + on-click-right = "kanshictl switch numenor"; + return-type = "json"; + interval = 60; + }; + + "custom/ddc-backlight-right" = { + format = "{icon}"; + tooltip-format = "Right {percentage}%"; + format-icons = ["🌑" "🌘" "🌗" "🌖" "🌕"]; + exec = "ddc-backlight 6"; # For i2c-6 + exec-on-event = false; + on-scroll-up = "flock /tmp/ddc_backlight.lock ddcutil setvcp 10 + 5 -b 6"; + on-scroll-down = "flock /tmp/ddc_backlight.lock ddcutil setvcp 10 - 5 -b 6"; + on-click = "kanshictl switch numenor-movie"; + on-click-middle = "wdisplays"; + on-click-right = "kanshictl switch numenor"; + return-type = "json"; + interval = 60; + }; + + "custom/wlsunset" = { + interval = 1; + exec = "if pgrep wlsunset >/dev/null 2>&1; then stdbuf -oL printf '{\"alt\": \"on\",\"class\": \"on\"}'; else stdbuf -oL printf '{\"alt\": \"off\",\"class\": \"off\"}'; fi"; + on-click = "wlsunset-waybar"; + return-type = "json"; + format = " {icon}"; + tooltip-format = "wlsunset: {alt}"; + signal = 1; # SIGRTMIN+1 or 35 for updating immediately from script + format-icons = { + on = ""; + off = ""; + }; + }; + + "group/audio" = { + modules = [ + "pulseaudio#in" + "pulseaudio#out" + "mpris" + ]; + orientation = "inherit"; + }; + + "pulseaudio#in" = { + format = "{format_source}"; + format-source = " {volume}%"; + format-source-muted = ""; + format-icons = { + "bluez_input.DC:69:E2:9A:6E:30" = ""; + "alsa_input.usb-Apple__Inc._USB-C_to_3.5mm_Headphone_Jack_Adapter_DWH84440324JKLTA7-00.mono-fallback" = ""; + default = ["" ""]; + }; + max-volume = 200; + scroll-step = 5; + on-scroll-up = "pactl set-source-volume @DEFAULT_SOURCE@ +5%"; + on-scroll-down = "pactl set-source-volume @DEFAULT_SOURCE@ -5%"; + on-click = "pavucontrol --tab=4"; + on-click-right = "alacritty --command pulsemixer"; + on-click-middle = "pactl set-source-mute @DEFAULT_SOURCE@ toggle"; + tooltip-format = "{format_source}"; + }; + + "pulseaudio#out" = { + format = "{icon} {volume}%"; + format-bluetooth = "{icon} {volume}%"; + format-muted = ""; + format-icons = { + "alsa_output.usb-Generic_USB_Audio-00.analog-stereo" = ""; + "alsa_output.usb-Lenovo_ThinkPad_Thunderbolt_3_Dock_USB_Audio_000000000000-00.analog-stereo" = ""; + "bluez_output.14_3F_A6_28_DC_51.1" = ""; + "alsa_output.pci-0000_03_00.1.hdmi-stereo-extra3" = "🍿"; + "bluez_output.DC_69_E2_9A_6E_30.1" = ""; + "bluez_sink.DC_69_E2_9A_6E_30.handsfree_head_unit" = ""; + "alsa_output.pci-0000_00_1f.3-platform-sof_sdw.HiFi___ucm0003.hw_sofsoundwire_2__sink" = ""; + "alsa_output.pci-0000_00_1f.3-platform-sof_sdw.HiFi___ucm0005.hw_sofsoundwire_2__sink" = ""; + "alsa_output.pci-0000_00_1f.3-platform-sof_sdw.HiFi___ucm0007.hw_sofsoundwire_2__sink" = ""; + "alsa_output.pci-0000_00_1f.3-platform-sof_sdw.HiFi__hw_sofsoundwire_2__sink" = ""; + "alsa_output.pci-0000_00_1f.3-platform-sof_sdw.HiFi__Speaker__sink" = ""; + "alsa_output.pci-0000_00_1f.3-platform-sof_sdw.HiFi__hw_sofsoundwire__sink" = ""; + "alsa_output.usb-Apple__Inc._USB-C_to_3.5mm_Headphone_Jack_Adapter_DWH84440324JKLTA7-00.analog-stereo" = ""; + "bluez_output.34_E3_FB_C5_01_E0.1" = ""; + "bluez_sink.34_E3_FB_C5_01_E0.handsfree_head_unit" = ""; + default = ["" ""]; + }; + max-volume = 200; + on-click = "pavucontrol --tab=3"; + on-click-right = "alacritty --command pulsemixer"; + on-click-middle = "pactl set-sink-mute @DEFAULT_SINK@ toggle"; + ignored-sinks = ["Easy Effects Sink"]; + }; + + mpris = { + format = "{player_icon}"; + format-paused = "{status_icon}"; + on-click-right = ''niri msg action focus-window --id $(niri msg --json windows | jq -r '.[] | select(.app_id == "YouTube Music Desktop App") | .id')''; + player-icons = { + default = "▶"; + mpv = ""; + chromium = ""; + }; + status-icons = { + paused = "⏸"; + }; + }; + + bluetooth = { + format = "{icon}"; + format-connected = "{icon} {num_connections}"; + format-connnected-battery = "{icon} {num_connections} {device_battery_percentage}%"; + format-icons = { + connected = ""; + on = ""; + off = ""; + disabled = ""; + disconnected = ""; + default = ""; + }; + on-click = "bluetoothctl power on"; + on-click-right = "bluetoothctl power off"; + on-click-middle = "blueman-manager"; + tooltip-format = "{status} {num_connections}"; + }; + + "group/power" = { + modules = [ + "battery" + ]; + orientation = "inherit"; + }; + + battery = { + events = { + on-discharging-warning = "notify-send -u normal 'Low Battery'"; + on-discharging-critical = "notify-send -u critical 'Very Low Battery'"; + on-charging-100 = "notify-send -u normal 'Battery is full'"; + }; + states = { + warning = 30; + critical = 15; + }; + format = " {icon} {capacity}%"; + format-icons = ["" "" "" "" ""]; + tooltip = true; + backend = "upower"; + }; + }; + + rightSection = { + modules-right = [ + "privacy" + "custom/dunst-dnd" + "custom/caffeinate" + "niri/language" + "clock" + "tray" + ]; + + "custom/dunst-dnd" = { + exec = "${dunst-dnd-waybar}/bin/dunst-dnd-waybar"; + return-type = "json"; + interval = 1; + format = "{icon}{text}"; + tooltip-format = "{text}"; + format-icons = { + running = ""; + dnd = ""; + }; + on-click = "dunstctl set-paused toggle"; + on-click-right = "dunstctl set-paused false"; + }; + + "custom/caffeinate" = { + exec = "if systemctl --user is-active swayidle >/dev/null 2>&1; then echo '{\"alt\": \"deactivated\", \"class\": \"deactivated\"}'; else echo '{\"alt\": \"activated\", \"class\": \"activated\"}'; fi"; + return-type = "json"; + interval = 1; + format = " {icon}"; + format-icons = { + activated = ""; + deactivated = ""; + }; + on-click = "systemctl --user stop swayidle"; + on-click-right = "systemctl --user start swayidle"; + on-click-middle = "systemctl --user start swayidle"; + }; + + "niri/language" = { + format = "{short}"; + on-click = "niri msg action switch-layout next"; + on-click-right = "niri msg action switch-layout prev"; + on-click-middle = "niri msg action switch-layout 0"; + }; + + clock = { + format = "{:%H:%M}"; + format-alt = "{:%A, %B %d, %Y (%R)}  "; + tooltip-format = "{calendar}"; + calendar = { + mode = "year"; + mode-mon-col = 3; + weeks-pos = "right"; + on-scroll = 1; + format = { + months = "{}"; + days = "{}"; + weeks = "W{}"; + weekdays = "{}"; + today = "{}"; + }; + }; + actions = { + on-click-right = "mode"; + on-scroll-up = "shift_up"; + on-scroll-down = "shift_down"; + }; + on-click-middle = "brave calendar.google.com"; + }; + + tray = { + show-passive-items = true; + spacing = 3; + }; + }; + in { + programs.waybar = { + enable = true; + settings = { + main = + { + layer = "top"; + position = "bottom"; + expand-center = true; + output = [ + "DP-1" # Numenor main screen + "eDP-1" # Flexbox + ]; + } + // leftSection // centerSection // rightSection; + + aux = + { + layer = "top"; + position = "bottom"; + output = [ + "HDMI-A-1" + "HDMI-A-2" + ]; + } + // leftSection // rightSection; + }; + + style = '' + .info { + color: @base0D; + } + .critical { + color: @base08; + } + .warning { + color: @base0A; + } + + #systemd-failed-units.degraded { + color: @base08; + } + + #network.ethernet, + #network.wifi { + color: @base0B; + } + + #custom-dunst-dnd.dnd { + color: @base0A; + } + + #custom-macgyver.up { + color: @base0B; + } + + #custom-wlsunset.off { + color: @base0A; + } + + #custom-caffeinate.activated { + color: @base0A; + } + + #language:not(.us) { + color: @base0A; + } + + /* Can remove once https://github.com/nix-community/stylix/pull/1919 is merged */ + tooltip label { + color: @base05; + } + ''; + + systemd = { + enable = true; + enableDebug = false; + enableInspect = false; + }; + }; + + systemd.user.services.waybar = { + # Give on-click commands access to binaries they need + Service.Environment = lib.mkForce "PATH=/run/wrappers/bin:${config.home.profileDirectory}/bin:/run/current-system/sw/bin"; + # Fix for niri startup + Install.WantedBy = lib.mkForce ["niri.service"]; + Unit.Requires = ["niri.service"]; + Unit.After = ["niri.service"]; + }; + }; +} diff --git a/home/wlsunset/scripts/wlsunset-waybar.sh b/parts/features/desktop/wlsunset/_scripts/wlsunset-waybar.sh similarity index 100% rename from home/wlsunset/scripts/wlsunset-waybar.sh rename to parts/features/desktop/wlsunset/_scripts/wlsunset-waybar.sh diff --git a/parts/features/desktop/wlsunset/default.nix b/parts/features/desktop/wlsunset/default.nix new file mode 100644 index 00000000..61a43702 --- /dev/null +++ b/parts/features/desktop/wlsunset/default.nix @@ -0,0 +1,16 @@ +# Day/night gamma adjustments for Wayland +{...}: { + flake.modules.homeManager.wlsunset = {pkgs, ...}: let + # https://github.com/CyrilSLi/linux-scripts/blob/main/waybar/wlsunset.sh + wlsunset-waybar = pkgs.writeShellApplication { + name = "wlsunset-waybar"; + runtimeInputs = with pkgs; [wlsunset procps killall]; + text = builtins.readFile ./_scripts/wlsunset-waybar.sh; + }; + in { + home.packages = [ + pkgs.wlsunset + wlsunset-waybar + ]; + }; +} diff --git a/parts/features/dev/aider.nix b/parts/features/dev/aider.nix new file mode 100644 index 00000000..662bcda1 --- /dev/null +++ b/parts/features/dev/aider.nix @@ -0,0 +1,7 @@ +{...}: { + flake.modules.homeManager.aider = {...}: { + programs.aider-chat = { + enable = true; + }; + }; +} diff --git a/home/claude-code/CLAUDE.md b/parts/features/dev/claude-code/CLAUDE.md similarity index 100% rename from home/claude-code/CLAUDE.md rename to parts/features/dev/claude-code/CLAUDE.md diff --git a/parts/features/dev/claude-code/default.nix b/parts/features/dev/claude-code/default.nix new file mode 100644 index 00000000..8b996d6f --- /dev/null +++ b/parts/features/dev/claude-code/default.nix @@ -0,0 +1,54 @@ +{...}: { + flake.modules.homeManager.claude-code = { + lib, + pkgs, + osConfig, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".claude" # Claude Code global settings, agents, and credentials + ".cache/claude-cli-nodejs" # Claude Code cache + ]; + files = [ + ".claude.json" # Claude Code runtime state (credentials, project settings, etc.) + ]; + }; + + programs.claude-code = { + enable = true; + package = pkgs.unstable.claude-code; + + memory.source = ./CLAUDE.md; + + settings = { + permissions = { + allow = [ + "Bash(jj log:*)" + "Bash(jj diff:*)" + "Bash(jj status)" + "Grep" + "WebFetch(domain:github.com)" + "WebSearch" + ]; + deny = [ + "Read(./.env)" + "Read(./.env.*)" + "Read(./secrets/secrets.json)" + "Read(./config/credentials.json)" + ]; + }; + alwaysThinkingEnabled = true; + }; + }; + + # See https://dylancastillo.co/til/fix-claude-code-shift-enter-alacritty.html + programs.alacritty.settings.keyboard.bindings = [ + { + key = "Return"; + mods = "Shift"; + chars = "\n"; + } + ]; + }; +} diff --git a/parts/features/dev/codex.nix b/parts/features/dev/codex.nix new file mode 100644 index 00000000..d8badf00 --- /dev/null +++ b/parts/features/dev/codex.nix @@ -0,0 +1,14 @@ +{...}: { + flake.modules.homeManager.codex = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".codex"]; + }; + + home.packages = [pkgs.unstable.codex]; + }; +} diff --git a/parts/features/dev/devenv.nix b/parts/features/dev/devenv.nix new file mode 100644 index 00000000..f97d878e --- /dev/null +++ b/parts/features/dev/devenv.nix @@ -0,0 +1,19 @@ +# devenv.sh +{...}: { + flake.modules.homeManager.devenv = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".local/share/devenv" + ]; + }; + + home.packages = [ + pkgs.devenv + ]; + }; +} diff --git a/parts/features/dev/direnv.nix b/parts/features/dev/direnv.nix new file mode 100644 index 00000000..e2a41179 --- /dev/null +++ b/parts/features/dev/direnv.nix @@ -0,0 +1,13 @@ +{...}: { + flake.modules.homeManager.direnv = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".local/share/direnv"]; + }; + + programs.direnv = { + enable = true; + nix-direnv.enable = true; + config.strict_env = true; # Forces all .envrc scripts through set -euo pipefail + }; + }; +} diff --git a/parts/features/dev/git-worktree-switcher.nix b/parts/features/dev/git-worktree-switcher.nix new file mode 100644 index 00000000..cb019a9e --- /dev/null +++ b/parts/features/dev/git-worktree-switcher.nix @@ -0,0 +1,6 @@ +# Provides wt +{...}: { + flake.modules.homeManager.git-worktree-switcher = {...}: { + programs.git-worktree-switcher.enable = true; + }; +} diff --git a/parts/features/dev/git/default.nix b/parts/features/dev/git/default.nix new file mode 100644 index 00000000..dec16cde --- /dev/null +++ b/parts/features/dev/git/default.nix @@ -0,0 +1,83 @@ +{...}: { + flake.modules.homeManager.git = {pkgs, lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".config/glab-cli"]; + }; + + home.packages = with pkgs; [ + delta # Syntax highlighter for git + github-cli + glab + ]; + + home.file.".config/gh/config.yml".source = ./gh.config.yml; + + programs.difftastic = { + enable = true; + git.enable = true; + }; + + programs.git = { + enable = true; + + includes = [ + { + path = "~/code/dlh/.gitconfig"; + condition = "gitdir:~/code/dlh/"; + } + { + path = "~/code/dlh/plansee/.gitconfig"; + condition = "gitdir:~/code/dlh/plansee/"; + } + ]; + + signing = { + signByDefault = true; + key = "24575DB93F6CEC16"; + }; + + settings = { + alias = { + c = "commit"; + + # Difftastic + dlog = "-c diff.external=difft log --ext-diff"; + dshow = "-c diff.external=difft show --ext-diff"; + ddiff = "-c diff.external=difft diff"; + # `git log` with patches shown with difftastic. + dl = "-c diff.external=difft log -p --ext-diff"; + # Show the most recent commit with difftastic. + ds = "-c diff.external=difft show --ext-diff"; + # `git diff` with difftastic. + dft = "-c diff.external=difft diff"; + + p = "push"; + rim = "rebase -i main"; + rimm = "rebase -i master"; + }; + + core.pager = "delta"; + diff.colorMoved = "default"; + init.defaultBranch = "main"; + interactive.diffFilter = "delta --color-only"; + merge.conflictstyle = "diff3"; + pull = { + ff = "only"; + rebase = true; + }; + rebase = { + autoStash = true; + autoSquash = true; + }; + # https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/javascript.section.md#git-protocol-error + url."https://github.com".insteadOf = "git://github.com"; + user.email = "4farlion@gmail.com"; + user.name = "workflow"; + }; + + ignores = [".idea" "nohup.out" "mzdata" ".vimspector.json"]; + }; + + programs.mergiraf.enable = true; + }; +} diff --git a/home/git/github-cli/gh.config.yml b/parts/features/dev/git/gh.config.yml similarity index 100% rename from home/git/github-cli/gh.config.yml rename to parts/features/dev/git/gh.config.yml diff --git a/parts/features/dev/jujutsu.nix b/parts/features/dev/jujutsu.nix new file mode 100644 index 00000000..b96ab153 --- /dev/null +++ b/parts/features/dev/jujutsu.nix @@ -0,0 +1,64 @@ +{...}: { + flake.modules.homeManager.jujutsu = {pkgs, ...}: { + programs.difftastic.enable = true; + programs.jjui = { + enable = true; + package = pkgs.unstable.jjui; + }; + programs.jujutsu = { + enable = true; + package = pkgs.unstable.jujutsu; + settings = { + remotes.origin.auto-track-bookmarks = "main"; + ui.diff-formatter = ["difft" "--color=always" "$left" "$right"]; + user = { + email = "4farlion@gmail.com"; + name = "workflow"; + }; + signing = { + backend = "gpg"; + key = "24575DB93F6CEC16"; + behavior = "own"; # sign commits you authored on modify + }; + aliases = { + bt = ["bookmark" "track"]; + c = ["commit"]; + init = ["git" "init" "--colocate"]; + push = [ + "util" + "exec" + "--" + "bash" + "-c" + '' + set -e + + # Check if current commit has both description and changes + has_description=$(jj log -r @ --no-graph --color never -T 'description' | grep -q . && echo "yes" || echo "no") + # Use 'empty' template keyword to check if commit has changes + has_changes=$(jj log -r @ --no-graph --color never -T 'empty' | grep -q "false" && echo "yes" || echo "no") + + if [ "$has_description" = "yes" ] && [ "$has_changes" = "yes" ]; then + echo "Current commit has description and changes, creating new commit..." + jj new + fi + + # Get the bookmark from the parent commit directly + bookmark=$(jj log -r 'ancestors(@) & bookmarks()' -n 1 --no-graph --color never -T 'bookmarks' | sed 's/\*$//' | tr -d ' ') + + if [ -z "$bookmark" ]; then + echo "No bookmark found on parent commit" + exit 1 + fi + + echo "Moving bookmark '$bookmark' to parent commit and pushing..." + jj bookmark set "$bookmark" -r @- + jj git fetch + jj git push --bookmark "$bookmark" --allow-new + '' + ]; + }; + }; + }; + }; +} diff --git a/home/k9s/gruvbox-dark.yaml b/parts/features/dev/k9s/_skins/gruvbox-dark.yaml similarity index 100% rename from home/k9s/gruvbox-dark.yaml rename to parts/features/dev/k9s/_skins/gruvbox-dark.yaml diff --git a/home/k9s/gruvbox-light.yaml b/parts/features/dev/k9s/_skins/gruvbox-light.yaml similarity index 100% rename from home/k9s/gruvbox-light.yaml rename to parts/features/dev/k9s/_skins/gruvbox-light.yaml diff --git a/parts/features/dev/k9s/default.nix b/parts/features/dev/k9s/default.nix new file mode 100644 index 00000000..a31b34b3 --- /dev/null +++ b/parts/features/dev/k9s/default.nix @@ -0,0 +1,26 @@ +{...}: { + flake.modules.homeManager.k9s = { + pkgs, + lib, + ... + }: { + programs.k9s = { + enable = true; + package = pkgs.unstable.k9s; + + skins = { + gruvbox-dark = ./_skins/gruvbox-dark.yaml; + gruvbox-light = ./_skins/gruvbox-light.yaml; + }; + + settings = { + k9s = { + ui = { + # Default to gruvbox-dark, override in light specialisation + skin = lib.mkDefault "gruvbox-dark"; + }; + }; + }; + }; + }; +} diff --git a/home/neovim/avante/avante.lua b/parts/features/dev/neovim/_avante/avante.lua similarity index 100% rename from home/neovim/avante/avante.lua rename to parts/features/dev/neovim/_avante/avante.lua diff --git a/home/neovim/avante/default.nix b/parts/features/dev/neovim/_avante/default.nix similarity index 84% rename from home/neovim/avante/default.nix rename to parts/features/dev/neovim/_avante/default.nix index 6aea934d..013cef41 100644 --- a/home/neovim/avante/default.nix +++ b/parts/features/dev/neovim/_avante/default.nix @@ -1,11 +1,11 @@ # Cursor style AI IDE { - isImpermanent, lib, pkgs, + osConfig, ... }: { - home.persistence."/persist" = lib.mkIf isImpermanent { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { directories = [ ".config/avante.nvim" # Some Avante state like last used model ]; diff --git a/home/neovim/carbon/default.nix b/parts/features/dev/neovim/_carbon/default.nix similarity index 100% rename from home/neovim/carbon/default.nix rename to parts/features/dev/neovim/_carbon/default.nix diff --git a/home/neovim/cmp/cmp.lua b/parts/features/dev/neovim/_cmp/cmp.lua similarity index 100% rename from home/neovim/cmp/cmp.lua rename to parts/features/dev/neovim/_cmp/cmp.lua diff --git a/home/neovim/cmp/default.nix b/parts/features/dev/neovim/_cmp/default.nix similarity index 100% rename from home/neovim/cmp/default.nix rename to parts/features/dev/neovim/_cmp/default.nix diff --git a/home/neovim/comment-nvim/default.nix b/parts/features/dev/neovim/_comment-nvim/default.nix similarity index 100% rename from home/neovim/comment-nvim/default.nix rename to parts/features/dev/neovim/_comment-nvim/default.nix diff --git a/home/neovim/conform/conform.lua b/parts/features/dev/neovim/_conform/conform.lua similarity index 100% rename from home/neovim/conform/conform.lua rename to parts/features/dev/neovim/_conform/conform.lua diff --git a/home/neovim/conform/default.nix b/parts/features/dev/neovim/_conform/default.nix similarity index 100% rename from home/neovim/conform/default.nix rename to parts/features/dev/neovim/_conform/default.nix diff --git a/home/neovim/dadbod/default.nix b/parts/features/dev/neovim/_dadbod/default.nix similarity index 88% rename from home/neovim/dadbod/default.nix rename to parts/features/dev/neovim/_dadbod/default.nix index 30f35172..592fa33a 100644 --- a/home/neovim/dadbod/default.nix +++ b/parts/features/dev/neovim/_dadbod/default.nix @@ -1,10 +1,10 @@ { - isImpermanent, lib, pkgs, + osConfig, ... }: { - home.persistence."/persist" = lib.mkIf isImpermanent { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { directories = [ ".local/share/db_ui" ]; diff --git a/home/neovim/dap/dap-ui.lua b/parts/features/dev/neovim/_dap/dap-ui.lua similarity index 100% rename from home/neovim/dap/dap-ui.lua rename to parts/features/dev/neovim/_dap/dap-ui.lua diff --git a/home/neovim/dap/dap.lua b/parts/features/dev/neovim/_dap/dap.lua similarity index 100% rename from home/neovim/dap/dap.lua rename to parts/features/dev/neovim/_dap/dap.lua diff --git a/home/neovim/dap/default.nix b/parts/features/dev/neovim/_dap/default.nix similarity index 100% rename from home/neovim/dap/default.nix rename to parts/features/dev/neovim/_dap/default.nix diff --git a/home/neovim/diffview-nvim/default.nix b/parts/features/dev/neovim/_diffview-nvim/default.nix similarity index 100% rename from home/neovim/diffview-nvim/default.nix rename to parts/features/dev/neovim/_diffview-nvim/default.nix diff --git a/home/neovim/diffview-nvim/diffview-nvim.lua b/parts/features/dev/neovim/_diffview-nvim/diffview-nvim.lua similarity index 100% rename from home/neovim/diffview-nvim/diffview-nvim.lua rename to parts/features/dev/neovim/_diffview-nvim/diffview-nvim.lua diff --git a/home/neovim/fidget/default.nix b/parts/features/dev/neovim/_fidget/default.nix similarity index 100% rename from home/neovim/fidget/default.nix rename to parts/features/dev/neovim/_fidget/default.nix diff --git a/home/neovim/folds/default.nix b/parts/features/dev/neovim/_folds/default.nix similarity index 100% rename from home/neovim/folds/default.nix rename to parts/features/dev/neovim/_folds/default.nix diff --git a/home/neovim/folds/ufo.lua b/parts/features/dev/neovim/_folds/ufo.lua similarity index 100% rename from home/neovim/folds/ufo.lua rename to parts/features/dev/neovim/_folds/ufo.lua diff --git a/home/neovim/fugitive/default.nix b/parts/features/dev/neovim/_fugitive/default.nix similarity index 100% rename from home/neovim/fugitive/default.nix rename to parts/features/dev/neovim/_fugitive/default.nix diff --git a/home/neovim/fugitive/fugitive.lua b/parts/features/dev/neovim/_fugitive/fugitive.lua similarity index 100% rename from home/neovim/fugitive/fugitive.lua rename to parts/features/dev/neovim/_fugitive/fugitive.lua diff --git a/home/neovim/git-conflict-nvim/default.nix b/parts/features/dev/neovim/_git-conflict-nvim/default.nix similarity index 100% rename from home/neovim/git-conflict-nvim/default.nix rename to parts/features/dev/neovim/_git-conflict-nvim/default.nix diff --git a/home/neovim/gitsigns/default.nix b/parts/features/dev/neovim/_gitsigns/default.nix similarity index 100% rename from home/neovim/gitsigns/default.nix rename to parts/features/dev/neovim/_gitsigns/default.nix diff --git a/home/neovim/gitsigns/gitsigns.lua b/parts/features/dev/neovim/_gitsigns/gitsigns.lua similarity index 100% rename from home/neovim/gitsigns/gitsigns.lua rename to parts/features/dev/neovim/_gitsigns/gitsigns.lua diff --git a/home/neovim/gruvbox/default.nix b/parts/features/dev/neovim/_gruvbox/default.nix similarity index 100% rename from home/neovim/gruvbox/default.nix rename to parts/features/dev/neovim/_gruvbox/default.nix diff --git a/home/neovim/jdtls/default.nix b/parts/features/dev/neovim/_jdtls/default.nix similarity index 100% rename from home/neovim/jdtls/default.nix rename to parts/features/dev/neovim/_jdtls/default.nix diff --git a/home/neovim/jdtls/jdtls.lua b/parts/features/dev/neovim/_jdtls/jdtls.lua similarity index 100% rename from home/neovim/jdtls/jdtls.lua rename to parts/features/dev/neovim/_jdtls/jdtls.lua diff --git a/home/neovim/jj/default.nix b/parts/features/dev/neovim/_jj/default.nix similarity index 100% rename from home/neovim/jj/default.nix rename to parts/features/dev/neovim/_jj/default.nix diff --git a/home/neovim/jj/jj.lua b/parts/features/dev/neovim/_jj/jj.lua similarity index 100% rename from home/neovim/jj/jj.lua rename to parts/features/dev/neovim/_jj/jj.lua diff --git a/home/neovim/lspsaga/default.nix b/parts/features/dev/neovim/_lspsaga/default.nix similarity index 100% rename from home/neovim/lspsaga/default.nix rename to parts/features/dev/neovim/_lspsaga/default.nix diff --git a/home/neovim/lualine/default.nix b/parts/features/dev/neovim/_lualine/default.nix similarity index 100% rename from home/neovim/lualine/default.nix rename to parts/features/dev/neovim/_lualine/default.nix diff --git a/home/neovim/mason-lsp/default.nix b/parts/features/dev/neovim/_mason-lsp/default.nix similarity index 96% rename from home/neovim/mason-lsp/default.nix rename to parts/features/dev/neovim/_mason-lsp/default.nix index e5272123..3fc085bf 100644 --- a/home/neovim/mason-lsp/default.nix +++ b/parts/features/dev/neovim/_mason-lsp/default.nix @@ -1,10 +1,10 @@ { - isImpermanent, lib, pkgs, + osConfig, ... }: { - home.persistence."/persist" = lib.mkIf isImpermanent { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { directories = [ ".local/state/logrotate" # Logrotate state for nvim LSP logs ]; diff --git a/home/neovim/mason-lsp/mason.lua b/parts/features/dev/neovim/_mason-lsp/mason.lua similarity index 100% rename from home/neovim/mason-lsp/mason.lua rename to parts/features/dev/neovim/_mason-lsp/mason.lua diff --git a/home/neovim/mason-lsp/shared_lsp_config.lua b/parts/features/dev/neovim/_mason-lsp/shared_lsp_config.lua similarity index 100% rename from home/neovim/mason-lsp/shared_lsp_config.lua rename to parts/features/dev/neovim/_mason-lsp/shared_lsp_config.lua diff --git a/home/neovim/mini-icons/default.nix b/parts/features/dev/neovim/_mini-icons/default.nix similarity index 100% rename from home/neovim/mini-icons/default.nix rename to parts/features/dev/neovim/_mini-icons/default.nix diff --git a/home/neovim/mini-operators/default.nix b/parts/features/dev/neovim/_mini-operators/default.nix similarity index 100% rename from home/neovim/mini-operators/default.nix rename to parts/features/dev/neovim/_mini-operators/default.nix diff --git a/home/neovim/neotest/default.nix b/parts/features/dev/neovim/_neotest/default.nix similarity index 100% rename from home/neovim/neotest/default.nix rename to parts/features/dev/neovim/_neotest/default.nix diff --git a/home/neovim/neotest/neotest.lua b/parts/features/dev/neovim/_neotest/neotest.lua similarity index 100% rename from home/neovim/neotest/neotest.lua rename to parts/features/dev/neovim/_neotest/neotest.lua diff --git a/home/neovim/noice/default.nix b/parts/features/dev/neovim/_noice/default.nix similarity index 100% rename from home/neovim/noice/default.nix rename to parts/features/dev/neovim/_noice/default.nix diff --git a/home/neovim/noice/noice.lua b/parts/features/dev/neovim/_noice/noice.lua similarity index 100% rename from home/neovim/noice/noice.lua rename to parts/features/dev/neovim/_noice/noice.lua diff --git a/home/neovim/notify/default.nix b/parts/features/dev/neovim/_notify/default.nix similarity index 100% rename from home/neovim/notify/default.nix rename to parts/features/dev/neovim/_notify/default.nix diff --git a/home/neovim/nui/default.nix b/parts/features/dev/neovim/_nui/default.nix similarity index 100% rename from home/neovim/nui/default.nix rename to parts/features/dev/neovim/_nui/default.nix diff --git a/home/neovim/nvim-tree-lua/default.nix b/parts/features/dev/neovim/_nvim-tree-lua/default.nix similarity index 100% rename from home/neovim/nvim-tree-lua/default.nix rename to parts/features/dev/neovim/_nvim-tree-lua/default.nix diff --git a/home/neovim/nvim-tree-lua/nvim-tree.lua b/parts/features/dev/neovim/_nvim-tree-lua/nvim-tree.lua similarity index 100% rename from home/neovim/nvim-tree-lua/nvim-tree.lua rename to parts/features/dev/neovim/_nvim-tree-lua/nvim-tree.lua diff --git a/home/neovim/obsidian-nvim/default.nix b/parts/features/dev/neovim/_obsidian-nvim/default.nix similarity index 100% rename from home/neovim/obsidian-nvim/default.nix rename to parts/features/dev/neovim/_obsidian-nvim/default.nix diff --git a/home/neovim/oil/default.nix b/parts/features/dev/neovim/_oil/default.nix similarity index 100% rename from home/neovim/oil/default.nix rename to parts/features/dev/neovim/_oil/default.nix diff --git a/home/neovim/otter/default.nix b/parts/features/dev/neovim/_otter/default.nix similarity index 100% rename from home/neovim/otter/default.nix rename to parts/features/dev/neovim/_otter/default.nix diff --git a/home/neovim/overseer/default.nix b/parts/features/dev/neovim/_overseer/default.nix similarity index 100% rename from home/neovim/overseer/default.nix rename to parts/features/dev/neovim/_overseer/default.nix diff --git a/home/neovim/overseer/overseer.lua b/parts/features/dev/neovim/_overseer/overseer.lua similarity index 100% rename from home/neovim/overseer/overseer.lua rename to parts/features/dev/neovim/_overseer/overseer.lua diff --git a/home/neovim/overseer/overseer_lib.lua b/parts/features/dev/neovim/_overseer/overseer_lib.lua similarity index 100% rename from home/neovim/overseer/overseer_lib.lua rename to parts/features/dev/neovim/_overseer/overseer_lib.lua diff --git a/home/neovim/overseer/templates/gmailctl_apply.lua b/parts/features/dev/neovim/_overseer/templates/gmailctl_apply.lua similarity index 100% rename from home/neovim/overseer/templates/gmailctl_apply.lua rename to parts/features/dev/neovim/_overseer/templates/gmailctl_apply.lua diff --git a/home/neovim/overseer/templates/java_gradle/init.lua b/parts/features/dev/neovim/_overseer/templates/java_gradle/init.lua similarity index 100% rename from home/neovim/overseer/templates/java_gradle/init.lua rename to parts/features/dev/neovim/_overseer/templates/java_gradle/init.lua diff --git a/home/neovim/overseer/templates/java_maven/init.lua b/parts/features/dev/neovim/_overseer/templates/java_maven/init.lua similarity index 100% rename from home/neovim/overseer/templates/java_maven/init.lua rename to parts/features/dev/neovim/_overseer/templates/java_maven/init.lua diff --git a/home/neovim/overseer/templates/nixos_rebuild_boot.lua b/parts/features/dev/neovim/_overseer/templates/nixos_rebuild_boot.lua similarity index 100% rename from home/neovim/overseer/templates/nixos_rebuild_boot.lua rename to parts/features/dev/neovim/_overseer/templates/nixos_rebuild_boot.lua diff --git a/home/neovim/overseer/templates/nixos_rebuild_switch.lua b/parts/features/dev/neovim/_overseer/templates/nixos_rebuild_switch.lua similarity index 100% rename from home/neovim/overseer/templates/nixos_rebuild_switch.lua rename to parts/features/dev/neovim/_overseer/templates/nixos_rebuild_switch.lua diff --git a/home/neovim/overseer/templates/nixos_update_secrets.lua b/parts/features/dev/neovim/_overseer/templates/nixos_update_secrets.lua similarity index 100% rename from home/neovim/overseer/templates/nixos_update_secrets.lua rename to parts/features/dev/neovim/_overseer/templates/nixos_update_secrets.lua diff --git a/home/neovim/overseer/templates/skaffold_dev.lua b/parts/features/dev/neovim/_overseer/templates/skaffold_dev.lua similarity index 100% rename from home/neovim/overseer/templates/skaffold_dev.lua rename to parts/features/dev/neovim/_overseer/templates/skaffold_dev.lua diff --git a/home/neovim/plenary/default.nix b/parts/features/dev/neovim/_plenary/default.nix similarity index 100% rename from home/neovim/plenary/default.nix rename to parts/features/dev/neovim/_plenary/default.nix diff --git a/home/neovim/rainbow-csv/default.nix b/parts/features/dev/neovim/_rainbow-csv/default.nix similarity index 100% rename from home/neovim/rainbow-csv/default.nix rename to parts/features/dev/neovim/_rainbow-csv/default.nix diff --git a/home/neovim/render-markdown/default.nix b/parts/features/dev/neovim/_render-markdown/default.nix similarity index 100% rename from home/neovim/render-markdown/default.nix rename to parts/features/dev/neovim/_render-markdown/default.nix diff --git a/home/neovim/telescope/default.nix b/parts/features/dev/neovim/_telescope/default.nix similarity index 100% rename from home/neovim/telescope/default.nix rename to parts/features/dev/neovim/_telescope/default.nix diff --git a/home/neovim/toggleterm/default.nix b/parts/features/dev/neovim/_toggleterm/default.nix similarity index 100% rename from home/neovim/toggleterm/default.nix rename to parts/features/dev/neovim/_toggleterm/default.nix diff --git a/home/neovim/treesitter/default.nix b/parts/features/dev/neovim/_treesitter/default.nix similarity index 100% rename from home/neovim/treesitter/default.nix rename to parts/features/dev/neovim/_treesitter/default.nix diff --git a/home/neovim/treesitter/queries/nix/injections.scm b/parts/features/dev/neovim/_treesitter/queries/nix/injections.scm similarity index 100% rename from home/neovim/treesitter/queries/nix/injections.scm rename to parts/features/dev/neovim/_treesitter/queries/nix/injections.scm diff --git a/home/neovim/treesitter/treesitter.lua b/parts/features/dev/neovim/_treesitter/treesitter.lua similarity index 100% rename from home/neovim/treesitter/treesitter.lua rename to parts/features/dev/neovim/_treesitter/treesitter.lua diff --git a/home/neovim/trouble/default.nix b/parts/features/dev/neovim/_trouble/default.nix similarity index 100% rename from home/neovim/trouble/default.nix rename to parts/features/dev/neovim/_trouble/default.nix diff --git a/home/neovim/trouble/trouble.lua b/parts/features/dev/neovim/_trouble/trouble.lua similarity index 100% rename from home/neovim/trouble/trouble.lua rename to parts/features/dev/neovim/_trouble/trouble.lua diff --git a/home/neovim/undotree/default.nix b/parts/features/dev/neovim/_undotree/default.nix similarity index 100% rename from home/neovim/undotree/default.nix rename to parts/features/dev/neovim/_undotree/default.nix diff --git a/home/neovim/vim-be-good/default.nix b/parts/features/dev/neovim/_vim-be-good/default.nix similarity index 100% rename from home/neovim/vim-be-good/default.nix rename to parts/features/dev/neovim/_vim-be-good/default.nix diff --git a/home/neovim/vim-terraform/default.nix b/parts/features/dev/neovim/_vim-terraform/default.nix similarity index 100% rename from home/neovim/vim-terraform/default.nix rename to parts/features/dev/neovim/_vim-terraform/default.nix diff --git a/home/neovim/vim-visual-multi/default.nix b/parts/features/dev/neovim/_vim-visual-multi/default.nix similarity index 100% rename from home/neovim/vim-visual-multi/default.nix rename to parts/features/dev/neovim/_vim-visual-multi/default.nix diff --git a/home/neovim/web-devicons/default.nix b/parts/features/dev/neovim/_web-devicons/default.nix similarity index 100% rename from home/neovim/web-devicons/default.nix rename to parts/features/dev/neovim/_web-devicons/default.nix diff --git a/home/neovim/yank-file-line/default.nix b/parts/features/dev/neovim/_yank-file-line/default.nix similarity index 100% rename from home/neovim/yank-file-line/default.nix rename to parts/features/dev/neovim/_yank-file-line/default.nix diff --git a/home/neovim/yank-file-line/yank-file-line.lua b/parts/features/dev/neovim/_yank-file-line/yank-file-line.lua similarity index 100% rename from home/neovim/yank-file-line/yank-file-line.lua rename to parts/features/dev/neovim/_yank-file-line/yank-file-line.lua diff --git a/parts/features/dev/neovim/default.nix b/parts/features/dev/neovim/default.nix new file mode 100644 index 00000000..d6f624e6 --- /dev/null +++ b/parts/features/dev/neovim/default.nix @@ -0,0 +1,320 @@ +{...}: { + flake.modules.homeManager.neovim = { + osConfig, + pkgs, + lib, + ... + }: let + isLightTheme = osConfig.specialisation != {}; + in { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".local/share/nvim" # Data + ".local/state/nvim" # state + ".cache/nvim" + ]; + }; + + imports = + [ + ./_avante # Cursor style AI IDE + ./_carbon + ./_cmp + ./_comment-nvim + ./_dadbod + ./_dap + ./_diffview-nvim + ./_fidget # Sidebar notifications for LSP + ./_folds + ./_fugitive + ./_git-conflict-nvim + ./_gitsigns + ./_jdtls + ./_jj + ./_lspsaga + ./_lualine + ./_mason-lsp + # Ensure devicons module is available before mocking it with mini.icons + ./_mini-icons + ./_mini-operators + ./_neotest + ./_noice # UI for commandline, messages and popupmenu + ./_conform + ./_notify # Pluggable Notifications + ./_nui # UI Components + ./_nvim-tree-lua # File Tree + ./_obsidian-nvim + ./_oil + ./_otter # LSP for embedded code in markdown/quarto + ./_overseer + ./_plenary # LUA Functions + ./_rainbow-csv + ./_render-markdown + ./_telescope + ./_toggleterm + ./_treesitter + ./_trouble + ./_undotree + ./_vim-be-good # Vim Motion Learnenings + ./_vim-terraform + ./_vim-visual-multi + ./_web-devicons + ./_yank-file-line + ] + ++ lib.optionals isLightTheme [./_gruvbox]; + + programs.neovim = { + enable = true; + + extraConfig = '' + set mouse=a + set number + set clipboard=unnamedplus + set ignorecase + set smartcase + + " Defaults to be overwritten by vim-sleuth + set tabstop=4 + set shiftwidth=4 + + set grepprg=rg\ --vimgrep\ --no-heading\ --smart-case + + noremap h ; + noremap ; l + noremap l k + noremap k j + noremap j h + noremap ; l + noremap l k + noremap k j + noremap j h + noremap : L + noremap L K + noremap K J + noremap J H + + " Vertical Awesomeness + nnoremap zz + nnoremap zz + nnoremap G Gzz + + tnoremap h + tnoremap j + tnoremap k + tnoremap l + inoremap h + inoremap j + inoremap k + inoremap l + nnoremap h + nnoremap j + nnoremap k + nnoremap l + + set shell=/etc/profiles/per-user/farlion/bin/fish + + let mapleader = ' ' + let maplocalleader = ',' + + " Diff Settings + set diffopt+=internal,algorithm:patience + + " Quickfix Lists + nnoremap :cprev + nnoremap :cnext + + " Saving - use z. for centering the cursor instead + nnoremap zz :update + + " Applying a macro to lines matching in visual selection + xnoremap @ :call ExecuteMacroOverVisualRange() + function! ExecuteMacroOverVisualRange() + echo "@".getcmdline() + execute ":'<,'>normal @".nr2char(getchar()) + endfunction + + lua require('crates').setup() + + " Fugitive + " See https://github.com/tpope/vim-fugitive/issues/1510#issuecomment-660837020 + function! s:ftplugin_fugitive() abort + nnoremap cc :Git commit --quiet + nnoremap ca :Git commit --quiet --amend + nnoremap ce :Git commit --quiet --amend --no-edit + endfunction + augroup quiet_fugitive + autocmd! + autocmd FileType fugitive call s:ftplugin_fugitive() + augroup END + + " Vim Visual Multi + let g:VM_custom_motions = {'h': ';', ';': 'l', 'l': 'k', 'k': 'j', 'j': 'h'} + let g:VM_mouse_mappings = 1 + + " Background light/dark toggling + nmap i :let &bg=(&bg=='light'?'dark':'light') + + " Sudo powers with :w!! + cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' edit! + ''; + + extraLuaConfig = '' + vim.opt.termguicolors = true + vim.o.breakindent = true + vim.keymap.set('n', 'k', "v:count == 0 ? 'gj' : 'j'", { expr = true, silent = true }) + vim.keymap.set('n', 'l', "v:count == 0 ? 'gk' : 'k'", { expr = true, silent = true }) + vim.o.undofile = true + vim.o.timeout = true + vim.o.timeoutlen = 300 + vim.o.updatetime = 250 + vim.o.completeopt = 'menuone,noselect' + vim.keymap.set({ 'n', 'v' }, '', '', { silent = true }) + vim.opt.swapfile = false + vim.keymap.set('t', '', '', { silent = true }) + vim.keymap.set('t', '', [[]], { silent = true }) + vim.keymap.set({ 'n', 'v', 't' }, 'ZQ', ':qa!') + vim.keymap.set({'n', 'i'}, '', '', { noremap = true, silent = true }) + ''; + + extraPackages = with pkgs; [ + harper # Grammar checker + nixd # Nix Language Server + nodejs # For vim to have npm for Mason etc... + prettierd # For yaml, html, json, markdown + pyright + shellcheck + shfmt + ]; + + plugins = with pkgs.vimPlugins; [ + argtextobj-vim + { + plugin = b64-nvim; # Base64 encoding/decoding + config = '' + local wk = require("which-key") + wk.add( + { + { + mode = { "v" }, + { "b", group = "[B]ase64" }, + { "bd", require("b64").decode, desc = "Base64 [D]ecode" }, + { "be", require("b64").decode, desc = "Base64 [E]ncode" }, + }, + } + ) + ''; + type = "lua"; + } + { + plugin = dressing-nvim; # Better UI for codeactions, code input etc... + config = ""; + type = "lua"; + } + {plugin = friendly-snippets;} # User-friendly snippets, work with LuaSnip and other engines + vim-highlightedyank + { + plugin = indent-blankline-nvim; # Indentation guides + config = '' + require("ibl").setup({ + exclude = { + filetypes = {"startify"}, + } + }) + local hooks = require "ibl.hooks" + hooks.register(hooks.type.ACTIVE, function(bufnr) + return vim.tbl_contains( + { "yaml", "html", "svelte" }, + vim.api.nvim_get_option_value("filetype", { buf = bufnr }) + ) + end) + local wk = require("which-key") + wk.add({ + { "[i", function() require("ibl").setup_buffer(0, {enabled = true}) end, desc = "Indentation Guides ON" }, + { "]i", function() require("ibl").setup_buffer(0, {enabled = false}) end, desc = "Indentation Guides OFF" }, + }) + ''; + type = "lua"; + } + {plugin = nvim-lspconfig;} # Defaults for loads of LSP languages + {plugin = luasnip;} # Snippet engine + { + plugin = markdown-preview-nvim; + config = '' + local wk = require("which-key") + wk.add({ + { "p", "MarkdownPreviewToggle", desc = "Toggle Markdown [P]review" }, + }) + ''; + type = "lua"; + } + vim-numbertoggle + { + plugin = vim-qf; # Quickfix improvements + config = ""; + } + vim-rooter + vim-sleuth # Automatic shiftwidth and expandtab + { + plugin = vim-startify; + config = '' + let g:startify_change_to_dir = 0 + let g:startify_change_to_vcs_root = 1 + let g:startify_session_persistence = 1 + let g:startify_bookmarks = [ {'c': '~/nixos-config/parts/features/dev/neovim/default.nix'} ] + ''; + } + vim-surround + {plugin = vim-suda;} # Sudo support via :SudaRead and :SudaWrite + { + plugin = text-case-nvim; + config = '' + require("textcase").setup({ + prefix = "ga", + }) + require("telescope").load_extension("textcase") + local wk = require("which-key") + wk.add({ + { "ga.", "TextCaseOpenTelescope", desc = "Telescope" }, + }) + ''; + type = "lua"; + } + vim-textobj-entire + vim-toml + vim-unimpaired + { + plugin = which-key-nvim; + config = '' + require("which-key").setup { + } + ''; + type = "lua"; + } + ## Language Specific Plugins + crates-nvim + dart-vim-plugin + elm-vim + vim-graphql + vim-jsonnet + { + plugin = neodev-nvim; # Nvim LUA development + config = '' + require("neodev").setup({ + library = { plugins = { "nvim-dap-ui", "neotest" }, types = true }, + }) + ''; + type = "lua"; + } + vim-flutter + vim-helm + vim-shellcheck + vim-solidity + vim-nix + ]; + + viAlias = true; + vimAlias = true; + vimdiffAlias = true; + }; + }; +} diff --git a/parts/features/hardware/amd.nix b/parts/features/hardware/amd.nix new file mode 100644 index 00000000..b8cbcdd8 --- /dev/null +++ b/parts/features/hardware/amd.nix @@ -0,0 +1,42 @@ +# See: https://wiki.nixos.org/wiki/AMD_GPU +# Also see: https://wiki.archlinux.org/title/Hardware_video_acceleration +{...}: { + flake.modules.nixos.amd = {config, lib, pkgs, ...}: + lib.mkIf config.dendrix.hasAmd { + hardware.amdgpu = { + initrd.enable = true; + opencl.enable = true; + }; + + # Disable AMD GPU power management to see if it prevents feezes on S3/s2idle + boot.kernelParams = [ + "amdgpu.runpm=0" + ]; + + environment.systemPackages = with pkgs; [ + lact # GUI for overclocking, undervolting, setting fan curves, etc. + libva-utils + mesa-demos + nvtopPackages.full # nvtop + vulkan-tools + ]; + services.xserver.videoDrivers = ["amdgpu"]; + hardware.graphics = { + enable = true; + enable32Bit = true; + }; + + # # AMDVLK drivers (programs will choose whether to use this over Mesa RADV drivers) + # hardware.graphics.extraPackages = with pkgs; [ + # amdvlk + # ]; + # # For 32 bit applications + # hardware.opengl.extraPackages32 = with pkgs; [ + # driversi686Linux.amdvlk + # ]; + + # GUI for overclocking, undervolting, setting fan curves, etc. + systemd.packages = with pkgs; [lact]; + systemd.services.lactd.wantedBy = ["multi-user.target"]; + }; +} diff --git a/parts/features/hardware/audio.nix b/parts/features/hardware/audio.nix new file mode 100644 index 00000000..f82ad6e5 --- /dev/null +++ b/parts/features/hardware/audio.nix @@ -0,0 +1,108 @@ +{...}: { + flake.modules.nixos.audio = { + config, + lib, + pkgs, + ... + }: { + home-manager.users.farlion.home.persistence."/persist" = lib.mkIf config.dendrix.isImpermanent { + directories = [ + ".local/state/wireplumber" # Wireplumber state + ".config/rncbc.org" # qpwgraph config file + ".config/pulse" # pulseaudio cookie + ]; + }; + + environment.systemPackages = with pkgs; [ + alsa-utils + pulseaudioFull + qpwgraph # More extensive patchbay for Pipewire + ]; + + users.users.farlion.extraGroups = ["audio"]; + + # PipeWire! + security.rtkit.enable = true; + services.pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + + extraConfig.pipewire."92-adjust-clock-quantum" = { + "context.properties" = { + "default.clock.quantum" = 2048; # Larger buffers should prevent xruns + "default.clock.min-quantum" = 512; # Larger buffers should prevent xruns + "default.clock.max-quantum" = 8192; # Matches Windows Settings + }; + }; + extraConfig.pipewire."93-disable-autosuspend" = { + "context.properties" = { + "session.suspend-timeout-seconds" = 0; # Prevent autosuspend of ALSA nodes, causing xruns and crashes + }; + }; + + wireplumber.extraConfig = { + # Enable Fancy Blueooth Codecs + "monitor.bluez.properties" = { + "bluez5.enable-sbc-xq" = true; + "bluez5.enable-msbc" = true; + "bluez5.enable-hw-volume" = true; + "bluez5.roles" = ["hsp_hs" "hsp_ag" "hfp_hf" "hfp_ag"]; + }; + + # Disable unused sinks and sources + "disable-unused-nodes" = { + "monitor.alsa.rules" = [ + { + matches = [ + { + "device.nick" = "HDA NVidia"; + } + ]; + actions = { + update-props = { + "device.disabled" = true; + }; + }; + } + ]; + }; + }; + }; + }; + + flake.modules.homeManager.audio = { + lib, + pkgs, + osConfig, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/easyeffects" + ]; + }; + + home.file = { + # IRS file from the same repo above + ".config/easyeffects/irs/Razor Surround ((48k Z-Edition)) 2.Stereo +20 bass.irs".source = ./audio/_presets/irs/razor-surround-48k-z-edition-stereo-plus20-bass.irs; + ".config/pulsemixer.cfg".source = ./audio/pulsemixer.cfg; + }; + + home.packages = [ + pkgs.pavucontrol + pkgs.pulsemixer + ]; + + # GUI for PipeWire effects + services.easyeffects = { + enable = true; + # Preset from https://github.com/JackHack96/EasyEffects-Presets/blob/master/Bass%20Enhancing%20%2B%20Perfect%20EQ.json + preset = "bass-enhancing-perfect-eq"; + extraPresets = { + "bass-enhancing-perfect-eq" = builtins.fromJSON (builtins.readFile ./audio/_presets/output/bass-enhancing-perfect-eq.json); + }; + }; + }; +} diff --git a/home/easyeffects/presets/irs/razor-surround-48k-z-edition-stereo-plus20-bass.irs b/parts/features/hardware/audio/_presets/irs/razor-surround-48k-z-edition-stereo-plus20-bass.irs similarity index 100% rename from home/easyeffects/presets/irs/razor-surround-48k-z-edition-stereo-plus20-bass.irs rename to parts/features/hardware/audio/_presets/irs/razor-surround-48k-z-edition-stereo-plus20-bass.irs diff --git a/home/easyeffects/presets/output/bass-enhancing-perfect-eq.json b/parts/features/hardware/audio/_presets/output/bass-enhancing-perfect-eq.json similarity index 100% rename from home/easyeffects/presets/output/bass-enhancing-perfect-eq.json rename to parts/features/hardware/audio/_presets/output/bass-enhancing-perfect-eq.json diff --git a/home/pulsemixer/pulsemixer.cfg b/parts/features/hardware/audio/pulsemixer.cfg similarity index 100% rename from home/pulsemixer/pulsemixer.cfg rename to parts/features/hardware/audio/pulsemixer.cfg diff --git a/parts/features/hardware/bluetooth.nix b/parts/features/hardware/bluetooth.nix new file mode 100644 index 00000000..64841364 --- /dev/null +++ b/parts/features/hardware/bluetooth.nix @@ -0,0 +1,18 @@ +# Bluetooth TUI +{...}: { + flake.modules.nixos.bluetooth = {config, lib, ...}: { + environment.persistence."/persist/system" = lib.mkIf config.dendrix.isImpermanent { + directories = [ + "/var/lib/bluetooth" + ]; + }; + services.blueman.enable = true; + hardware.bluetooth.enable = true; + }; + + flake.modules.homeManager.bluetuith = {...}: { + programs.bluetuith = { + enable = true; + }; + }; +} diff --git a/parts/features/hardware/btrfs.nix b/parts/features/hardware/btrfs.nix new file mode 100644 index 00000000..532712f5 --- /dev/null +++ b/parts/features/hardware/btrfs.nix @@ -0,0 +1,16 @@ +{...}: { + flake.modules.nixos.btrfs = {config, lib, ...}: + lib.mkIf config.dendrix.isBtrfs { + environment.persistence."/persist/system" = lib.mkIf config.dendrix.isImpermanent { + directories = [ + "/var/lib/btrfs" # Scrub reports + ]; + }; + + services.btrfs.autoScrub = { + enable = true; + interval = "monthly"; + fileSystems = ["/"]; # Subvols of the same mount point don't need to be scrubbed + }; + }; +} diff --git a/parts/features/hardware/firmware.nix b/parts/features/hardware/firmware.nix new file mode 100644 index 00000000..08e05e40 --- /dev/null +++ b/parts/features/hardware/firmware.nix @@ -0,0 +1,10 @@ +# Linux Firmware Updates +{...}: { + flake.modules.nixos.firmware = {config, lib, ...}: { + environment.persistence."/persist/system" = lib.mkIf config.dendrix.isImpermanent { + directories = ["/var/lib/fwupd"]; + }; + + services.fwupd.enable = true; + }; +} diff --git a/parts/features/hardware/io.nix b/parts/features/hardware/io.nix new file mode 100644 index 00000000..6b5b5f7a --- /dev/null +++ b/parts/features/hardware/io.nix @@ -0,0 +1,45 @@ +{...}: { + flake.modules.nixos.io = {pkgs, ...}: { + hardware.logitech.wireless = { + enable = true; + enableGraphical = true; + }; + + # Keyd remappenings + environment.systemPackages = [pkgs.keyd]; + services.keyd = { + enable = true; + keyboards.default = { + ids = ["*"]; + settings = { + main = { + # Tap = Esc, hold = enter the fkeys layer + capslock = "overload(fkeys, esc)"; + }; + fkeys = { + "1" = "f1"; + "2" = "f2"; + "3" = "f3"; + "4" = "f4"; + "5" = "f5"; + "6" = "f6"; + "7" = "f7"; + "8" = "f8"; + "9" = "f9"; + "0" = "f10"; + minus = "f11"; + equal = "f12"; + }; + }; + }; + }; + + # Improves palm-rejection with the keyd virtual keyboard + environment.etc."libinput/local-overrides.quirks".text = '' + [Serial Keyboards] + MatchUdevType=keyboard + MatchName=keyd*keyboard + AttrKeyboardIntegration=internal + ''; + }; +} diff --git a/parts/features/hardware/nvidia.nix b/parts/features/hardware/nvidia.nix new file mode 100644 index 00000000..86bfb37b --- /dev/null +++ b/parts/features/hardware/nvidia.nix @@ -0,0 +1,53 @@ +# See: https://nixos.wiki/wiki/Nvidia +{...}: { + flake.modules.nixos.nvidia = {config, lib, pkgs, ...}: + lib.mkIf config.dendrix.hasNvidia { + boot.blacklistedKernelModules = ["nouveau"]; + environment.systemPackages = [ + pkgs.nvtopPackages.full # nvtop + pkgs.mesa-demos + pkgs.vulkan-tools + pkgs.libva-utils + pkgs.nvidia-vaapi-driver # VA-API implementation using NVIDIA's NVDEC + ]; + + # Enable VAAPI for NVIDIA + environment.sessionVariables = { + LIBVA_DRIVER_NAME = "nvidia"; + NVD_BACKEND = "direct"; # Use direct backend for better performance + }; + + services.xserver.videoDrivers = ["nvidia"]; + hardware.graphics = { + enable = true; + }; + + hardware.nvidia = { + # Modesetting is required. + modesetting.enable = true; + + # Nvidia power management. Experimental, and can cause sleep/suspend to fail. + # Enable this if you have graphical corruption issues or application crashes after waking + # up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead + # of just the bare essentials. + powerManagement.enable = false; + + # Fine-grained power management. Turns off GPU when not in use. + # Experimental and only works on modern Nvidia GPUs (Turing or newer). + powerManagement.finegrained = true; + + # Use the NVidia open source kernel module (not to be confused with the + # independent third-party "nouveau" open source driver). + # Support is limited to the Turing and later architectures. Full list of + # supported GPUs is at: + # https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus + # Only available from driver 515.43.04+ + # Currently alpha-quality/buggy, so false is currently the recommended setting. + open = false; + + # Enable the Nvidia settings menu, + # accessible via `nvidia-settings`. + nvidiaSettings = true; + }; + }; +} diff --git a/parts/features/hardware/power.nix b/parts/features/hardware/power.nix new file mode 100644 index 00000000..a4aa6173 --- /dev/null +++ b/parts/features/hardware/power.nix @@ -0,0 +1,54 @@ +{...}: { + flake.modules.nixos.power = { + config, + lib, + pkgs, + ... + }: let + isFlexbox = config.dendrix.hostname == "flexbox"; + in { + # This will save you money and possibly your life! + services.thermald.enable = true; + + services.logind = { + settings = { + Login = { + HandleLidSwitch = "suspend-then-hibernate"; + HandleLidSwitchDocked = "lock"; + HandleLidSwitchExternalPower = "lock"; + }; + }; + }; + systemd.sleep.extraConfig = '' + HibernateDelaySec=1h + ''; + + environment.systemPackages = + [] + ++ lib.lists.optional isFlexbox pkgs.libsmbios; # Dell-specific power management + + services.auto-cpufreq = { + enable = true; + settings = {}; + }; + + security.sudo.extraRules = [ + { + users = ["farlion"]; + commands = [ + { + command = "${pkgs.auto-cpufreq}/bin/auto-cpufreq --force *"; + options = ["NOPASSWD" "SETENV"]; + } + { + command = "/run/current-system/sw/bin/auto-cpufreq --force *"; + options = ["NOPASSWD" "SETENV"]; + } + ]; + } + ]; + + # Dbus Service provding historical battery stats, access to external device batteries... etc. + services.upower.enable = true; + }; +} diff --git a/parts/features/hardware/video.nix b/parts/features/hardware/video.nix new file mode 100644 index 00000000..cde8ccd3 --- /dev/null +++ b/parts/features/hardware/video.nix @@ -0,0 +1,14 @@ +{...}: { + flake.modules.nixos.video = {pkgs, ...}: { + programs.obs-studio = { + enable = true; + enableVirtualCamera = true; + }; + + environment.systemPackages = [ + pkgs.v4l-utils # Video4Linux2 -> configuring webcam + ]; + + users.users.farlion.extraGroups = ["video"]; + }; +} diff --git a/parts/features/security/bitwarden.nix b/parts/features/security/bitwarden.nix new file mode 100644 index 00000000..c6df77d5 --- /dev/null +++ b/parts/features/security/bitwarden.nix @@ -0,0 +1,19 @@ +{...}: { + flake.modules.homeManager.bitwarden = { + osConfig, + lib, + pkgs, + ... + }: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".config/Bitwarden" + ]; + }; + + home.packages = [ + pkgs.bitwarden-desktop + pkgs.bitwarden-cli + ]; + }; +} diff --git a/parts/features/security/security.nix b/parts/features/security/security.nix new file mode 100644 index 00000000..40adf508 --- /dev/null +++ b/parts/features/security/security.nix @@ -0,0 +1,66 @@ +{...}: { + flake.modules.nixos.security = { + config, + lib, + pkgs, + ... + }: { + environment.persistence."/persist/system" = lib.mkIf config.dendrix.isImpermanent { + directories = [ + "/var/lib/boltd" # Boltd state + "/run/sudo" # Sudo timestamp (to not show the lecture message) + ]; + }; + home-manager.users.farlion.home.persistence."/persist" = lib.mkIf config.dendrix.isImpermanent { + directories = [ + ".local/share/keyrings" # Gnome Keyrings + ".gnupg" # PGP keys + ]; + }; + + # Thunderbolt security daemon + services.hardware.bolt.enable = true; + + services.gnome.gnome-keyring.enable = true; + environment.systemPackages = with pkgs; [ + libsecret # Already in home packages but ensuring it's available system-wide + ]; + + programs.gnupg.agent = { + enable = true; + enableSSHSupport = false; # Let GNOME Keyring handle SSH agent + pinentryPackage = pkgs.pinentry-gnome3; + }; + + programs.seahorse.enable = true; + + # Writes to /etc/sudoers + security.sudo.extraConfig = '' + Defaults:root,%wheel timestamp_timeout=30 + ''; + users.users.farlion.extraGroups = ["wheel"]; + + # Yubikeys + services.pcscd.enable = true; # Smartcard services for Yubikeys + # Sudo via U2F (Yubikey) + security.pam = { + u2f = { + enable = true; + control = "sufficient"; + settings = { + origin = "pam://farlion-realm"; # Overridde host-dependent realm to share yubikey + appid = "pam://farlion-realm"; # keep equal to origin for compatibility + cue = false; # CLI message to show touch is needed, not needed since using system-wide notification + }; + }; + services = { + login.u2fAuth = false; + ly.u2fAuth = false; + sudo.u2fAuth = true; + swaylock.u2fAuth = true; + }; + }; + # Enable system-wide Yubikey Support + services.udev.packages = [pkgs.yubikey-personalization]; + }; +} diff --git a/home/yubico/modules/yubikey-touch-detector/default.nix b/parts/features/security/yubico/_modules/yubikey-touch-detector/default.nix similarity index 100% rename from home/yubico/modules/yubikey-touch-detector/default.nix rename to parts/features/security/yubico/_modules/yubikey-touch-detector/default.nix diff --git a/parts/features/security/yubico/default.nix b/parts/features/security/yubico/default.nix new file mode 100644 index 00000000..4f2fbb94 --- /dev/null +++ b/parts/features/security/yubico/default.nix @@ -0,0 +1,21 @@ +{...}: { + flake.modules.homeManager.yubico = {pkgs, ...}: { + imports = [ + ./_modules/yubikey-touch-detector + ]; + + home.packages = with pkgs; [ + pam_u2f # U2F (via yubikey) support for PAM + yubikey-manager # ykman + yubioath-flutter # Yubikey management GUI + ]; + + services = { + yubikey-touch-detector.enable = true; + dunst.settings.yubikey_touch_detector_icon = { + summary = "YubiKey is waiting for a touch"; + new_icon = "${pkgs.yubikey-touch-detector}/share/icons/hicolor/128x128/apps/yubikey-touch-detector.png"; + }; + }; + }; +} diff --git a/parts/features/services/kind-killer.nix b/parts/features/services/kind-killer.nix new file mode 100644 index 00000000..30dcbcb4 --- /dev/null +++ b/parts/features/services/kind-killer.nix @@ -0,0 +1,15 @@ +{...}: { + flake.modules.nixos.kind-killer = {pkgs, ...}: { + systemd.services.kind-killer = { + description = "Kill kind cluster on shutdown"; + after = ["docker.service"]; # Ensures docker is still running when trying to delete the cluster, since systemd reverses the ordering during shutdown + requires = ["docker.service"]; + wantedBy = ["multi-user.target"]; + serviceConfig = { + Environment = "PATH=$PATH:/run/current-system/sw/bin"; + ExecStart = "${pkgs.coreutils}/bin/sleep infinity"; + ExecStop = "${pkgs.kind}/bin/kind delete cluster"; + }; + }; + }; +} diff --git a/parts/features/services/localsend.nix b/parts/features/services/localsend.nix new file mode 100644 index 00000000..5a156448 --- /dev/null +++ b/parts/features/services/localsend.nix @@ -0,0 +1,11 @@ +{...}: { + flake.modules.nixos.localsend = {...}: { + programs.localsend.enable = true; + }; + + flake.modules.homeManager.localsend = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [".local/share/org.localsend.localsend_app"]; + }; + }; +} diff --git a/parts/features/services/printing.nix b/parts/features/services/printing.nix new file mode 100644 index 00000000..158d805e --- /dev/null +++ b/parts/features/services/printing.nix @@ -0,0 +1,18 @@ +{...}: { + flake.modules.nixos.printing = {pkgs, ...}: { + services.printing = { + enable = true; + drivers = [ + pkgs.gutenprint + pkgs.hplip + ]; + listenAddresses = ["127.0.0.1:631"]; + }; + + services.avahi = { + enable = true; + nssmdns4 = true; + openFirewall = true; + }; + }; +} diff --git a/parts/features/services/scrutiny.nix b/parts/features/services/scrutiny.nix new file mode 100644 index 00000000..d98ff271 --- /dev/null +++ b/parts/features/services/scrutiny.nix @@ -0,0 +1,24 @@ +{...}: { + flake.modules.nixos.scrutiny = {config, lib, ...}: { + environment.persistence."/persist/system" = lib.mkIf config.dendrix.isImpermanent { + directories = [ + { + directory = "/var/lib/private"; + mode = "0700"; + } + "/var/lib/private/scrutiny" + ]; + }; + + services.scrutiny = { + enable = true; + settings.web.listen.port = 8081; + }; + + systemd.services.scrutiny.enableStrictShellChecks = false; + + systemd.tmpfiles.rules = [ + "d /var/lib/private 0700 root root -" + ]; + }; +} diff --git a/parts/features/services/syncthing.nix b/parts/features/services/syncthing.nix new file mode 100644 index 00000000..3b3b986d --- /dev/null +++ b/parts/features/services/syncthing.nix @@ -0,0 +1,38 @@ +{...}: { + flake.modules.nixos.syncthing = {pkgs, ...}: { + # Prevent syncthing from preventing sleep + powerManagement.powerDownCommands = '' + export XDG_RUNTIME_DIR=/run/user/1000 + ${pkgs.systemd}/bin/machinectl shell farlion@ /run/current-system/sw/bin/systemctl --user stop syncthing.service + ''; + powerManagement.resumeCommands = '' + export XDG_RUNTIME_DIR=/run/user/1000 + ${pkgs.systemd}/bin/machinectl shell farlion@ /run/current-system/sw/bin/systemctl --user start syncthing.service + ''; + }; + + flake.modules.homeManager.syncthing = {lib, osConfig, ...}: { + home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent { + directories = [ + ".local/state/syncthing" # device keys and certificates + ".config/syncthing" # pre-v1.27.0 uses this instead of $XDG_STATE_HOME above, keeping for backward-compatibility + ]; + files = [ + ".config/syncthingtray.ini" + ]; + }; + + services.syncthing = { + enable = true; + tray = { + enable = true; + command = "syncthingtray --wait"; + }; + }; + + systemd.user.services.syncthingtray.Service = { + Restart = "on-failure"; + RestartSec = 5; + }; + }; +} diff --git a/system/virtualisation/scripts/benchmark-containers.nix b/parts/features/services/virtualisation/_scripts/benchmark-containers.nix similarity index 100% rename from system/virtualisation/scripts/benchmark-containers.nix rename to parts/features/services/virtualisation/_scripts/benchmark-containers.nix diff --git a/system/virtualisation/scripts/benchmark-containers.sh b/parts/features/services/virtualisation/_scripts/benchmark-containers.sh similarity index 100% rename from system/virtualisation/scripts/benchmark-containers.sh rename to parts/features/services/virtualisation/_scripts/benchmark-containers.sh diff --git a/system/virtualisation/scripts/benchmark-heavy-containers.nix b/parts/features/services/virtualisation/_scripts/benchmark-heavy-containers.nix similarity index 100% rename from system/virtualisation/scripts/benchmark-heavy-containers.nix rename to parts/features/services/virtualisation/_scripts/benchmark-heavy-containers.nix diff --git a/system/virtualisation/scripts/benchmark-heavy-containers.sh b/parts/features/services/virtualisation/_scripts/benchmark-heavy-containers.sh similarity index 100% rename from system/virtualisation/scripts/benchmark-heavy-containers.sh rename to parts/features/services/virtualisation/_scripts/benchmark-heavy-containers.sh diff --git a/system/virtualisation/scripts/reset-container-state.nix b/parts/features/services/virtualisation/_scripts/reset-container-state.nix similarity index 100% rename from system/virtualisation/scripts/reset-container-state.nix rename to parts/features/services/virtualisation/_scripts/reset-container-state.nix diff --git a/system/virtualisation/scripts/reset-container-state.sh b/parts/features/services/virtualisation/_scripts/reset-container-state.sh similarity index 100% rename from system/virtualisation/scripts/reset-container-state.sh rename to parts/features/services/virtualisation/_scripts/reset-container-state.sh diff --git a/parts/features/services/virtualisation/default.nix b/parts/features/services/virtualisation/default.nix new file mode 100644 index 00000000..b78b5d3e --- /dev/null +++ b/parts/features/services/virtualisation/default.nix @@ -0,0 +1,120 @@ +{...}: { + flake.modules.nixos.virtualisation = { + config, + lib, + pkgs, + ... + }: let + benchmark-containers = pkgs.writeShellApplication { + name = "benchmark-containers"; + runtimeInputs = with pkgs; [docker podman coreutils]; + text = builtins.readFile ./_scripts/benchmark-containers.sh; + }; + benchmark-heavy-containers = pkgs.writeShellApplication { + name = "benchmark-heavy-containers"; + runtimeInputs = with pkgs; [docker podman coreutils]; + text = builtins.readFile ./_scripts/benchmark-heavy-containers.sh; + }; + reset-container-state = pkgs.writeShellApplication { + name = "reset-container-state"; + runtimeInputs = with pkgs; [docker podman coreutils systemd]; + text = builtins.readFile ./_scripts/reset-container-state.sh; + }; + in { + environment.persistence."/persist/system" = lib.mkIf config.dendrix.isImpermanent { + directories = [ + "/var/lib/containers" # Podman + "/var/lib/docker" + "/var/lib/libvirt" # Virt-Manager + ]; + }; + home-manager.users.farlion.home.persistence."/persist" = lib.mkIf config.dendrix.isImpermanent { + directories = [ + ".local/share/containers" # Podman (userspace) + ]; + }; + + environment.systemPackages = [ + pkgs.podman-compose # docker-compose rewritten with podman backend + pkgs.virt-manager # Desktop user interface for managing virtual machines + benchmark-containers + benchmark-heavy-containers + reset-container-state + ]; + + virtualisation.docker = { + enable = true; + # Explicitly use overlay2 for best performance and stability + storageDriver = "overlay2"; + daemon.settings = { + # Attach to resolved instead of using default Docker DNS servers + dns = ["172.17.0.1"]; + # Have containers listen on localhost instead of 0.0.0.0, + # see https://github.com/NixOS/nixpkgs/issues/111852#issuecomment-1954656069 + ip = "127.0.0.1"; + ipv6 = false; + ip6tables = false; + iptables = true; + }; + }; + + # Allow connecting to resolved DNS from inside Docker containers + networking.firewall.interfaces.docker0.allowedTCPPorts = [53]; + networking.firewall.interfaces.docker0.allowedUDPPorts = [53]; + + networking.firewall.extraCommands = '' + # Allow DNS (port 53) on all Docker bridge interfaces + for iface in $(${pkgs.iproute2}/bin/ip link show | grep -o 'br-[a-f0-9]\{12\}' || true); do + if [ -n "$iface" ]; then + # Insert before default REJECT so these rules are effective + iptables -I nixos-fw 1 -i "$iface" -p tcp --dport 53 -j ACCEPT + iptables -I nixos-fw 1 -i "$iface" -p udp --dport 53 -j ACCEPT + fi + done + + # Allow cross-network DNS resolution between Docker networks + # This allows containers on any Docker network to reach DNS servers on other Docker networks + iptables -I FORWARD 1 -s 172.16.0.0/12 -d 172.16.0.0/12 -p tcp --dport 53 -j ACCEPT + iptables -I FORWARD 1 -s 172.16.0.0/12 -d 172.16.0.0/12 -p udp --dport 53 -j ACCEPT + + # Fix MTU issues for Docker -> Tailscale traffic + # Clamp MSS to account for Tailscale's MTU of 1280 + # Without this, HTTPS connections from Docker to Tailscale frequently fail. + iptables -t mangle -A FORWARD -s 172.17.0.0/16 -d 100.64.0.0/10 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + iptables -t mangle -A FORWARD -s 172.18.0.0/16 -d 100.64.0.0/10 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + iptables -t mangle -A FORWARD -s 172.17.0.0/16 -d 100.100.0.0/16 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + iptables -t mangle -A FORWARD -s 172.18.0.0/16 -d 100.100.0.0/16 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + ''; + + virtualisation.libvirtd.enable = true; + + virtualisation.podman = { + enable = true; + }; + + # Configure Podman to use overlay with optimizations + virtualisation.containers.storage.settings = { + storage = { + driver = "overlay"; + runroot = "/run/containers/storage"; + graphroot = "/var/lib/containers/storage"; + options = { + # Pull images in parallel for better performance + pull_options = { + enable_partial_images = "true"; + use_hard_links = "false"; + ostree_repos = ""; + }; + overlay = { + # Use native overlay with metacopy for better performance + # metacopy=on allows fast copy-up of large files + mountopt = "nodev,metacopy=on"; + force_mask = "0000"; + }; + }; + }; + }; + + users.users.farlion.extraGroups = ["libvirtd" "kvm" "docker"]; + }; +} diff --git a/parts/hosts.nix b/parts/hosts.nix new file mode 100644 index 00000000..5e482cb6 --- /dev/null +++ b/parts/hosts.nix @@ -0,0 +1,178 @@ +{ + config, + lib, + inputs, + ... +}: let + inherit (inputs) nixpkgs home-manager impermanence niri nur sops-nix stylix determinate; + + # All dendritic modules for NixOS + nixosModules = builtins.attrValues config.flake.modules.nixos; + + # All dendritic modules for home-manager + homeManagerModules = builtins.attrValues config.flake.modules.homeManager; + + # Secrets modules (CI-safe: when overridden with nixpkgs, these are empty) + secretNixosModules = + if (inputs.secrets ? modules.nixos) + then builtins.attrValues inputs.secrets.modules.nixos + else []; + + secretHomeModules = + if (inputs.secrets ? modules.homeManager) + then builtins.attrValues inputs.secrets.modules.homeManager + else []; + + commonOverlays = { + unstable = import inputs.nixos-unstable { + system = "x86_64-linux"; + config.allowUnfree = true; + }; + }; + + # NixOS module that defines dendrix options for host-specific configuration + dendrixOptionsModule = {lib, ...}: { + options.dendrix = { + hostname = lib.mkOption { + type = lib.types.str; + description = "Hostname of this machine"; + }; + isLaptop = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether this is a laptop"; + }; + isImpermanent = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether this machine uses impermanence (ephemeral root)"; + }; + hasNvidia = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether this machine has an NVIDIA GPU"; + }; + hasAmd = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether this machine has an AMD GPU"; + }; + isBtrfs = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether this machine uses btrfs filesystem"; + }; + stateVersion = lib.mkOption { + type = lib.types.str; + description = "NixOS state version for this host"; + }; + homeStateVersion = lib.mkOption { + type = lib.types.str; + description = "Home Manager state version for this host"; + }; + }; + }; + + commonNixosModules = [ + dendrixOptionsModule + { + nixpkgs.overlays = [ + (_: _: commonOverlays) + ]; + } + determinate.nixosModules.default + nixpkgs.nixosModules.notDetected + nur.modules.nixos.default + impermanence.nixosModules.impermanence + sops-nix.nixosModules.sops + stylix.nixosModules.stylix + home-manager.nixosModules.home-manager + niri.nixosModules.niri + ]; + + commonHomeManagerSettings = { + useGlobalPkgs = true; + useUserPackages = true; + backupFileExtension = "home-manager-backup"; + sharedModules = + homeManagerModules + ++ secretHomeModules + ++ [ + "${impermanence}/home-manager.nix" + nur.modules.homeManager.default + sops-nix.homeManagerModules.sops + # Note: stylix home-manager module is injected by stylix.nixosModules.stylix + # Note: niri home-manager module is injected by niri.nixosModules.niri + ]; + }; + + mkHost = { + hostname, + hostModule, + dendrixConfig, + extraModules ? [], + }: + nixpkgs.lib.nixosSystem { + specialArgs = { + inherit inputs; + secrets = inputs.secrets; + }; + modules = + commonNixosModules + ++ nixosModules + ++ secretNixosModules + ++ [ + # Host-specific hardware and settings + hostModule + + # Set dendrix options + { + dendrix = dendrixConfig; + } + + # Home-manager configuration + { + home-manager = + commonHomeManagerSettings + // { + users.farlion = {pkgs, ...}: { + home.stateVersion = dendrixConfig.homeStateVersion; + }; + }; + } + ] + ++ extraModules; + }; +in { + flake.nixosConfigurations = { + flexbox = mkHost { + hostname = "flexbox"; + hostModule = ./_hosts/flexbox; + dendrixConfig = { + hostname = "flexbox"; + isLaptop = true; + isImpermanent = false; + hasNvidia = true; + hasAmd = false; + isBtrfs = false; + stateVersion = "22.05"; + homeStateVersion = "22.05"; + }; + }; + + numenor = mkHost { + hostname = "numenor"; + hostModule = ./_hosts/numenor; + dendrixConfig = { + hostname = "numenor"; + isLaptop = false; + isImpermanent = true; + hasNvidia = false; + hasAmd = true; + isBtrfs = true; + stateVersion = "24.11"; + homeStateVersion = "24.11"; + }; + }; + }; +} diff --git a/parts/modules.nix b/parts/modules.nix new file mode 100644 index 00000000..8b515f36 --- /dev/null +++ b/parts/modules.nix @@ -0,0 +1,5 @@ +{inputs, ...}: { + imports = [ + inputs.flake-parts.flakeModules.modules + ]; +} diff --git a/specialisations/light/default.nix b/specialisations/light/default.nix deleted file mode 100644 index 421881d3..00000000 --- a/specialisations/light/default.nix +++ /dev/null @@ -1,53 +0,0 @@ -{ - lib, - pkgs, - ... -}: { - specialisation.light.configuration = { - environment.etc."specialisation".text = "light"; # this is for 'nh' to correctly recognise the specialisation - - # System - stylix = { - base16Scheme = lib.mkForce "${pkgs.base16-schemes}/share/themes/catppuccin-latte.yaml"; - image = lib.mkForce ../../system/stylix/gruvbox-light-rainbow.png; - polarity = lib.mkForce "light"; - }; - - # Home Manager - home-manager.users.farlion = { - # Aichat Light Theme - home.sessionVariables = { - AICHAT_LIGHT_THEME = 1; - }; - - # Dunst - services.dunst.iconTheme.name = lib.mkForce "Papirus-Light"; - - # GTK - gtk.gtk3.extraConfig.gtk-application-prefer-dark-theme = lib.mkForce false; - - # QT - qt.enable = lib.mkForce false; - - # Neovim - programs.neovim = { - extraLuaConfig = '' - -- Override lualine theme for light mode - if require('lualine') then - local lualine_config = require('lualine').get_config() - lualine_config.options.theme = 'gruvbox-light' - require('lualine').setup(lualine_config) - end - ''; - }; - - stylix.targets = { - # Neovim - neovim.enable = lib.mkForce true; - }; - - # K9s - programs.k9s.settings.k9s.ui.skin = lib.mkForce "gruvbox-light"; - }; - }; -} diff --git a/system/amd/default.nix b/system/amd/default.nix deleted file mode 100644 index e630ef92..00000000 --- a/system/amd/default.nix +++ /dev/null @@ -1,39 +0,0 @@ -# See: https://wiki.nixos.org/wiki/AMD_GPU -# Also see: https://wiki.archlinux.org/title/Hardware_video_acceleration -{pkgs, ...}: { - hardware.amdgpu = { - initrd.enable = true; - opencl.enable = true; - }; - - # Disable AMD GPU power management to see if it prevents feezes on S3/s2idle - boot.kernelParams = [ - "amdgpu.runpm=0" - ]; - - environment.systemPackages = with pkgs; [ - lact # GUI for overclocking, undervolting, setting fan curves, etc. - libva-utils - mesa-demos - nvtopPackages.full # nvtop - vulkan-tools - ]; - services.xserver.videoDrivers = ["amdgpu"]; - hardware.graphics = { - enable = true; - enable32Bit = true; - }; - - # # AMDVLK drivers (programs will choose whether to use this over Mesa RADV drivers) - # hardware.graphics.extraPackages = with pkgs; [ - # amdvlk - # ]; - # # For 32 bit applications - # hardware.opengl.extraPackages32 = with pkgs; [ - # driversi686Linux.amdvlk - # ]; - - # GUI for overclocking, undervolting, setting fan curves, etc. - systemd.packages = with pkgs; [lact]; - systemd.services.lactd.wantedBy = ["multi-user.target"]; -} diff --git a/system/audio/default.nix b/system/audio/default.nix deleted file mode 100644 index ad820b65..00000000 --- a/system/audio/default.nix +++ /dev/null @@ -1,73 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let -in { - home-manager.users.farlion.home.persistence."/persist" = lib.mkIf config.home-manager.extraSpecialArgs.isImpermanent { - directories = [ - ".local/state/wireplumber" # Wireplumber state - ".config/rncbc.org" # qpwgraph config file - ".config/pulse" # pulseaudio cookie - ]; - }; - - environment.systemPackages = with pkgs; [ - alsa-utils - pulseaudioFull - qpwgraph # More extensive patchbay for Pipewire - ]; - - users.users.farlion.extraGroups = ["audio"]; - - # PipeWire! - security.rtkit.enable = true; - services.pipewire = { - enable = true; - alsa.enable = true; - alsa.support32Bit = true; - pulse.enable = true; - - extraConfig.pipewire."92-adjust-clock-quantum" = { - "context.properties" = { - "default.clock.quantum" = 2048; # Larger buffers should prevent xruns - "default.clock.min-quantum" = 512; # Larger buffers should prevent xruns - "default.clock.max-quantum" = 8192; # Matches Windows Settings - }; - }; - extraConfig.pipewire."93-disable-autosuspend" = { - "context.properties" = { - "session.suspend-timeout-seconds" = 0; # Prevent autosuspend of ALSA nodes, causing xruns and crashes - }; - }; - - wireplumber.extraConfig = { - # Enable Fancy Blueooth Codecs - "monitor.bluez.properties" = { - "bluez5.enable-sbc-xq" = true; - "bluez5.enable-msbc" = true; - "bluez5.enable-hw-volume" = true; - "bluez5.roles" = ["hsp_hs" "hsp_ag" "hfp_hf" "hfp_ag"]; - }; - - # Disable unused sinks and sources - "disable-unused-nodes" = { - "monitor.alsa.rules" = [ - { - matches = [ - { - "device.nick" = "HDA NVidia"; - } - ]; - actions = { - update-props = { - "device.disabled" = true; - }; - }; - } - ]; - }; - }; - }; -} diff --git a/system/bluetooth/default.nix b/system/bluetooth/default.nix deleted file mode 100644 index d6cebb3d..00000000 --- a/system/bluetooth/default.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ - lib, - isImpermanent, - ... -}: { - environment.persistence."/persist/system" = lib.mkIf isImpermanent { - directories = [ - "/var/lib/bluetooth" - ]; - }; - services.blueman.enable = true; - hardware.bluetooth.enable = true; -} diff --git a/system/boot/default.nix b/system/boot/default.nix deleted file mode 100644 index dd29ef7c..00000000 --- a/system/boot/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{...}: let -in { - boot = { - loader.systemd-boot = { - enable = true; - memtest86.enable = true; - }; - loader.efi.canTouchEfiVariables = true; - consoleLogLevel = 7; - - initrd = { - systemd = { - enable = true; - }; - }; - }; -} diff --git a/system/btrfs/default.nix b/system/btrfs/default.nix deleted file mode 100644 index 2a0d19e1..00000000 --- a/system/btrfs/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - lib, - isImpermanent, - ... -}: { - environment.persistence."/persist/system" = lib.mkIf isImpermanent { - directories = [ - "/var/lib/btrfs" # Scrub reports - ]; - }; - - services.btrfs.autoScrub = { - enable = true; - interval = "monthly"; - fileSystems = ["/"]; # Subvols of the same mount point don't need to be scrubbed - }; -} diff --git a/system/cachix/default.nix b/system/cachix/default.nix deleted file mode 100644 index d6f0c3f9..00000000 --- a/system/cachix/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{pkgs, ...}: { - environment.systemPackages = [ - pkgs.cachix - ]; -} diff --git a/system/default.nix b/system/default.nix deleted file mode 100644 index dc5bd67e..00000000 --- a/system/default.nix +++ /dev/null @@ -1,46 +0,0 @@ -{ - lib, - isImpermanent, - pkgs, - secrets, - ... -}: let - systemSecrets = - if secrets ? systemSecrets - then secrets.systemSecrets {inherit isImpermanent lib pkgs;} - else {}; -in { - imports = - [ - ./audio - ./bluetooth - ./boot - ./cachix - ./desktop - ./dns - ./firmware - ./fonts - ./io - ./kernel - ./kind-killer - ./localsend # Apple Airdrop OSS - ./networking - ./nix-ld - ./performance - ./power - ./printing - ./scrutiny # Hard Drive S.M.A.R.T Monitoring, historical trends - ./security - ./smb - ./stylix - ./syncthing - ./systemd - ./users - ./various - ./video - ./virtualisation - ./wireshark - ] - ++ lib.lists.optionals isImpermanent [./impermanence] - ++ [systemSecrets]; -} diff --git a/system/desktop/default.nix b/system/desktop/default.nix deleted file mode 100644 index e4316672..00000000 --- a/system/desktop/default.nix +++ /dev/null @@ -1,68 +0,0 @@ -{ - isImpermanent, - lib, - pkgs, - ... -}: { - environment.persistence."/persist/system" = lib.mkIf isImpermanent { - files = [ - "/etc/ly/save.ini" # Selected user and session - ]; - }; - - services.displayManager = { - defaultSession = "niri"; - ly = { - enable = true; - settings = { - animation = "doom"; - hide_borders = true; - }; - }; - }; - - # Mounting Android phone from Files - services.gvfs.enable = true; - - programs.niri = { - enable = true; - }; - # XDG Portal Settings For Niri - xdg.portal = { - enable = true; - config = { - common = { - default = ["gnome" "gtk"]; - }; - niri = { - default = ["gnome" "gtk"]; - "org.freedesktop.impl.portal.ScreenCast" = ["gnome"]; - "org.freedesktop.impl.portal.Screenshot" = ["gnome"]; - "org.freedesktop.impl.portal.FileChooser" = ["gtk"]; - }; - }; - extraPortals = [ - pkgs.xdg-desktop-portal-gnome - pkgs.xdg-desktop-portal-gtk - ]; - }; - - # Fix niri portals autostart order - # See https://github.com/sodiboo/niri-flake/issues/509 - systemd.user.services.xdg-desktop-portal = { - after = ["xdg-desktop-autostart.target"]; - }; - systemd.user.services.xdg-desktop-portal-gtk = { - after = ["xdg-desktop-autostart.target"]; - }; - systemd.user.services.xdg-desktop-portal-gnome = { - after = ["xdg-desktop-autostart.target"]; - }; - systemd.user.services.niri-flake-polkit = { - after = ["xdg-desktop-autostart.target"]; - }; - - programs.sway = { - enable = true; - }; -} diff --git a/system/dns/default.nix b/system/dns/default.nix deleted file mode 100644 index 4a0daccd..00000000 --- a/system/dns/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -{...}: { - services.resolved = { - enable = true; - llmnr = "false"; # https://www.blackhillsinfosec.com/how-to-disable-llmnr-why-you-want-to/ - extraConfig = '' - MulticastDNS=false - DNSStubListenerExtra=172.17.0.1 - ''; - fallbackDns = []; # Ensure we always go through the configured DNS, no magic defaults - }; - networking.networkmanager.dns = "systemd-resolved"; -} diff --git a/system/firmware/default.nix b/system/firmware/default.nix deleted file mode 100644 index 37f95369..00000000 --- a/system/firmware/default.nix +++ /dev/null @@ -1,14 +0,0 @@ -# Linux Firmware Updates -{ - lib, - isImpermanent, - ... -}: { - environment.persistence."/persist/system" = lib.mkIf isImpermanent { - directories = [ - "/var/lib/fwupd" - ]; - }; - - services.fwupd.enable = true; -} diff --git a/system/fonts/default.nix b/system/fonts/default.nix deleted file mode 100644 index 0ed5b6e3..00000000 --- a/system/fonts/default.nix +++ /dev/null @@ -1,78 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let - # Looted from https://gist.github.com/elijahmanor/c10e5787bf9ac6b8c276e47e6745826c, much obliged - fontSmokeTest = pkgs.writers.writeBashBin "font-smoke-test" '' - set -e - - printf "%b\n" "Normal" - printf "%b\n" "\033[1mBold\033[22m" - printf "%b\n" "\033[3mItalic\033[23m" - printf "%b\n" "\033[3;1mBold Italic\033[0m" - printf "%b\n" "\033[4mUnderline\033[24m" - printf "%b\n" "== === !== >= <= =>" - printf "%b\n" "   󰾆   󱑥 󰒲 󰗼" - ''; - # Patch Fira Code with Nerd Fonts plus local Font Awesome 6 Pro glyphs into ~/.local/share/fonts - patchFiraWithFA6Pro = pkgs.writeShellApplication { - name = "patch-fira-with-fa6-pro"; - runtimeInputs = [ - pkgs.fira-code - pkgs.fontforge - pkgs.findutils - pkgs.coreutils - pkgs.fontconfig - pkgs.nerd-font-patcher - ]; - runtimeEnv = { - SRC_DIR = "${pkgs.fira-code}/share/fonts/truetype"; - }; - text = builtins.readFile ./scripts/patch-fira-with-fa6-pro.sh; - }; -in { - home-manager.users.farlion.home.persistence."/persist" = lib.mkIf config.home-manager.extraSpecialArgs.isImpermanent { - directories = [ - ".local/share/fonts" # Locally persisted fonts (not nixos-managed) - ".cache/fontconfig" # Fontconfig cache - ]; - }; - - environment.systemPackages = [ - fontSmokeTest - patchFiraWithFA6Pro - ]; - - # Run the patcher during Home Manager activation so that fonts are ready after switch - home-manager.users.farlion.home.activation.patchFiraWithFA6Pro = '' - OUT_DIR="$HOME/.local/share/fonts/NerdPatched/FiraCodeFAPro" - if [ ! -d "$OUT_DIR" ] || [ -z "$(ls -A "$OUT_DIR" 2>/dev/null)" ]; then - echo "[patch-fira-with-fa6-pro] Patched fonts not found or directory empty, running patcher..." - "${patchFiraWithFA6Pro}/bin/patch-fira-with-fa6-pro" - else - echo "[patch-fira-with-fa6-pro] Patched fonts already exist in $OUT_DIR, skipping..." - fi - ''; - - fonts = { - enableDefaultPackages = false; - packages = [ - pkgs.fira-code - pkgs.fira-code-symbols - pkgs.dejavu_fonts - pkgs.font-awesome_5 - pkgs.font-awesome_6 - pkgs.unstable.font-awesome - pkgs.noto-fonts-color-emoji # emoji font - ]; - fontconfig = { - defaultFonts = { - sansSerif = ["DejaVu Sans"]; - serif = ["DejaVu Serif"]; - monospace = ["FiraCode Nerd Font" "Fira Code"]; - }; - }; - }; -} diff --git a/system/impermanence/default.nix b/system/impermanence/default.nix deleted file mode 100644 index 04775b02..00000000 --- a/system/impermanence/default.nix +++ /dev/null @@ -1,105 +0,0 @@ -# General impermanence setup -# Note: specifics should live with their respective modules, where possible! -{ - lib, - pkgs, - ... -}: let - rootExplosion = '' - echo "Time to 🧨" >/dev/kmsg - mkdir /btrfs_tmp - mount /dev/mapper/nixos--vg-root /btrfs_tmp - - # Root impermanence - if [[ -e /btrfs_tmp/root ]]; then - mkdir -p /btrfs_tmp/persist/old_roots - timestamp=$(date --date="@$(stat -c %Y /btrfs_tmp/root)" "+%Y-%m-%d_%H:%M:%S") - if [[ ! -e /btrfs_tmp/persist/old_roots/$timestamp ]]; then - mv /btrfs_tmp/root "/btrfs_tmp/persist/old_roots/$timestamp" - else - btrfs subvolume delete /btrfs_tmp/root - fi - fi - - ### - # GC - ### - latest_snapshot=$(find /btrfs_tmp/persist/old_roots/ -mindepth 1 -maxdepth 1 -type d | sort -r | head -n 1) - # Only delete old snapshots if there's at least one that will remain after deletion - if [ -n "$latest_snapshot" ]; then - for i in $(find /btrfs_tmp/persist/old_roots/ -mindepth 1 -maxdepth 1 -mtime +30 | grep -v -e "$latest_snapshot"); do - btrfs subvolume delete -R "$i" - done - fi - - btrfs subvolume create /btrfs_tmp/root - umount /btrfs_tmp - echo "Done with 🧨. Au revoir!" >/dev/kmsg - ''; -in { - # Explode / on every boot and resume, see https://grahamc.com/blog/erase-your-darlings/ - boot.initrd.systemd = { - extraBin = { - grep = "${pkgs.gnugrep}/bin/grep"; - }; - services = { - root-explode = { - enableStrictShellChecks = false; - wantedBy = ["initrd-root-device.target"]; - wants = ["lvm2-activation.service"]; - # See https://github.com/nix-community/impermanence/issues/250#issuecomment-2603848867 - after = ["lvm2-activation.service" "local-fs-pre.target"]; - before = ["sysroot.mount"]; - # Run on cold boot only, never on resume from hibernation - unitConfig = { - ConditionKernelCommandLine = ["!resume="]; - RequiresMountsFor = ["/dev/mapper/nixos--vg-root"]; - }; - serviceConfig = { - StandardOutput = "journal+console"; - StandardError = "journal+console"; - Type = "oneshot"; - }; - script = rootExplosion; - }; - }; - }; - - boot.tmp.cleanOnBoot = true; - - fileSystems."/persist".neededForBoot = true; - - environment.persistence."/persist/system" = { - enable = true; - hideMounts = true; - directories = [ - "/root/.cache/nix" - "/var/lib/logrotate" # See https://github.com/nix-community/impermanence/issues/270 - "/var/lib/nixos" - "/var/lib/systemd/coredump" - "/var/lib/systemd/timers" - "/var/lib/udisks2" - "/var/log" - ]; - files = [ - "/etc/machine-id" - ]; - }; - # Workaround for /etc/ file timings not working with impermanence - environment.etc = { - # Timezone data linked by tzupdate - "localtime".source = "/persist/system/etc/localtime"; - }; - - # Woraround for logrotate, see https://github.com/nix-community/impermanence/issues/270 - services.logrotate.extraArgs = lib.mkAfter ["--state" "/var/lib/logrotate/logrotate.status"]; - - # home-manager's impermanence module doesn't have permissions to bootstrap these dirs, so we do it here: - system.activationScripts.bootstrapPersistHome.text = '' - mkdir -p /persist/home/farlion - chown farlion:users /persist/home/farlion - chmod 0700 /persist/home/farlion - ''; - - programs.fuse.userAllowOther = true; # Needed for home-manager's impermanence allowOther option to work -} diff --git a/system/io/default.nix b/system/io/default.nix deleted file mode 100644 index cb5f57f5..00000000 --- a/system/io/default.nix +++ /dev/null @@ -1,45 +0,0 @@ -{pkgs, ...}: { - hardware.logitech.wireless = { - enable = true; - enableGraphical = true; - }; - - # Keyd remappenings - environment.systemPackages = with pkgs; [ - keyd - ]; - services.keyd = { - enable = true; - keyboards.default = { - ids = ["*"]; - settings = { - main = { - # Tap = Esc, hold = enter the fkeys layer - capslock = "overload(fkeys, esc)"; - }; - fkeys = { - "1" = "f1"; - "2" = "f2"; - "3" = "f3"; - "4" = "f4"; - "5" = "f5"; - "6" = "f6"; - "7" = "f7"; - "8" = "f8"; - "9" = "f9"; - "0" = "f10"; - minus = "f11"; - equal = "f12"; - }; - }; - }; - }; - - # Improves palm-rejection with the keyd virtual keyboard - environment.etc."libinput/local-overrides.quirks".text = '' - [Serial Keyboards] - MatchUdevType=keyboard - MatchName=keyd*keyboard - AttrKeyboardIntegration=internal - ''; -} diff --git a/system/kernel/default.nix b/system/kernel/default.nix deleted file mode 100644 index 142453d1..00000000 --- a/system/kernel/default.nix +++ /dev/null @@ -1,22 +0,0 @@ -{pkgs, ...}: { - # Writes to /etc/sysctl.d/60-nixos.conf - boot.kernel.sysctl = { - # Enable all magic sysrq commands (NixOS sets this to 16, which enables sync only) - "kernel.sysrq" = 1; - "vm.swappiness" = 20; # balanced setting favoring RAM usage, Default=60 - }; - - boot.kernelPackages = pkgs.linuxPackages_zen; # Optimized for desktop use - environment.systemPackages = with pkgs; [ - perf - linuxKernel.packages.linux_zen.cpupower - ]; - - boot.initrd.verbose = true; - # Keep console visible during teardown - boot.kernelParams = [ - "systemd.show_status=1" - "i915.enable_psr=0" # Intel PSR often blanks the console on transitions - "i915.fastboot=0" # avoid early/quiet KMS handover - ]; -} diff --git a/system/kind-killer/default.nix b/system/kind-killer/default.nix deleted file mode 100644 index 42ad9f64..00000000 --- a/system/kind-killer/default.nix +++ /dev/null @@ -1,13 +0,0 @@ -{pkgs, ...}: { - systemd.services.kind-killer = { - description = "Kill kind cluster on shutdown"; - after = ["docker.service"]; # Ensures docker is still running when trying to delete the cluster, since systemd reverses the ordering during shutdown - requires = ["docker.service"]; - wantedBy = ["multi-user.target"]; - serviceConfig = { - Environment = "PATH=$PATH:/run/current-system/sw/bin"; - ExecStart = "${pkgs.coreutils}/bin/sleep infinity"; - ExecStop = "${pkgs.kind}/bin/kind delete cluster"; - }; - }; -} diff --git a/system/localsend/default.nix b/system/localsend/default.nix deleted file mode 100644 index 33839316..00000000 --- a/system/localsend/default.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ - config, - lib, - ... -}: { - home-manager.users.farlion.home.persistence."/persist" = lib.mkIf config.home-manager.extraSpecialArgs.isImpermanent { - directories = [ - ".local/share/org.localsend.localsend_app" - ]; - }; - - programs.localsend = { - enable = true; - }; -} diff --git a/system/networking/default.nix b/system/networking/default.nix deleted file mode 100644 index 67f914b1..00000000 --- a/system/networking/default.nix +++ /dev/null @@ -1,91 +0,0 @@ -{ - config, - lib, - isImpermanent, - isLaptop, - pkgs, - ... -}: let - # Get the current tailscale ip if tailscale is up - tailscale-ip = pkgs.writers.writeBashBin "tailscale-ip" '' - set -euo pipefail - - isOnline=$(tailscale status --json | jq -r '.Self.Online') - if [[ "$isOnline" == "true" ]]; then - tailscaleIp=$(tailscale status --json | jq -r '.Self.TailscaleIPs[0]') - echo "{\"icon\": \"tailscale_up\", \"text\": \"$tailscaleIp\", \"state\": \"Good\"}" - else - echo "{\"icon\": \"tailscale_down\", \"text\": \"\", \"state\": \"Idle\"}" - fi - ''; -in { - environment.persistence."/persist/system" = lib.mkIf isImpermanent { - directories = [ - "/etc/NetworkManager/system-connections" - "/var/lib/tailscale" - "/var/lib/NetworkManager" - ]; - }; - home-manager.users.farlion.home.persistence."/persist" = lib.mkIf config.home-manager.extraSpecialArgs.isImpermanent { - directories = [ - ".config/tailscale" # Tailscale known hosts - ]; - }; - - environment.systemPackages = [ - pkgs.pwru # eBPF-based linux kernel networking debugger - tailscale-ip # Get the current tailscale IP if tailscale is up - ]; - - networking.firewall = { - # if packets are dropped, they will show up in dmesg - logReversePathDrops = true; - logRefusedPackets = true; - # logRefusedUnicastsOnly = false; - }; - - # Tailscale - services.tailscale.enable = true; - services.tailscale.package = pkgs.unstable.tailscale; - services.tailscale.useRoutingFeatures = "client"; - - # Allow for dynamic hosts file override (by root) - environment.etc.hosts.mode = "0644"; - - networking.firewall.allowedTCPPorts = [ - 22000 # Syncthing TCP - ]; - - networking.firewall.allowedUDPPorts = [ - 22000 # Syncthing QUIC - 21027 # Syncthing discovery broadcasts on IPv4 and multicasts on IPv6 - ]; - - # BBR -> Better performance over weak/jittery links - boot.kernel.sysctl = { - "net.core.default_qdisc" = "fq"; - "net.ipv4.tcp_congestion_control" = "bbr"; - }; - - networking.networkmanager = { - enable = true; - }; - users.users.farlion.extraGroups = ["networkmanager"]; - - # IPv6 - # TODO: Temporarily enabled to allow buggy Hoppscotch to work - #networking.enableIPv6 = false; - #boot.kernelParams = ["ipv6.disable=1"]; - - # Disabling DHCPCD in favor of NetworkManager - networking.dhcpcd.enable = false; - - # Captive Browser - programs.captive-browser = lib.mkIf isLaptop { - enable = true; - bindInterface = false; - }; - - # Only wait for a single interface to come up - systemd.network.wait-online.anyInterface = true; -} diff --git a/system/nix-ld/default.nix b/system/nix-ld/default.nix deleted file mode 100644 index 7720e1b3..00000000 --- a/system/nix-ld/default.nix +++ /dev/null @@ -1,3 +0,0 @@ -{...}: { - programs.nix-ld.enable = true; -} diff --git a/system/nvidia/default.nix b/system/nvidia/default.nix deleted file mode 100644 index 17d61608..00000000 --- a/system/nvidia/default.nix +++ /dev/null @@ -1,50 +0,0 @@ -# See: https://nixos.wiki/wiki/Nvidia -{pkgs, ...}: { - boot.blacklistedKernelModules = ["nouveau"]; - environment.systemPackages = [ - pkgs.nvtopPackages.full # nvtop - pkgs.mesa-demos - pkgs.vulkan-tools - pkgs.libva-utils - pkgs.nvidia-vaapi-driver # VA-API implementation using NVIDIA's NVDEC - ]; - - # Enable VAAPI for NVIDIA - environment.sessionVariables = { - LIBVA_DRIVER_NAME = "nvidia"; - NVD_BACKEND = "direct"; # Use direct backend for better performance - }; - - services.xserver.videoDrivers = ["nvidia"]; - hardware.graphics = { - enable = true; - }; - - hardware.nvidia = { - # Modesetting is required. - modesetting.enable = true; - - # Nvidia power management. Experimental, and can cause sleep/suspend to fail. - # Enable this if you have graphical corruption issues or application crashes after waking - # up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead - # of just the bare essentials. - powerManagement.enable = false; - - # Fine-grained power management. Turns off GPU when not in use. - # Experimental and only works on modern Nvidia GPUs (Turing or newer). - powerManagement.finegrained = true; - - # Use the NVidia open source kernel module (not to be confused with the - # independent third-party "nouveau" open source driver). - # Support is limited to the Turing and later architectures. Full list of - # supported GPUs is at: - # https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus - # Only available from driver 515.43.04+ - # Currently alpha-quality/buggy, so false is currently the recommended setting. - open = false; - - # Enable the Nvidia settings menu, - # accessible via `nvidia-settings`. - nvidiaSettings = true; - }; -} diff --git a/system/performance/default.nix b/system/performance/default.nix deleted file mode 100644 index 74a79b54..00000000 --- a/system/performance/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -{...}: { - documentation.man = { - enable = true; - generateCaches = false; # Used for apropos and the -k option of man, but significantly slows down builds - }; -} diff --git a/system/power/default.nix b/system/power/default.nix deleted file mode 100644 index 28d972fb..00000000 --- a/system/power/default.nix +++ /dev/null @@ -1,52 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let - isFlexbox = config.networking.hostName == "flexbox"; -in { - # This will save you money and possibly your life! - services.thermald.enable = true; - - services.logind = { - settings = { - Login = { - HandleLidSwitch = "suspend-then-hibernate"; - HandleLidSwitchDocked = "lock"; - HandleLidSwitchExternalPower = "lock"; - }; - }; - }; - systemd.sleep.extraConfig = '' - HibernateDelaySec=1h - ''; - - environment.systemPackages = - [] - ++ lib.lists.optional isFlexbox pkgs.libsmbios; # Dell-specific power management - - services.auto-cpufreq = { - enable = true; - settings = {}; - }; - - security.sudo.extraRules = [ - { - users = ["farlion"]; - commands = [ - { - command = "${pkgs.auto-cpufreq}/bin/auto-cpufreq --force *"; - options = ["NOPASSWD" "SETENV"]; - } - { - command = "/run/current-system/sw/bin/auto-cpufreq --force *"; - options = ["NOPASSWD" "SETENV"]; - } - ]; - } - ]; - - # Dbus Service provding historical battery stats, access to external device batteries... etc. - services.upower.enable = true; -} diff --git a/system/printing/default.nix b/system/printing/default.nix deleted file mode 100644 index 47585ce3..00000000 --- a/system/printing/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{pkgs, ...}: { - services.printing = { - enable = true; - drivers = [ - pkgs.gutenprint - pkgs.hplip - ]; - listenAddresses = ["127.0.0.1:631"]; - }; - - services.avahi = { - enable = true; - nssmdns4 = true; - openFirewall = true; - }; -} diff --git a/system/scripts/nh-eval-profile.nix b/system/scripts/nh-eval-profile.nix deleted file mode 100644 index ada7d594..00000000 --- a/system/scripts/nh-eval-profile.nix +++ /dev/null @@ -1,68 +0,0 @@ -{pkgs}: -pkgs.writeShellApplication { - name = "nh-eval-profile"; - runtimeInputs = with pkgs; [ - perf - flamegraph - gnugrep - coreutils - gawk - findutils - util-linux - git - which - gzip - ]; - text = '' - set -euo pipefail - - if [ $# -lt 1 ]; then - echo "Usage: nh-eval-profile [extra nh args]" >&2 - exit 1 - fi - - host="$1" - shift || true - - outdir="result-profile" - mkdir -p "$outdir" - - # nh os switch syntax: nh os switch [FLAGS] [INSTALLABLE] [-- EXTRA_ARGS] - # We use --dry (-n) to avoid building/activating, only evaluate - cmd=(nh os switch --dry ".#nixosConfigurations.$host") - - if [ "$#" -gt 0 ]; then - cmd+=(--) - cmd+=("$@") - fi - - perfdata="$outdir/perf.data" - echo "Profiling evaluation with perf..." - echo "Running: ''${cmd[*]}" - sleep 1 - if ! perf record -F 997 -g -o "$perfdata" -- "''${cmd[@]}"; then - echo "perf record failed; you may need to run: sudo sysctl kernel.perf_event_paranoid=1" >&2 - exit 1 - fi - sleep 1 - - echo "Generating folded stacks..." - folded="$outdir/stacks.folded" - if ! perf script -i "$perfdata" 2>/dev/null | stackcollapse-perf.pl > "$folded"; then - echo "Failed to collapse stacks." >&2 - exit 1 - fi - - echo "Rendering flamegraph SVG..." - svg="$outdir/nh-eval-flamegraph.svg" - if ! flamegraph.pl --countname=samples --title "nh os switch eval flamegraph ($host)" "$folded" > "$svg"; then - echo "Failed to render flamegraph." >&2 - exit 1 - fi - - echo "" - echo "Flamegraph written to: $svg" - echo "Perf data: $perfdata" - echo "You can open the SVG in your browser to inspect hotspots." - ''; -} diff --git a/system/scrutiny/default.nix b/system/scrutiny/default.nix deleted file mode 100644 index f0b4861b..00000000 --- a/system/scrutiny/default.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ - lib, - isImpermanent, - ... -}: { - environment.persistence."/persist/system" = lib.mkIf isImpermanent { - directories = [ - { - directory = "/var/lib/private"; - mode = "0700"; - } - "/var/lib/private/scrutiny" - ]; - }; - - services.scrutiny = { - enable = true; - settings.web.listen.port = 8081; - }; - - systemd.services.scrutiny.enableStrictShellChecks = false; - - systemd.tmpfiles.rules = [ - "d /var/lib/private 0700 root root -" - ]; -} diff --git a/system/security/default.nix b/system/security/default.nix deleted file mode 100644 index ff52d823..00000000 --- a/system/security/default.nix +++ /dev/null @@ -1,65 +0,0 @@ -{ - config, - lib, - isImpermanent, - pkgs, - ... -}: { - environment.persistence."/persist/system" = lib.mkIf isImpermanent { - directories = [ - "/var/lib/boltd" # Boltd state - "/run/sudo" # Sudo timestamp (to not show the lecture message) - ]; - }; - home-manager.users.farlion.home.persistence."/persist" = lib.mkIf config.home-manager.extraSpecialArgs.isImpermanent { - directories = [ - ".local/share/keyrings" # Gnome Keyrings - ".gnupg" # PGP keys - ]; - }; - - # Thunderbolt security daemon - services.hardware.bolt.enable = true; - - services.gnome.gnome-keyring.enable = true; - environment.systemPackages = with pkgs; [ - libsecret # Already in home packages but ensuring it's available system-wide - ]; - - programs.gnupg.agent = { - enable = true; - enableSSHSupport = false; # Let GNOME Keyring handle SSH agent - pinentryPackage = pkgs.pinentry-gnome3; - }; - - programs.seahorse.enable = true; - - # Writes to /etc/sudoers - security.sudo.extraConfig = '' - Defaults:root,%wheel timestamp_timeout=30 - ''; - users.users.farlion.extraGroups = ["wheel"]; - - # Yubikeys - services.pcscd.enable = true; # Smartcard services for Yubikeys - # Sudo via U2F (Yubikey) - security.pam = { - u2f = { - enable = true; - control = "sufficient"; - settings = { - origin = "pam://farlion-realm"; # Overridde host-dependent realm to share yubikey - appid = "pam://farlion-realm"; # keep equal to origin for compatibility - cue = false; # CLI message to show touch is needed, not needed since using system-wide notification - }; - }; - services = { - login.u2fAuth = false; - ly.u2fAuth = false; - sudo.u2fAuth = true; - swaylock.u2fAuth = true; - }; - }; - # Enable system-wide Yubikey Support - services.udev.packages = [pkgs.yubikey-personalization]; -} diff --git a/system/smb/default.nix b/system/smb/default.nix deleted file mode 100644 index 0468f0cd..00000000 --- a/system/smb/default.nix +++ /dev/null @@ -1,4 +0,0 @@ -{pkgs, ...}: { - boot.supportedFilesystems = ["cifs"]; - environment.systemPackages = [pkgs.cifs-utils]; -} diff --git a/system/stylix/default.nix b/system/stylix/default.nix deleted file mode 100644 index bd795114..00000000 --- a/system/stylix/default.nix +++ /dev/null @@ -1,37 +0,0 @@ -{pkgs, ...}: { - stylix = { - enable = true; - base16Scheme = "${pkgs.base16-schemes}/share/themes/gruvbox-dark-medium.yaml"; - cursor = { - name = "Bibata-Modern-Ice"; - package = pkgs.bibata-cursors; - size = 24; - }; - fonts = { - emoji = { - package = pkgs.noto-fonts-color-emoji; - name = "Noto Color Emoji"; - }; - monospace = { - package = pkgs.fira-code; - name = "Fira Code"; - }; - serif = { - package = pkgs.dejavu_fonts; - name = "DejaVu Serif"; - }; - sansSerif = { - package = pkgs.dejavu_fonts; - name = "DejaVu Sans"; - }; - sizes = { - applications = 9; - desktop = 9; - terminal = 8; - popups = 9; - }; - }; - image = ./gruvbox-dark-rainbow.png; - polarity = "dark"; - }; -} diff --git a/system/syncthing/default.nix b/system/syncthing/default.nix deleted file mode 100644 index 86259875..00000000 --- a/system/syncthing/default.nix +++ /dev/null @@ -1,11 +0,0 @@ -{pkgs, ...}: { - # Prevent syncthing from preventing sleep - powerManagement.powerDownCommands = '' - export XDG_RUNTIME_DIR=/run/user/1000 - ${pkgs.systemd}/bin/machinectl shell farlion@ /run/current-system/sw/bin/systemctl --user stop syncthing.service - ''; - powerManagement.resumeCommands = '' - export XDG_RUNTIME_DIR=/run/user/1000 - ${pkgs.systemd}/bin/machinectl shell farlion@ /run/current-system/sw/bin/systemctl --user start syncthing.service - ''; -} diff --git a/system/systemd/default.nix b/system/systemd/default.nix deleted file mode 100644 index 26907e05..00000000 --- a/system/systemd/default.nix +++ /dev/null @@ -1,3 +0,0 @@ -{pkgs, ...}: { - systemd.enableStrictShellChecks = true; -} diff --git a/system/users/default.nix b/system/users/default.nix deleted file mode 100644 index 58e61eae..00000000 --- a/system/users/default.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ - lib, - pkgs, - ... -}: { - users.mutableUsers = false; - - users = { - users.farlion = { - description = "Florian Peter"; - extraGroups = ["disk"]; - group = "users"; - hashedPassword = lib.mkDefault ""; # For CI, otherwise gets overwritten by parent `secrets` flake - isNormalUser = true; - shell = pkgs.fish; - }; - }; - - # Default editor for root - programs.vim = { - defaultEditor = true; - enable = true; - }; - # Enable fish for root - programs.fish.enable = true; -} diff --git a/system/various/default.nix b/system/various/default.nix deleted file mode 100644 index d052eb0e..00000000 --- a/system/various/default.nix +++ /dev/null @@ -1,25 +0,0 @@ -{pkgs, ...}: { - environment.systemPackages = with pkgs; [comma]; - - services.atd.enable = true; - - boot.supportedFilesystems = ["ntfs"]; - - services.tzupdate = { - enable = true; # Oneshot systemd service, run with `sudo systemctl start tzupdate` - timer.enable = false; # Disable automatic TZ updates - }; - # Fix tzupdate networking dependency issue - systemd.services.tzupdate = { - after = ["network-online.target"]; - wants = ["network-online.target"]; - serviceConfig = { - # Add retry logic in case network is still not fully ready - Restart = "on-failure"; - RestartSec = "30s"; - RestartMode = "direct"; - }; - }; - - i18n.defaultLocale = "en_US.UTF-8"; -} diff --git a/system/video/default.nix b/system/video/default.nix deleted file mode 100644 index 2de8d367..00000000 --- a/system/video/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -{pkgs, ...}: { - programs.obs-studio = { - enable = true; - enableVirtualCamera = true; - }; - - environment.systemPackages = [ - pkgs.v4l-utils # Video4Linux2 -> configuring webcam - ]; - - users.users.farlion.extraGroups = ["video"]; -} diff --git a/system/virtualisation/default.nix b/system/virtualisation/default.nix deleted file mode 100644 index 9d0a940c..00000000 --- a/system/virtualisation/default.nix +++ /dev/null @@ -1,102 +0,0 @@ -{ - config, - isImpermanent, - lib, - pkgs, - ... -}: { - environment.persistence."/persist/system" = lib.mkIf isImpermanent { - directories = [ - "/var/lib/containers" # Podman - "/var/lib/docker" - "/var/lib/libvirt" # Virt-Manager - ]; - }; - home-manager.users.farlion.home.persistence."/persist" = lib.mkIf config.home-manager.extraSpecialArgs.isImpermanent { - directories = [ - ".local/share/containers" # Podman (userspace) - ]; - }; - - environment.systemPackages = with pkgs; [ - podman-compose # docker-compose rewritten with podman backend - virt-manager # Desktop user interface for managing virtual machines - (pkgs.callPackage ./scripts/benchmark-containers.nix {}) - (pkgs.callPackage ./scripts/benchmark-heavy-containers.nix {}) - (pkgs.callPackage ./scripts/reset-container-state.nix {}) - ]; - virtualisation.docker = { - enable = true; - # Explicitly use overlay2 for best performance and stability - storageDriver = "overlay2"; - daemon.settings = { - # Attach to resolved instead of using default Docker DNS servers - dns = ["172.17.0.1"]; - # Have containers listen on localhost instead of 0.0.0.0, - # see https://github.com/NixOS/nixpkgs/issues/111852#issuecomment-1954656069 - ip = "127.0.0.1"; - ipv6 = false; - ip6tables = false; - iptables = true; - }; - }; - # Allow connecting to resolved DNS from inside Docker containers - networking.firewall.interfaces.docker0.allowedTCPPorts = [53]; - networking.firewall.interfaces.docker0.allowedUDPPorts = [53]; - - # Allow DNS on all Docker bridge interfaces (br-*) - networking.firewall.extraCommands = '' - # Allow DNS (port 53) on all Docker bridge interfaces - for iface in $(${pkgs.iproute2}/bin/ip link show | grep -o 'br-[a-f0-9]\{12\}' || true); do - if [ -n "$iface" ]; then - # Insert before default REJECT so these rules are effective - iptables -I nixos-fw 1 -i "$iface" -p tcp --dport 53 -j ACCEPT - iptables -I nixos-fw 1 -i "$iface" -p udp --dport 53 -j ACCEPT - fi - done - - # Allow cross-network DNS resolution between Docker networks - # This allows containers on any Docker network to reach DNS servers on other Docker networks - iptables -I FORWARD 1 -s 172.16.0.0/12 -d 172.16.0.0/12 -p tcp --dport 53 -j ACCEPT - iptables -I FORWARD 1 -s 172.16.0.0/12 -d 172.16.0.0/12 -p udp --dport 53 -j ACCEPT - - # Fix MTU issues for Docker -> Tailscale traffic - # Clamp MSS to account for Tailscale's MTU of 1280 - # Without this, HTTPS connections from Docker to Tailscale frequently fail. - iptables -t mangle -A FORWARD -s 172.17.0.0/16 -d 100.64.0.0/10 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu - iptables -t mangle -A FORWARD -s 172.18.0.0/16 -d 100.64.0.0/10 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu - iptables -t mangle -A FORWARD -s 172.17.0.0/16 -d 100.100.0.0/16 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu - iptables -t mangle -A FORWARD -s 172.18.0.0/16 -d 100.100.0.0/16 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu - ''; - - virtualisation.libvirtd.enable = true; - - virtualisation.podman = { - enable = true; - }; - - # Configure Podman to use overlay with optimizations - virtualisation.containers.storage.settings = { - storage = { - driver = "overlay"; - runroot = "/run/containers/storage"; - graphroot = "/var/lib/containers/storage"; - options = { - # Pull images in parallel for better performance - pull_options = { - enable_partial_images = "true"; - use_hard_links = "false"; - ostree_repos = ""; - }; - overlay = { - # Use native overlay with metacopy for better performance - # metacopy=on allows fast copy-up of large files - mountopt = "nodev,metacopy=on"; - force_mask = "0000"; - }; - }; - }; - }; - - users.users.farlion.extraGroups = ["libvirtd" "kvm" "docker"]; -} diff --git a/system/wireshark/default.nix b/system/wireshark/default.nix deleted file mode 100644 index f3841246..00000000 --- a/system/wireshark/default.nix +++ /dev/null @@ -1,7 +0,0 @@ -{pkgs, ...}: { - environment.systemPackages = [pkgs.wireshark]; - programs.wireshark = { - enable = true; - }; - users.users.farlion.extraGroups = ["wireshark"]; -}