mirror of
https://github.com/nmasur/dotfiles
synced 2025-07-05 12:40:13 +00:00
split nixos from darwin
required because they don't share all attributes
This commit is contained in:
@ -1,19 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options = {
|
||||
calibre = {
|
||||
enable = lib.mkEnableOption {
|
||||
description = "Enable Calibre.";
|
||||
default = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.gui.enable && config.calibre.enable) {
|
||||
home-manager.users.${config.user} = {
|
||||
home.packages = with pkgs; [ calibre ];
|
||||
# home.sessionVariables = { CALIBRE_USE_DARK_PALETTE = 1; };
|
||||
};
|
||||
environment.sessionVariables = { CALIBRE_USE_DARK_PALETTE = "1"; };
|
||||
};
|
||||
}
|
@ -3,7 +3,6 @@
|
||||
imports = [
|
||||
./1password.nix
|
||||
./alacritty.nix
|
||||
./calibre.nix
|
||||
./discord.nix
|
||||
./firefox.nix
|
||||
./kitty.nix
|
||||
|
@ -1,651 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
# MacOS-specific settings for Alacritty
|
||||
home-manager.users.${config.user} = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
programs.alacritty.settings = {
|
||||
font.size = lib.mkForce 20.0;
|
||||
shell.program = "${pkgs.fish}/bin/fish";
|
||||
window.startup_mode = "SimpleFullScreen";
|
||||
key_bindings = [
|
||||
{
|
||||
key = "F";
|
||||
mods = "Super";
|
||||
action = "ToggleSimpleFullscreen";
|
||||
}
|
||||
{
|
||||
key = "F";
|
||||
mods = "Control|Shift";
|
||||
action = "SearchForward";
|
||||
}
|
||||
{
|
||||
key = "L";
|
||||
mods = "Super";
|
||||
chars = "\\x1F";
|
||||
}
|
||||
# Some Mac key combinations need to be manually added
|
||||
{
|
||||
key = "Return";
|
||||
mods = "Shift";
|
||||
chars = "\\x1b[13;2u";
|
||||
}
|
||||
{
|
||||
key = "Return";
|
||||
mods = "Control";
|
||||
chars = "\\x1b[13;5u";
|
||||
}
|
||||
{
|
||||
key = "Return";
|
||||
mods = "Control|Shift";
|
||||
chars = "\\x1b[13;6u";
|
||||
}
|
||||
|
||||
{
|
||||
key = "N";
|
||||
mods = "Command";
|
||||
action = "SpawnNewInstance";
|
||||
}
|
||||
{
|
||||
key = "PageUp";
|
||||
action = "ScrollPageUp";
|
||||
}
|
||||
{
|
||||
key = "PageUp";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B[5~";
|
||||
}
|
||||
{
|
||||
key = "PageUp";
|
||||
mods = "Shift";
|
||||
chars = "\\x1B[5~";
|
||||
}
|
||||
{
|
||||
key = "PageDown";
|
||||
action = "ScrollPageDown";
|
||||
}
|
||||
{
|
||||
key = "PageDown";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B[6~";
|
||||
}
|
||||
{
|
||||
key = "PageDown";
|
||||
mods = "Shift";
|
||||
chars = "\\x1B[6~";
|
||||
}
|
||||
{
|
||||
key = "Home";
|
||||
action = "ScrollToTop";
|
||||
}
|
||||
{
|
||||
key = "Home";
|
||||
mods = "Alt";
|
||||
chars = "\\x1BOH";
|
||||
}
|
||||
{
|
||||
key = "Home";
|
||||
mods = "Shift";
|
||||
chars = "\\x1B[H";
|
||||
}
|
||||
{
|
||||
key = "End";
|
||||
action = "ScrollToBottom";
|
||||
}
|
||||
{
|
||||
key = "End";
|
||||
mods = "Alt";
|
||||
chars = "\\x1BOF";
|
||||
}
|
||||
{
|
||||
key = "End";
|
||||
mods = "Shift";
|
||||
chars = "\\x1B[F";
|
||||
}
|
||||
{
|
||||
key = "Tab";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B ";
|
||||
}
|
||||
{
|
||||
key = "Tab";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1Bx1B[Z";
|
||||
}
|
||||
# {
|
||||
# key = "Back";
|
||||
# chars = "\\x7F";
|
||||
# }
|
||||
{
|
||||
key = "Back";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bx7F";
|
||||
}
|
||||
{
|
||||
key = "Back";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1Bx08";
|
||||
}
|
||||
{
|
||||
key = "Space";
|
||||
mods = "Control";
|
||||
chars = "\\x00";
|
||||
}
|
||||
{
|
||||
key = "Space";
|
||||
mods = "Alt";
|
||||
chars = "\\x20";
|
||||
}
|
||||
{
|
||||
key = "Left";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bb";
|
||||
}
|
||||
{
|
||||
key = "Left";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1Bx1B[D";
|
||||
}
|
||||
{
|
||||
key = "Right";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bf";
|
||||
}
|
||||
{
|
||||
key = "Right";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1Bx1B[C";
|
||||
}
|
||||
{
|
||||
key = "Down";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bx1B[B";
|
||||
}
|
||||
{
|
||||
key = "Down";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1Bx1B[B";
|
||||
}
|
||||
{
|
||||
key = "Up";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bx1B[A";
|
||||
}
|
||||
{
|
||||
key = "Up";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1Bx1B[A";
|
||||
}
|
||||
{
|
||||
key = "A";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Ba";
|
||||
}
|
||||
{
|
||||
key = "A";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BA";
|
||||
}
|
||||
{
|
||||
key = "B";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bb";
|
||||
}
|
||||
{
|
||||
key = "B";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BB";
|
||||
}
|
||||
{
|
||||
key = "C";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bc";
|
||||
}
|
||||
{
|
||||
key = "C";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BC";
|
||||
}
|
||||
{
|
||||
key = "D";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bd";
|
||||
}
|
||||
{
|
||||
key = "D";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BD";
|
||||
}
|
||||
{
|
||||
key = "E";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Be";
|
||||
}
|
||||
{
|
||||
key = "E";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BE";
|
||||
}
|
||||
{
|
||||
key = "F";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bf";
|
||||
}
|
||||
{
|
||||
key = "F";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BF";
|
||||
}
|
||||
{
|
||||
key = "G";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bg";
|
||||
}
|
||||
{
|
||||
key = "G";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BG";
|
||||
}
|
||||
{
|
||||
key = "H";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bh";
|
||||
}
|
||||
{
|
||||
key = "H";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BH";
|
||||
}
|
||||
{
|
||||
key = "I";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bi";
|
||||
}
|
||||
{
|
||||
key = "I";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BI";
|
||||
}
|
||||
{
|
||||
key = "J";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bj";
|
||||
}
|
||||
{
|
||||
key = "J";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BJ";
|
||||
}
|
||||
{
|
||||
key = "K";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bk";
|
||||
}
|
||||
{
|
||||
key = "K";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BK";
|
||||
}
|
||||
{
|
||||
key = "L";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bl";
|
||||
}
|
||||
{
|
||||
key = "L";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BL";
|
||||
}
|
||||
{
|
||||
key = "M";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bm";
|
||||
}
|
||||
{
|
||||
key = "M";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BM";
|
||||
}
|
||||
{
|
||||
key = "N";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bn";
|
||||
}
|
||||
{
|
||||
key = "N";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BN";
|
||||
}
|
||||
{
|
||||
key = "O";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bo";
|
||||
}
|
||||
{
|
||||
key = "O";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BO";
|
||||
}
|
||||
{
|
||||
key = "P";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bp";
|
||||
}
|
||||
{
|
||||
key = "P";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BP";
|
||||
}
|
||||
{
|
||||
key = "Q";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bq";
|
||||
}
|
||||
{
|
||||
key = "Q";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BQ";
|
||||
}
|
||||
{
|
||||
key = "R";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Br";
|
||||
}
|
||||
{
|
||||
key = "R";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BR";
|
||||
}
|
||||
{
|
||||
key = "S";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bs";
|
||||
}
|
||||
{
|
||||
key = "S";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BS";
|
||||
}
|
||||
{
|
||||
key = "T";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bt";
|
||||
}
|
||||
{
|
||||
key = "T";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BT";
|
||||
}
|
||||
{
|
||||
key = "U";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bu";
|
||||
}
|
||||
{
|
||||
key = "U";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BU";
|
||||
}
|
||||
{
|
||||
key = "V";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bv";
|
||||
}
|
||||
{
|
||||
key = "V";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BV";
|
||||
}
|
||||
{
|
||||
key = "W";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bw";
|
||||
}
|
||||
{
|
||||
key = "W";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BW";
|
||||
}
|
||||
{
|
||||
key = "X";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bx";
|
||||
}
|
||||
{
|
||||
key = "X";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BX";
|
||||
}
|
||||
{
|
||||
key = "Y";
|
||||
mods = "Alt";
|
||||
chars = "\\x1By";
|
||||
}
|
||||
{
|
||||
key = "Y";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BY";
|
||||
}
|
||||
{
|
||||
key = "Z";
|
||||
mods = "Alt";
|
||||
chars = "\\x1Bz";
|
||||
}
|
||||
{
|
||||
key = "Z";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1BZ";
|
||||
}
|
||||
{
|
||||
key = "Key1";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B1";
|
||||
}
|
||||
{
|
||||
key = "Key1";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B!";
|
||||
}
|
||||
{
|
||||
key = "Key2";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B2";
|
||||
}
|
||||
{
|
||||
key = "Key2";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B#";
|
||||
}
|
||||
{
|
||||
key = "Key3";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B3";
|
||||
}
|
||||
{
|
||||
key = "Key3";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B#";
|
||||
}
|
||||
{
|
||||
key = "Key4";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B4";
|
||||
}
|
||||
{
|
||||
key = "Key4";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B$";
|
||||
}
|
||||
{
|
||||
key = "Key5";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B5";
|
||||
}
|
||||
{
|
||||
key = "Key5";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B%";
|
||||
}
|
||||
{
|
||||
key = "Key6";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B6";
|
||||
}
|
||||
{
|
||||
key = "Key6";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B^";
|
||||
}
|
||||
{
|
||||
key = "Key7";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B7";
|
||||
}
|
||||
{
|
||||
key = "Key7";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B&";
|
||||
}
|
||||
{
|
||||
key = "Key8";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B8";
|
||||
}
|
||||
{
|
||||
key = "Key8";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B*";
|
||||
}
|
||||
{
|
||||
key = "Key9";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B9";
|
||||
}
|
||||
{
|
||||
key = "Key9";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B(";
|
||||
}
|
||||
{
|
||||
key = "Key0";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B0";
|
||||
}
|
||||
{
|
||||
key = "Key0";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B)";
|
||||
}
|
||||
{
|
||||
key = "Minus";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B-";
|
||||
}
|
||||
{
|
||||
key = "Minus";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B_";
|
||||
}
|
||||
{
|
||||
key = "Equals";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B=";
|
||||
}
|
||||
{
|
||||
key = "Equals";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B+";
|
||||
}
|
||||
{
|
||||
key = "LBracket";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B[";
|
||||
}
|
||||
{
|
||||
key = "LBracket";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B{";
|
||||
}
|
||||
{
|
||||
key = "RBracket";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B]";
|
||||
}
|
||||
{
|
||||
key = "RBracket";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B}";
|
||||
}
|
||||
{
|
||||
key = "Backslash";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B\\\\";
|
||||
}
|
||||
{
|
||||
key = "Backslash";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B|";
|
||||
}
|
||||
{
|
||||
key = "Semicolon";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B;";
|
||||
}
|
||||
{
|
||||
key = "Semicolon";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B =";
|
||||
}
|
||||
{
|
||||
key = "Apostrophe";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B'";
|
||||
}
|
||||
{
|
||||
key = "Apostrophe";
|
||||
mods = "Alt|Shift";
|
||||
chars = ''\x1B"'';
|
||||
}
|
||||
{
|
||||
key = "Comma";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B;";
|
||||
}
|
||||
{
|
||||
key = "Comma";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B<";
|
||||
}
|
||||
{
|
||||
key = "Period";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B.";
|
||||
}
|
||||
{
|
||||
key = "Period";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B>";
|
||||
}
|
||||
{
|
||||
key = "Slash";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B/";
|
||||
}
|
||||
{
|
||||
key = "Slash";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B?";
|
||||
}
|
||||
{
|
||||
key = "Grave";
|
||||
mods = "Alt";
|
||||
chars = "\\x1B`";
|
||||
}
|
||||
{
|
||||
key = "Grave";
|
||||
mods = "Alt|Shift";
|
||||
chars = "\\x1B~";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{ config, ... }: {
|
||||
|
||||
imports = [
|
||||
./alacritty.nix
|
||||
./fonts.nix
|
||||
./hammerspoon.nix
|
||||
./homebrew.nix
|
||||
./kitty.nix
|
||||
./networking.nix
|
||||
./nixpkgs.nix
|
||||
./system.nix
|
||||
./tmux.nix
|
||||
./user.nix
|
||||
./utilities.nix
|
||||
];
|
||||
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
home-manager.users.${config.user} = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
|
||||
home.packages = with pkgs;
|
||||
[ (nerdfonts.override { fonts = [ "VictorMono" ]; }) ];
|
||||
|
||||
programs.alacritty.settings = { font.normal.family = "VictorMono"; };
|
||||
|
||||
programs.kitty.font = {
|
||||
package = (pkgs.nerdfonts.override { fonts = [ "VictorMono" ]; });
|
||||
name = "VictorMono Nerd Font Mono";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
# Hammerspoon - MacOS custom automation scripting
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
xdg.configFile."hammerspoon/init.lua".source = ./hammerspoon/init.lua;
|
||||
xdg.configFile."hammerspoon/Spoons/ControlEscape.spoon".source =
|
||||
./hammerspoon/Spoons/ControlEscape.spoon;
|
||||
xdg.configFile."hammerspoon/Spoons/DismissAlerts.spoon".source =
|
||||
./hammerspoon/Spoons/DismissAlerts.spoon;
|
||||
xdg.configFile."hammerspoon/Spoons/Launcher.spoon/init.lua".source =
|
||||
pkgs.substituteAll {
|
||||
src = ./hammerspoon/Spoons/Launcher.spoon/init.lua;
|
||||
firefox = "${pkgs.firefox-bin}/Applications/Firefox.app";
|
||||
discord = "${pkgs.discord}/Applications/Discord.app";
|
||||
kitty = "${pkgs.kitty}/Applications/kitty.app";
|
||||
};
|
||||
xdg.configFile."hammerspoon/Spoons/MoveWindow.spoon".source =
|
||||
./hammerspoon/Spoons/MoveWindow.spoon;
|
||||
};
|
||||
|
||||
homebrew.casks = [ "hammerspoon" ];
|
||||
|
||||
system.activationScripts.postUserActivation.text = ''
|
||||
defaults write org.hammerspoon.Hammerspoon MJConfigFile "~/.config/hammerspoon/init.lua"
|
||||
'';
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1 +0,0 @@
|
||||
indent_type = "Spaces"
|
@ -1,112 +0,0 @@
|
||||
--- === ControlEscape ===
|
||||
---
|
||||
--- Adapted very loosely from https://github.com/jasonrudolph/ControlEscape.spoon
|
||||
--- Removed timing/delay; always send Escape as well as Control
|
||||
---
|
||||
--- Make the `control` key more useful: If the `control` key is tapped, treat it
|
||||
--- as the `escape` key. If the `control` key is held down and used in
|
||||
--- combination with another key, then provide the normal `control` key
|
||||
--- behavior.
|
||||
|
||||
local obj = {}
|
||||
obj.__index = obj
|
||||
|
||||
-- Metadata
|
||||
obj.name = "ControlEscape"
|
||||
obj.version = "0.1"
|
||||
obj.author = "Jason Rudolph <jason@jasonrudolph.com>"
|
||||
obj.homepage = "https://github.com/jasonrudolph/ControlEscape.spoon"
|
||||
obj.license = "MIT - https://opensource.org/licenses/MIT"
|
||||
|
||||
function obj:init()
|
||||
self.movements = 0
|
||||
self.sendEscape = false
|
||||
self.lastModifiers = {}
|
||||
|
||||
-- Create an eventtap to run each time the modifier keys change (i.e., each
|
||||
-- time a key like control, shift, option, or command is pressed or released)
|
||||
self.controlTap = hs.eventtap.new({ hs.eventtap.event.types.flagsChanged }, function(event)
|
||||
local newModifiers = event:getFlags()
|
||||
|
||||
-- If this change to the modifier keys does not involve a *change* to the
|
||||
-- up/down state of the `control` key (i.e., it was up before and it's
|
||||
-- still up, or it was down before and it's still down), then don't take
|
||||
-- any action.
|
||||
if self.lastModifiers["ctrl"] == newModifiers["ctrl"] then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Control was not down but is now
|
||||
if not self.lastModifiers["ctrl"] then
|
||||
-- Only prepare to send escape if no other modifier keys are in use
|
||||
self.lastModifiers = newModifiers
|
||||
if not self.lastModifiers["cmd"] and not self.lastModifiers["alt"] then
|
||||
self.sendEscape = true
|
||||
self.movements = 0
|
||||
end
|
||||
|
||||
-- Control was down and is up, hasn't been blocked by another key, and
|
||||
-- isn't above the movement threshold
|
||||
elseif self.sendEscape == true and not newModifiers["ctrl"] and self.movements < 30 then
|
||||
self.lastModifiers = newModifiers
|
||||
|
||||
-- Allow for shift-escape
|
||||
if newModifiers["shift"] then
|
||||
hs.eventtap.keyStroke({ "shift" }, "escape", 0)
|
||||
else
|
||||
hs.eventtap.keyStroke(newModifiers, "escape", 0)
|
||||
end
|
||||
self.sendEscape = false
|
||||
self.movements = 0
|
||||
self.numberOfCharacters = 0
|
||||
|
||||
-- Control was down and is up, but isn't ready to send escape
|
||||
else
|
||||
self.lastModifiers = newModifiers
|
||||
end
|
||||
end)
|
||||
|
||||
-- If any other key is pressed, don't send escape
|
||||
self.asModifier = hs.eventtap.new({ hs.eventtap.event.types.keyDown }, function(_)
|
||||
self.sendEscape = false
|
||||
-- print("Don't sent escape")
|
||||
end)
|
||||
|
||||
-- If mouse is moving significantly, don't send escape
|
||||
self.scrolling = hs.eventtap.new({ hs.eventtap.event.types.gesture }, function(event)
|
||||
local touches = event:getTouches()
|
||||
local i, v = next(touches, nil)
|
||||
while i do
|
||||
if v["phase"] == "moved" then
|
||||
-- Increment the movement counter
|
||||
self.movements = self.movements + 1
|
||||
end
|
||||
i, v = next(touches, i) -- get next index
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- ControlEscape:start()
|
||||
--- Method
|
||||
--- Start sending `escape` when `control` is pressed and released in isolation
|
||||
function obj:start()
|
||||
self.controlTap:start()
|
||||
self.asModifier:start()
|
||||
self.scrolling:start()
|
||||
end
|
||||
|
||||
--- ControlEscape:stop()
|
||||
--- Method
|
||||
--- Stop sending `escape` when `control` is pressed and released in isolation
|
||||
function obj:stop()
|
||||
-- Stop monitoring keystrokes
|
||||
self.controlTap:stop()
|
||||
self.asModifier:stop()
|
||||
self.scrolling:stop()
|
||||
|
||||
-- Reset state
|
||||
self.sendEscape = false
|
||||
self.lastModifiers = {}
|
||||
end
|
||||
|
||||
return obj
|
@ -1,298 +0,0 @@
|
||||
/* Credit: https://gist.github.com/lancethomps/a5ac103f334b171f70ce2ff983220b4f */
|
||||
|
||||
function run(input, parameters) {
|
||||
|
||||
const appNames = [];
|
||||
const skipAppNames = [];
|
||||
const verbose = true;
|
||||
|
||||
const scriptName = "close_notifications_applescript";
|
||||
|
||||
const CLEAR_ALL_ACTION = "Clear All";
|
||||
const CLEAR_ALL_ACTION_TOP = "Clear";
|
||||
const CLOSE_ACTION = "Close";
|
||||
|
||||
const notNull = (val) => {
|
||||
return val !== null && val !== undefined;
|
||||
};
|
||||
|
||||
const isNull = (val) => {
|
||||
return !notNull(val);
|
||||
};
|
||||
|
||||
const notNullOrEmpty = (val) => {
|
||||
return notNull(val) && val.length > 0;
|
||||
};
|
||||
|
||||
const isNullOrEmpty = (val) => {
|
||||
return !notNullOrEmpty(val);
|
||||
};
|
||||
|
||||
const isError = (maybeErr) => {
|
||||
return notNull(maybeErr) && (maybeErr instanceof Error || maybeErr.message);
|
||||
};
|
||||
|
||||
const systemVersion = () => {
|
||||
return Application("Finder").version().split(".").map(val => parseInt(val));
|
||||
};
|
||||
|
||||
const systemVersionGreaterThanOrEqualTo = (vers) => {
|
||||
return systemVersion()[0] >= vers;
|
||||
};
|
||||
|
||||
const isBigSurOrGreater = () => {
|
||||
return systemVersionGreaterThanOrEqualTo(11);
|
||||
};
|
||||
|
||||
const V11_OR_GREATER = isBigSurOrGreater();
|
||||
const APP_NAME_MATCHER_ROLE = V11_OR_GREATER ? "AXStaticText" : "AXImage";
|
||||
const hasAppNames = notNullOrEmpty(appNames);
|
||||
const hasSkipAppNames = notNullOrEmpty(skipAppNames);
|
||||
const hasAppNameFilters = hasAppNames || hasSkipAppNames;
|
||||
const appNameForLog = hasAppNames ? ` [${appNames.join(",")}]` : "";
|
||||
|
||||
const logs = [];
|
||||
const log = (message, ...optionalParams) => {
|
||||
let message_with_prefix = `${new Date().toISOString().replace("Z", "").replace("T", " ")} [${scriptName}]${appNameForLog} ${message}`;
|
||||
console.log(message_with_prefix, optionalParams);
|
||||
logs.push(message_with_prefix);
|
||||
};
|
||||
|
||||
const logError = (message, ...optionalParams) => {
|
||||
if (isError(message)) {
|
||||
let err = message;
|
||||
message = `${err}${err.stack ? (" " + err.stack) : ""}`;
|
||||
}
|
||||
log(`ERROR ${message}`, optionalParams);
|
||||
};
|
||||
|
||||
const logErrorVerbose = (message, ...optionalParams) => {
|
||||
if (verbose) {
|
||||
logError(message, optionalParams);
|
||||
}
|
||||
};
|
||||
|
||||
const logVerbose = (message) => {
|
||||
if (verbose) {
|
||||
log(message);
|
||||
}
|
||||
};
|
||||
|
||||
const getLogLines = () => {
|
||||
return logs.join("\n");
|
||||
};
|
||||
|
||||
const getSystemEvents = () => {
|
||||
let systemEvents = Application("System Events");
|
||||
systemEvents.includeStandardAdditions = true;
|
||||
return systemEvents;
|
||||
};
|
||||
|
||||
const getNotificationCenter = () => {
|
||||
try {
|
||||
return getSystemEvents().processes.byName("NotificationCenter");
|
||||
} catch (err) {
|
||||
logError("Could not get NotificationCenter");
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const getNotificationCenterGroups = (retryOnError = false) => {
|
||||
try {
|
||||
let notificationCenter = getNotificationCenter();
|
||||
if (notificationCenter.windows.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
if (!V11_OR_GREATER) {
|
||||
return notificationCenter.windows();
|
||||
}
|
||||
return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements();
|
||||
} catch (err) {
|
||||
logError("Could not get NotificationCenter groups");
|
||||
if (retryOnError) {
|
||||
logError(err);
|
||||
log("Retrying getNotificationCenterGroups...");
|
||||
return getNotificationCenterGroups(false);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const isClearButton = (description, name) => {
|
||||
return description === "button" && name === CLEAR_ALL_ACTION_TOP;
|
||||
};
|
||||
|
||||
const matchesAnyAppNames = (value, checkValues) => {
|
||||
if (isNullOrEmpty(checkValues)) {
|
||||
return false;
|
||||
}
|
||||
let lowerAppName = value.toLowerCase();
|
||||
for (let checkValue of checkValues) {
|
||||
if (lowerAppName === checkValue.toLowerCase()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const matchesAppName = (role, value) => {
|
||||
if (role !== APP_NAME_MATCHER_ROLE) {
|
||||
return false;
|
||||
}
|
||||
if (hasAppNames) {
|
||||
return matchesAnyAppNames(value, appNames);
|
||||
}
|
||||
return !matchesAnyAppNames(value, skipAppNames);
|
||||
};
|
||||
|
||||
const notificationGroupMatches = (group) => {
|
||||
try {
|
||||
let description = group.description();
|
||||
if (V11_OR_GREATER && isClearButton(description, group.name())) {
|
||||
return true;
|
||||
}
|
||||
if (V11_OR_GREATER && description !== "group") {
|
||||
return false;
|
||||
}
|
||||
if (!V11_OR_GREATER) {
|
||||
let matchedAppName = !hasAppNameFilters;
|
||||
if (!matchedAppName) {
|
||||
for (let elem of group.uiElements()) {
|
||||
if (matchesAppName(elem.role(), elem.description())) {
|
||||
matchedAppName = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matchedAppName) {
|
||||
return notNull(findCloseActionV10(group, -1));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!hasAppNameFilters) {
|
||||
return true;
|
||||
}
|
||||
let firstElem = group.uiElements[0];
|
||||
return matchesAppName(firstElem.role(), firstElem.value());
|
||||
} catch (err) {
|
||||
logErrorVerbose(`Caught error while checking window, window is probably closed: ${err}`);
|
||||
logErrorVerbose(err);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const findCloseActionV10 = (group, closedCount) => {
|
||||
try {
|
||||
for (let elem of group.uiElements()) {
|
||||
if (elem.role() === "AXButton" && elem.title() === CLOSE_ACTION) {
|
||||
return elem.actions["AXPress"];
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logErrorVerbose(`(group_${closedCount}) Caught error while searching for close action, window is probably closed: ${err}`);
|
||||
logErrorVerbose(err);
|
||||
return null;
|
||||
}
|
||||
log("No close action found for notification");
|
||||
return null;
|
||||
};
|
||||
|
||||
const findCloseAction = (group, closedCount) => {
|
||||
try {
|
||||
if (!V11_OR_GREATER) {
|
||||
return findCloseActionV10(group, closedCount);
|
||||
}
|
||||
let checkForPress = isClearButton(group.description(), group.name());
|
||||
let clearAllAction;
|
||||
let closeAction;
|
||||
for (let action of group.actions()) {
|
||||
let description = action.description();
|
||||
if (description === CLEAR_ALL_ACTION) {
|
||||
clearAllAction = action;
|
||||
break;
|
||||
} else if (description === CLOSE_ACTION) {
|
||||
closeAction = action;
|
||||
} else if (checkForPress && description === "press") {
|
||||
clearAllAction = action;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (notNull(clearAllAction)) {
|
||||
return clearAllAction;
|
||||
} else if (notNull(closeAction)) {
|
||||
return closeAction;
|
||||
}
|
||||
} catch (err) {
|
||||
logErrorVerbose(`(group_${closedCount}) Caught error while searching for close action, window is probably closed: ${err}`);
|
||||
logErrorVerbose(err);
|
||||
return null;
|
||||
}
|
||||
log("No close action found for notification");
|
||||
return null;
|
||||
};
|
||||
|
||||
const closeNextGroup = (groups, closedCount) => {
|
||||
try {
|
||||
for (let group of groups) {
|
||||
if (notificationGroupMatches(group)) {
|
||||
let closeAction = findCloseAction(group, closedCount);
|
||||
|
||||
if (notNull(closeAction)) {
|
||||
try {
|
||||
closeAction.perform();
|
||||
return [true, 1];
|
||||
} catch (err) {
|
||||
logErrorVerbose(`(group_${closedCount}) Caught error while performing close action, window is probably closed: ${err}`);
|
||||
logErrorVerbose(err);
|
||||
}
|
||||
}
|
||||
return [true, 0];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (err) {
|
||||
logError("Could not run closeNextGroup");
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
let groupsCount = getNotificationCenterGroups(true).filter(group => notificationGroupMatches(group)).length;
|
||||
|
||||
if (groupsCount > 0) {
|
||||
logVerbose(`Closing ${groupsCount}${appNameForLog} notification group${(groupsCount > 1 ? "s" : "")}`);
|
||||
|
||||
let startTime = new Date().getTime();
|
||||
let closedCount = 0;
|
||||
let maybeMore = true;
|
||||
let maxAttempts = 2;
|
||||
let attempts = 1;
|
||||
while (maybeMore && ((new Date().getTime() - startTime) <= (1000 * 30))) {
|
||||
try {
|
||||
let closeResult = closeNextGroup(getNotificationCenterGroups(), closedCount);
|
||||
maybeMore = closeResult[0];
|
||||
if (maybeMore) {
|
||||
closedCount = closedCount + closeResult[1];
|
||||
}
|
||||
} catch (innerErr) {
|
||||
if (maybeMore && closedCount === 0 && attempts < maxAttempts) {
|
||||
log(`Caught an error before anything closed, trying ${maxAttempts - attempts} more time(s).`)
|
||||
attempts++;
|
||||
} else {
|
||||
throw innerErr;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw Error(`No${appNameForLog} notifications found...`);
|
||||
}
|
||||
} catch (err) {
|
||||
logError(err);
|
||||
logError(err.message);
|
||||
getLogLines();
|
||||
throw err;
|
||||
}
|
||||
|
||||
return getLogLines();
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
--- === Dismiss Alerts ===
|
||||
|
||||
local obj = {}
|
||||
obj.__index = obj
|
||||
|
||||
-- Metadata
|
||||
obj.name = "DismissAlerts"
|
||||
obj.version = "0.1"
|
||||
obj.license = "MIT - https://opensource.org/licenses/MIT"
|
||||
|
||||
function obj:init()
|
||||
hs.hotkey.bind({ "cmd", "alt", "ctrl" }, "k", function()
|
||||
hs.osascript.javascriptFromFile("Spoons/DismissAlerts.spoon/close_notifications_applescript.js")
|
||||
end)
|
||||
end
|
||||
|
||||
return obj
|
@ -1,107 +0,0 @@
|
||||
--- === Launcher ===
|
||||
|
||||
local obj = {}
|
||||
obj.__index = obj
|
||||
|
||||
-- Metadata
|
||||
obj.name = "Launcher"
|
||||
obj.version = "0.1"
|
||||
obj.license = "MIT - https://opensource.org/licenses/MIT"
|
||||
|
||||
function DrawSwitcher()
|
||||
-- Drawing
|
||||
local width = hs.screen.mainScreen():fullFrame().w
|
||||
local switcherWidth = 500
|
||||
local canv = hs.canvas.new({
|
||||
x = width / 2 - switcherWidth / 2,
|
||||
y = 1,
|
||||
h = 3,
|
||||
w = switcherWidth,
|
||||
})
|
||||
canv[#canv + 1] = {
|
||||
action = "build",
|
||||
type = "rectangle",
|
||||
}
|
||||
canv[#canv + 1] = {
|
||||
type = "rectangle",
|
||||
fillColor = { alpha = 1, red = 0.8, green = 0.6, blue = 0.3 },
|
||||
action = "fill",
|
||||
}
|
||||
return canv:show()
|
||||
end
|
||||
|
||||
function obj:init()
|
||||
-- Begin launcher mode
|
||||
if self.launcher == nil then
|
||||
self.launcher = hs.hotkey.modal.new("ctrl", "space")
|
||||
end
|
||||
|
||||
-- Behaviors on enter
|
||||
function self.launcher:entered()
|
||||
-- hs.alert("Entered mode")
|
||||
self.canv = DrawSwitcher()
|
||||
end
|
||||
|
||||
-- Behaviors on exit
|
||||
function self.launcher:exited()
|
||||
-- hs.alert("Exited mode")
|
||||
self.canv:hide()
|
||||
end
|
||||
|
||||
-- Use escape to exit launcher mode
|
||||
self.launcher:bind("", "escape", function()
|
||||
self.launcher:exit()
|
||||
end)
|
||||
|
||||
-- Launcher shortcuts
|
||||
self.launcher:bind("ctrl", "space", function() end)
|
||||
self.launcher:bind("", "return", function()
|
||||
self:switch("@kitty@")
|
||||
end)
|
||||
self.launcher:bind("", "C", function()
|
||||
self:switch("Calendar.app")
|
||||
end)
|
||||
self.launcher:bind("", "D", function()
|
||||
self:switch("@discord@")
|
||||
end)
|
||||
self.launcher:bind("", "E", function()
|
||||
self:switch("Mail.app")
|
||||
end)
|
||||
self.launcher:bind("", "U", function()
|
||||
self:switch("Music.app")
|
||||
end)
|
||||
self.launcher:bind("", "F", function()
|
||||
self:switch("@firefox@")
|
||||
end)
|
||||
self.launcher:bind("", "H", function()
|
||||
self:switch("Hammerspoon.app")
|
||||
end)
|
||||
self.launcher:bind("", "G", function()
|
||||
self:switch("Mimestream.app")
|
||||
end)
|
||||
self.launcher:bind("", "M", function()
|
||||
self:switch("Messages.app")
|
||||
end)
|
||||
self.launcher:bind("", "O", function()
|
||||
self:switch("Obsidian.app")
|
||||
end)
|
||||
self.launcher:bind("", "P", function()
|
||||
self:switch("System Preferences.app")
|
||||
end)
|
||||
self.launcher:bind("", "R", function()
|
||||
hs.reload()
|
||||
end)
|
||||
self.launcher:bind("", "S", function()
|
||||
self:switch("Slack.app")
|
||||
end)
|
||||
self.launcher:bind("", "Z", function()
|
||||
self:switch("zoom.us.app")
|
||||
end)
|
||||
end
|
||||
|
||||
function obj:switch(app)
|
||||
hs.application.launchOrFocus(app)
|
||||
self.launcher:exit()
|
||||
end
|
||||
|
||||
return obj
|
@ -1,52 +0,0 @@
|
||||
--- === Move Window ===
|
||||
|
||||
local obj = {}
|
||||
obj.__index = obj
|
||||
|
||||
-- Metadata
|
||||
obj.name = "MoveWindow"
|
||||
obj.version = "0.1"
|
||||
obj.license = "MIT - https://opensource.org/licenses/MIT"
|
||||
|
||||
function obj:init()
|
||||
hs.window.animationDuration = 0
|
||||
dofile(hs.spoons.resourcePath("worklayout.lua"))()
|
||||
-- bind hotkey
|
||||
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "n", function()
|
||||
-- get the focused window
|
||||
local win = hs.window.focusedWindow()
|
||||
-- get the screen where the focused window is displayed, a.k.a. current screen
|
||||
local screen = win:screen()
|
||||
-- local nextScreen = screen:next()
|
||||
-- compute the unitRect of the focused window relative to the current screen
|
||||
-- and move the window to the next screen setting the same unitRect
|
||||
win:moveToScreen(screen:next(), true, true, 0)
|
||||
end)
|
||||
|
||||
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "b", function()
|
||||
local win = hs.window.focusedWindow()
|
||||
local screen = win:screen()
|
||||
win:moveToScreen(screen:previous(), true, true, 0)
|
||||
end)
|
||||
|
||||
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "m", function()
|
||||
-- get the focused window
|
||||
local win = hs.window.focusedWindow()
|
||||
local frame = win:frame()
|
||||
-- maximize if possible
|
||||
local max = win:screen():fullFrame()
|
||||
frame.x = max.x
|
||||
frame.y = max.y
|
||||
frame.w = max.w
|
||||
frame.h = max.h
|
||||
win:setFrame(frame)
|
||||
-- -- first maximize to grid
|
||||
-- hs.grid.maximizeWindow(win)
|
||||
-- -- then spam maximize
|
||||
-- for i = 1, 8 do
|
||||
-- win:maximize()
|
||||
-- end
|
||||
end)
|
||||
end
|
||||
|
||||
return obj
|
@ -1,60 +0,0 @@
|
||||
--- === Work Layout ===
|
||||
-- Portions of this is adopted from:
|
||||
-- https://github.com/anishathalye/dotfiles-local/tree/ffdadd313e58514eb622736b09b91a7d7eb7c6c9/hammerspoon
|
||||
-- License is also available:
|
||||
-- https://github.com/anishathalye/dotfiles-local/blob/ffdadd313e58514eb622736b09b91a7d7eb7c6c9/LICENSE.md
|
||||
|
||||
WORK_LEFT_MONITOR = "DELL U2415 (2)"
|
||||
WORK_RIGHT_MONITOR = "DELL U2415 (1)"
|
||||
LAPTOP_MONITOR = "Built-in Retina Display"
|
||||
|
||||
-- Used to find out the name of the monitor in Hammerspoon
|
||||
-- local function dump(o)
|
||||
-- if type(o) == "table" then
|
||||
-- local s = "{ "
|
||||
-- for k, v in pairs(o) do
|
||||
-- if type(k) ~= "number" then
|
||||
-- k = '"' .. k .. '"'
|
||||
-- end
|
||||
-- s = s .. "[" .. k .. "] = " .. dump(v) .. ","
|
||||
-- end
|
||||
-- return s .. "} "
|
||||
-- else
|
||||
-- return tostring(o)
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- Turn on when looking for the monitor name
|
||||
-- print(dump(hs.screen.allScreens()))
|
||||
|
||||
local function concat(...)
|
||||
local res = {}
|
||||
for _, tab in ipairs({ ... }) do
|
||||
for _, elem in ipairs(tab) do
|
||||
table.insert(res, elem)
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local function worklayout()
|
||||
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "l", function()
|
||||
local u = hs.geometry.unitrect
|
||||
-- set the layout
|
||||
local left = {
|
||||
{ "kitty", nil, WORK_LEFT_MONITOR, u(0, 0, 1, 1), nil, nil, visible = true },
|
||||
}
|
||||
local right = {
|
||||
{ "Slack", nil, WORK_RIGHT_MONITOR, u(0, 0, 1, 1), nil, nil, visible = true },
|
||||
{ "Mail", nil, WORK_RIGHT_MONITOR, u(0, 0, 1, 1), nil, nil, visible = true },
|
||||
{ "zoom.us", nil, WORK_RIGHT_MONITOR, u(1 / 4, 1 / 4, 1 / 2, 1 / 2), nil, nil, visible = true },
|
||||
}
|
||||
local laptop = {
|
||||
{ "Firefox", nil, LAPTOP_MONITOR, u(0, 0, 1, 1), nil, nil, visible = true },
|
||||
}
|
||||
local layout = concat(left, right, laptop)
|
||||
hs.layout.apply(layout)
|
||||
end)
|
||||
end
|
||||
|
||||
return worklayout
|
@ -1,4 +0,0 @@
|
||||
hs.loadSpoon("ControlEscape"):start() -- Load Hammerspoon bits from https://github.com/jasonrudolph/ControlEscape.spoon
|
||||
hs.loadSpoon("Launcher"):init()
|
||||
hs.loadSpoon("DismissAlerts"):init()
|
||||
hs.loadSpoon("MoveWindow"):init()
|
@ -1,56 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
# Homebrew - Mac-specific packages that aren't in Nix
|
||||
config = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
|
||||
# Requires Homebrew to be installed
|
||||
system.activationScripts.preUserActivation.text = ''
|
||||
if ! xcode-select --version 2>/dev/null; then
|
||||
$DRY_RUN_CMD xcode-select --install
|
||||
fi
|
||||
if ! /usr/local/bin/brew --version 2>/dev/null; then
|
||||
$DRY_RUN_CMD /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
fi
|
||||
'';
|
||||
|
||||
homebrew = {
|
||||
enable = true;
|
||||
onActivation = {
|
||||
autoUpdate = false; # Don't update during rebuild
|
||||
cleanup = "zap"; # Uninstall all programs not declared
|
||||
upgrade = true;
|
||||
};
|
||||
global = {
|
||||
brewfile = true; # Run brew bundle from anywhere
|
||||
lockfiles = false; # Don't save lockfile (since running from anywhere)
|
||||
};
|
||||
taps = [
|
||||
"homebrew/cask" # Required for casks
|
||||
"homebrew/cask-drivers" # Used for Logitech G-Hub
|
||||
];
|
||||
brews = [
|
||||
"trash" # Delete files and folders to trash instead of rm
|
||||
"openjdk" # Required by Apache Directory Studio
|
||||
];
|
||||
casks = [
|
||||
"1password" # 1Password packaging on Nix is broken for macOS
|
||||
"scroll-reverser" # Different scroll style for mouse vs. trackpad
|
||||
"meetingbar" # Show meetings in menu bar
|
||||
"gitify" # Git notifications in menu bar
|
||||
"logitech-g-hub" # Mouse and keyboard management
|
||||
"mimestream" # Gmail client
|
||||
"obsidian" # Obsidian packaging on Nix is not available for macOS
|
||||
"steam" # Not packaged for Nix
|
||||
"apache-directory-studio" # Packaging on Nix is not available for macOS
|
||||
];
|
||||
};
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
|
||||
programs.fish.shellAbbrs.t = "trash";
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
# MacOS-specific settings for Kitty
|
||||
home-manager.users.${config.user} = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
programs.kitty = {
|
||||
darwinLaunchOptions = [ "--start-as=fullscreen" ];
|
||||
font.size = lib.mkForce 20;
|
||||
settings = {
|
||||
shell = "/run/current-system/sw/bin/fish";
|
||||
macos_traditional_fullscreen = true;
|
||||
macos_quit_when_last_window_closed = true;
|
||||
disable_ligatures = "always";
|
||||
macos_option_as_alt = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
networking = {
|
||||
computerName = "${config.fullName}'\\''s Mac";
|
||||
# Adjust if necessary
|
||||
# hostName = "";
|
||||
};
|
||||
};
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
home-manager.users.${config.user} = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
|
||||
programs.fish = {
|
||||
shellAbbrs = {
|
||||
nr = lib.mkForce "rebuild-darwin";
|
||||
nro = lib.mkForce "rebuild-darwin offline";
|
||||
};
|
||||
functions = {
|
||||
rebuild-darwin = {
|
||||
body = ''
|
||||
if test "$argv[1]" = "offline"
|
||||
set option "--option substitute false"
|
||||
end
|
||||
git -C ${config.dotfilesPath} add --intent-to-add --all
|
||||
commandline -r "darwin-rebuild switch $option --flake ${config.dotfilesPath}#macbook"
|
||||
commandline --function execute
|
||||
'';
|
||||
};
|
||||
rebuild-home = lib.mkForce {
|
||||
body = ''
|
||||
git -C ${config.dotfilesPath} add --intent-to-add --all
|
||||
commandline -r "${pkgs.home-manager}/bin/home-manager switch --flake ${config.dotfilesPath}#macbook";
|
||||
commandline --function execute
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
{ pkgs, lib, ... }: {
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
|
||||
services.nix-daemon.enable = true;
|
||||
|
||||
environment.shells = [ pkgs.fish ];
|
||||
|
||||
security.pam.enableSudoTouchIdAuth = true;
|
||||
|
||||
system = {
|
||||
|
||||
keyboard = {
|
||||
remapCapsLockToControl = true;
|
||||
enableKeyMapping = true; # Allows for skhd
|
||||
};
|
||||
|
||||
defaults = {
|
||||
NSGlobalDomain = {
|
||||
|
||||
# Set to dark mode
|
||||
AppleInterfaceStyle = "Dark";
|
||||
|
||||
# Don't change from dark to light automatically
|
||||
# AppleInterfaceSwitchesAutomatically = false;
|
||||
|
||||
# Enable full keyboard access for all controls (e.g. enable Tab in modal dialogs)
|
||||
AppleKeyboardUIMode = 3;
|
||||
|
||||
# Automatically show and hide the menu bar
|
||||
_HIHideMenuBar = true;
|
||||
|
||||
# Expand save panel by default
|
||||
NSNavPanelExpandedStateForSaveMode = true;
|
||||
|
||||
# Expand print panel by default
|
||||
PMPrintingExpandedStateForPrint = true;
|
||||
|
||||
# Replace press-and-hold with key repeat
|
||||
ApplePressAndHoldEnabled = false;
|
||||
|
||||
# Set a fast key repeat rate
|
||||
KeyRepeat = 2;
|
||||
|
||||
# Shorten delay before key repeat begins
|
||||
InitialKeyRepeat = 12;
|
||||
|
||||
# Save to local disk by default, not iCloud
|
||||
NSDocumentSaveNewDocumentsToCloud = false;
|
||||
|
||||
# Disable autocorrect capitalization
|
||||
NSAutomaticCapitalizationEnabled = false;
|
||||
|
||||
# Disable autocorrect smart dashes
|
||||
NSAutomaticDashSubstitutionEnabled = false;
|
||||
|
||||
# Disable autocorrect adding periods
|
||||
NSAutomaticPeriodSubstitutionEnabled = false;
|
||||
|
||||
# Disable autocorrect smart quotation marks
|
||||
NSAutomaticQuoteSubstitutionEnabled = false;
|
||||
|
||||
# Disable autocorrect spellcheck
|
||||
NSAutomaticSpellingCorrectionEnabled = false;
|
||||
};
|
||||
|
||||
dock = {
|
||||
# Automatically show and hide the dock
|
||||
autohide = true;
|
||||
|
||||
# Add translucency in dock for hidden applications
|
||||
showhidden = true;
|
||||
|
||||
# Enable spring loading on all dock items
|
||||
enable-spring-load-actions-on-all-items = true;
|
||||
|
||||
# Highlight hover effect in dock stack grid view
|
||||
mouse-over-hilite-stack = true;
|
||||
|
||||
mineffect = "genie";
|
||||
orientation = "bottom";
|
||||
show-recents = false;
|
||||
tilesize = 44;
|
||||
};
|
||||
|
||||
finder = {
|
||||
|
||||
# Default Finder window set to column view
|
||||
FXPreferredViewStyle = "clmv";
|
||||
|
||||
# Finder search in current folder by default
|
||||
FXDefaultSearchScope = "SCcf";
|
||||
|
||||
# Disable warning when changing file extension
|
||||
FXEnableExtensionChangeWarning = false;
|
||||
|
||||
# Allow quitting of Finder application
|
||||
QuitMenuItem = true;
|
||||
|
||||
};
|
||||
|
||||
# Disable "Are you sure you want to open" dialog
|
||||
LaunchServices.LSQuarantine = false;
|
||||
|
||||
# Disable trackpad tap to click
|
||||
trackpad.Clicking = false;
|
||||
|
||||
# universalaccess = {
|
||||
|
||||
# # Zoom in with Control + Scroll Wheel
|
||||
# closeViewScrollWheelToggle = true;
|
||||
# closeViewZoomFollowsFocus = true;
|
||||
# };
|
||||
|
||||
# Where to save screenshots
|
||||
screencapture.location = "~/Downloads";
|
||||
|
||||
};
|
||||
|
||||
# Settings that don't have an option in nix-darwin
|
||||
activationScripts.postActivation.text = ''
|
||||
echo "Disable disk image verification"
|
||||
defaults write com.apple.frameworks.diskimages skip-verify -bool true
|
||||
defaults write com.apple.frameworks.diskimages skip-verify-locked -bool true
|
||||
defaults write com.apple.frameworks.diskimages skip-verify-remote -bool true
|
||||
|
||||
echo "Avoid creating .DS_Store files on network volumes"
|
||||
defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true
|
||||
|
||||
echo "Disable the warning before emptying the Trash"
|
||||
defaults write com.apple.finder WarnOnEmptyTrash -bool false
|
||||
|
||||
echo "Require password immediately after sleep or screen saver begins"
|
||||
defaults write com.apple.screensaver askForPassword -int 1
|
||||
defaults write com.apple.screensaver askForPasswordDelay -int 0
|
||||
|
||||
echo "Allow apps from anywhere"
|
||||
SPCTL=$(spctl --status)
|
||||
if ! [ "$SPCTL" = "assessments disabled" ]; then
|
||||
sudo spctl --master-disable
|
||||
fi
|
||||
|
||||
'';
|
||||
|
||||
# User-level settings
|
||||
activationScripts.postUserActivation.text = ''
|
||||
echo "Show the ~/Library folder"
|
||||
chflags nohidden ~/Library
|
||||
|
||||
echo "Enable dock magnification"
|
||||
defaults write com.apple.dock magnification -bool true
|
||||
|
||||
echo "Set dock magnification size"
|
||||
defaults write com.apple.dock largesize -int 48
|
||||
|
||||
echo "Define dock icon function"
|
||||
__dock_item() {
|
||||
printf "%s%s%s%s%s" \
|
||||
"<dict><key>tile-data</key><dict><key>file-data</key><dict>" \
|
||||
"<key>_CFURLString</key><string>" \
|
||||
"$1" \
|
||||
"</string><key>_CFURLStringType</key><integer>0</integer>" \
|
||||
"</dict></dict></dict>"
|
||||
}
|
||||
|
||||
echo "Choose and order dock icons"
|
||||
defaults write com.apple.dock persistent-apps -array \
|
||||
"$(__dock_item /Applications/1Password.app)" \
|
||||
"$(__dock_item /Applications/Slack.app)" \
|
||||
"$(__dock_item /System/Applications/Calendar.app)" \
|
||||
"$(__dock_item ${pkgs.firefox-bin}/Applications/Firefox.app)" \
|
||||
"$(__dock_item /System/Applications/Messages.app)" \
|
||||
"$(__dock_item /System/Applications/Mail.app)" \
|
||||
"$(__dock_item /Applications/Mimestream.app)" \
|
||||
"$(__dock_item /Applications/zoom.us.app)" \
|
||||
"$(__dock_item ${pkgs.discord}/Applications/Discord.app)" \
|
||||
"$(__dock_item /Applications/Obsidian.app)" \
|
||||
"$(__dock_item ${pkgs.kitty}/Applications/kitty.app)" \
|
||||
"$(__dock_item /System/Applications/System\ Settings.app)"
|
||||
'';
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options.tmux.enable = lib.mkEnableOption "Tmux terminal multiplexer";
|
||||
|
||||
config.home-manager.users.${config.user} = lib.mkIf config.tmux.enable {
|
||||
|
||||
programs.tmux = {
|
||||
enable = true;
|
||||
baseIndex = 1; # Start windows and panes at 1
|
||||
escapeTime = 0; # Wait time after escape is input
|
||||
historyLimit = 100000;
|
||||
keyMode = "vi";
|
||||
newSession = true; # Automatically spawn new session
|
||||
plugins = [ ];
|
||||
resizeAmount = 10;
|
||||
shell = "${pkgs.fish}/bin/fish";
|
||||
terminal = "screen-256color";
|
||||
extraConfig = ''
|
||||
# Horizontal and vertical splits
|
||||
bind \\ split-window -h -c '#{pane_current_path}'
|
||||
bind - split-window -v -c '#{pane_current_path}'
|
||||
|
||||
# Move between panes with vi keys
|
||||
bind h select-pane -L
|
||||
bind j select-pane -D
|
||||
bind K select-pane -U
|
||||
bind l select-pane -R
|
||||
|
||||
# Split out pane
|
||||
bind b break-pane
|
||||
|
||||
# Synchronize panes
|
||||
bind S set-window-option synchronize-panes
|
||||
|
||||
# Copy mode works as Vim
|
||||
bind Escape copy-mode
|
||||
bind k copy-mode
|
||||
bind C-[ copy-mode
|
||||
|
||||
# Use v to trigger selection
|
||||
bind-key -T copy-mode-vi v send-keys -X begin-selection
|
||||
|
||||
# Use y to yank current selection
|
||||
bind-key -T copy-mode-vi y send-keys -X copy-selection-and-cancel
|
||||
|
||||
# Enable mouse mode
|
||||
set -g mouse on
|
||||
|
||||
# Status bar
|
||||
set -g status-interval 60 # Seconds between refreshes
|
||||
set -g renumber-windows on
|
||||
set-option -g status-position bottom
|
||||
|
||||
## COLORSCHEME: gruvbox dark
|
||||
set-option -g status "on"
|
||||
|
||||
# Default statusbar color
|
||||
set-option -g status-style bg=colour237,fg=colour223 # bg=bg1, fg=fg1
|
||||
|
||||
# Default window title colors
|
||||
set-window-option -g window-status-style bg=colour214,fg=colour237 # bg=yellow, fg=bg1
|
||||
|
||||
# Default window with an activity alert
|
||||
set-window-option -g window-status-activity-style bg=colour237,fg=colour248 # bg=bg1, fg=fg3
|
||||
|
||||
# Active window title colors
|
||||
set-window-option -g window-status-current-style bg=red,fg=colour237 # fg=bg1
|
||||
|
||||
# Pane border
|
||||
set-option -g pane-active-border-style fg=colour250 #fg2
|
||||
set-option -g pane-border-style fg=colour237 #bg1
|
||||
|
||||
# Message infos
|
||||
set-option -g message-style bg=colour239,fg=colour223 # bg=bg2, fg=fg1
|
||||
|
||||
# Writing commands inactive
|
||||
set-option -g message-command-style bg=colour239,fg=colour223 # bg=fg3, fg=bg1
|
||||
|
||||
# Pane number display
|
||||
set-option -g display-panes-active-colour colour250 #fg2
|
||||
set-option -g display-panes-colour colour237 #bg1
|
||||
|
||||
# Clock
|
||||
set-window-option -g clock-mode-colour colour109 #blue
|
||||
|
||||
# Bell
|
||||
set-window-option -g window-status-bell-style bg=colour167,fg=colour235 # bg=red, fg=bg
|
||||
|
||||
# Theme settings mixed with colors (unfortunately, but there is no cleaner way)
|
||||
set-option -g status-justify "left"
|
||||
set-option -g status-left-style none
|
||||
set-option -g status-left-length "80"
|
||||
set-option -g status-right-style none
|
||||
set-option -g status-right-length "80"
|
||||
set-window-option -g window-status-separator ""
|
||||
|
||||
set-option -g status-left "#[fg=colour248, bg=colour241] #S #[fg=colour241, bg=colour237, nobold, noitalics, nounderscore]"
|
||||
set-option -g status-right "#[fg=colour239, bg=colour237, nobold, nounderscore, noitalics]#[fg=colour246,bg=colour239] %Y-%m-%d %H:%M #[fg=colour248, bg=colour239, nobold, noitalics, nounderscore]"
|
||||
|
||||
set-window-option -g window-status-current-format "#[fg=colour237, bg=colour214, nobold, noitalics, nounderscore]#[fg=colour239, bg=colour214] #I #[fg=colour239, bg=colour214, bold] #W #[fg=colour214, bg=colour237, nobold, noitalics, nounderscore]"
|
||||
set-window-option -g window-status-format "#[fg=colour237,bg=colour239,noitalics]#[fg=colour223,bg=colour239] #I #[fg=colour223, bg=colour239] #W #[fg=colour239, bg=colour237, noitalics]"
|
||||
'';
|
||||
};
|
||||
|
||||
programs.alacritty.settings = {
|
||||
# shell.args = [
|
||||
# "--login"
|
||||
# "--init-command"
|
||||
# "tmux attach-session -t noah || tmux new-session -s noah"
|
||||
# ];
|
||||
key_bindings = [
|
||||
{
|
||||
key = "H";
|
||||
mods = "Super|Shift";
|
||||
chars = "\\x02p"; # Previous tmux window
|
||||
}
|
||||
{
|
||||
key = "L";
|
||||
mods = "Super|Shift";
|
||||
chars = "\\x02n"; # Next tmux window
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
programs.fish.shellAbbrs = {
|
||||
ta = "tmux attach-session";
|
||||
tan = "tmux attach-session -t noah";
|
||||
tnn = "tmux new-session -s noah";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
|
||||
users.users."${config.user}" = {
|
||||
# macOS user
|
||||
home = config.homePath;
|
||||
shell = pkgs.fish; # Default shell
|
||||
|
||||
};
|
||||
|
||||
# Used for aerc
|
||||
home-manager.users.${config.user} = {
|
||||
home.sessionVariables = {
|
||||
XDG_CONFIG_HOME = "${config.homePath}/.config";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
|
||||
# Quickly package shell scripts with their dependencies
|
||||
# From https://discourse.nixos.org/t/how-to-create-a-script-with-dependencies/7970/6
|
||||
mkScript = { name, file, env ? [ ] }:
|
||||
pkgs.writeScriptBin name ''
|
||||
for i in ${lib.concatStringsSep " " env}; do
|
||||
export PATH="$i/bin:$PATH"
|
||||
done
|
||||
|
||||
exec ${pkgs.bash}/bin/bash ${file} $@
|
||||
'';
|
||||
|
||||
in {
|
||||
|
||||
home-manager.users.${config.user} = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
|
||||
home.packages = with pkgs; [
|
||||
# visidata # CSV inspector
|
||||
dos2unix # Convert Windows text files
|
||||
inetutils # Includes telnet
|
||||
youtube-dl # Convert web videos
|
||||
pandoc # Convert text documents
|
||||
mpd # TUI slideshows
|
||||
awscli2
|
||||
awslogs
|
||||
google-cloud-sdk
|
||||
ansible
|
||||
vault
|
||||
consul
|
||||
noti # Create notifications programmatically
|
||||
ipcalc # Make IP network calculations
|
||||
(mkScript {
|
||||
name = "ocr";
|
||||
file = ../shell/bash/scripts/ocr.sh;
|
||||
env = [ tesseract ];
|
||||
})
|
||||
];
|
||||
|
||||
programs.fish.shellAbbrs = {
|
||||
# Add noti for ghpr in Darwin
|
||||
ghpr = lib.mkForce "gh pr create && sleep 3 && noti gh run watch";
|
||||
grw = lib.mkForce "noti gh run watch";
|
||||
|
||||
# Shortcut to edit hosts file
|
||||
hosts = "sudo nvim /etc/hosts";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,20 +1,7 @@
|
||||
{ config, lib, pkgs, ... }: {
|
||||
|
||||
imports = [
|
||||
./applications
|
||||
./darwin
|
||||
./gaming
|
||||
./graphical
|
||||
./hardware
|
||||
./mail
|
||||
./neovim
|
||||
./nixos
|
||||
./programming
|
||||
./repositories
|
||||
./services
|
||||
./shell
|
||||
./wsl
|
||||
];
|
||||
imports =
|
||||
[ ./applications ./mail ./neovim ./programming ./repositories ./shell ];
|
||||
|
||||
options = {
|
||||
user = lib.mkOption {
|
||||
|
@ -1,19 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
imports = [
|
||||
./leagueoflegends.nix
|
||||
./legendary.nix
|
||||
./lutris.nix
|
||||
./minecraft-server.nix
|
||||
./steam.nix
|
||||
];
|
||||
|
||||
options.gaming.enable = lib.mkEnableOption "Enable gaming features.";
|
||||
|
||||
config = lib.mkIf (config.gaming.enable && pkgs.stdenv.isLinux) {
|
||||
hardware.opengl = {
|
||||
enable = true;
|
||||
driSupport32Bit = true;
|
||||
};
|
||||
};
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options.gaming.leagueoflegends.enable =
|
||||
lib.mkEnableOption "League of Legends";
|
||||
|
||||
config =
|
||||
lib.mkIf (config.gaming.leagueoflegends.enable && pkgs.stdenv.isLinux) {
|
||||
|
||||
# League of Legends anti-cheat requirement
|
||||
boot.kernel.sysctl = { "abi.vsyscall32" = 0; };
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
|
||||
# Lutris requirement to install the game
|
||||
lutris
|
||||
amdvlk
|
||||
wineWowPackages.stable
|
||||
# vulkan-tools
|
||||
|
||||
# Required according to https://lutris.net/games/league-of-legends/
|
||||
openssl
|
||||
gnome.zenity
|
||||
|
||||
# Don't remember if this is required
|
||||
dconf
|
||||
|
||||
];
|
||||
|
||||
environment.sessionVariables = { QT_X11_NO_MITSHM = "1"; };
|
||||
|
||||
};
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let home-packages = config.home-manager.users.${config.user}.home.packages;
|
||||
|
||||
in {
|
||||
|
||||
options.gaming.legendary.enable =
|
||||
lib.mkEnableOption "Legendary Epic Games launcher.";
|
||||
|
||||
config = lib.mkIf config.gaming.legendary.enable {
|
||||
environment.systemPackages = with pkgs; [
|
||||
legendary-gl
|
||||
rare # GUI for Legendary (not working)
|
||||
wineWowPackages.stable # 32-bit and 64-bit wineWowPackages, see https://nixos.wiki/wiki/Wine
|
||||
];
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
|
||||
xdg.configFile."legendary/config.ini".text = ''
|
||||
[Legendary]
|
||||
; Disables the automatic update check
|
||||
disable_update_check = false
|
||||
; Disables the notice about an available update on exit
|
||||
disable_update_notice = true
|
||||
; Set install directory
|
||||
install_dir = ${config.homePath}/media/games
|
||||
; Make output quiet
|
||||
log_level = error
|
||||
'';
|
||||
|
||||
home.file = let
|
||||
ignorePatterns = ''
|
||||
.wine/
|
||||
drive_c/'';
|
||||
in {
|
||||
".rgignore".text = ignorePatterns;
|
||||
".fdignore".text = ignorePatterns;
|
||||
};
|
||||
|
||||
programs.fish.functions =
|
||||
lib.mkIf (builtins.elem pkgs.fzf home-packages) {
|
||||
epic-games = {
|
||||
body = ''
|
||||
set game (legendary list 2>/dev/null \
|
||||
| awk '/^ \* / { print $0; }' \
|
||||
| sed -e 's/ (.*)$//' -e 's/ \* //' \
|
||||
| fzf)
|
||||
and legendary launch "$game" &> /dev/null
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options.gaming.lutris.enable = lib.mkEnableOption "Lutris game installer.";
|
||||
|
||||
config = lib.mkIf config.gaming.lutris.enable {
|
||||
environment.systemPackages = with pkgs; [
|
||||
lutris
|
||||
amdvlk # Vulkan drivers (probably already installed)
|
||||
wineWowPackages.stable # 32-bit and 64-bit wineWowPackages
|
||||
];
|
||||
};
|
||||
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
|
||||
localPort = 25564;
|
||||
publicPort = 49732;
|
||||
rconPort = 25575;
|
||||
rconPassword = "thiscanbeanything";
|
||||
|
||||
in {
|
||||
|
||||
options.gaming.minecraft-server.enable =
|
||||
lib.mkEnableOption "Minecraft Server.";
|
||||
|
||||
config = lib.mkIf config.gaming.minecraft-server.enable {
|
||||
|
||||
unfreePackages = [ "minecraft-server" ];
|
||||
|
||||
services.minecraft-server = {
|
||||
enable = true;
|
||||
eula = true;
|
||||
declarative = true;
|
||||
whitelist = { };
|
||||
openFirewall = false;
|
||||
serverProperties = {
|
||||
server-port = localPort;
|
||||
difficulty = "normal";
|
||||
gamemode = "survival";
|
||||
white-list = false;
|
||||
enforce-whitelist = false;
|
||||
level-name = "world";
|
||||
motd = "Welcome!";
|
||||
pvp = true;
|
||||
player-idle-timeout = 30;
|
||||
generate-structures = true;
|
||||
max-players = 20;
|
||||
snooper-enabled = false;
|
||||
spawn-npcs = true;
|
||||
spawn-animals = true;
|
||||
spawn-monsters = true;
|
||||
allow-nether = true;
|
||||
allow-flight = false;
|
||||
enable-rcon = true;
|
||||
"rcon.port" = rconPort;
|
||||
"rcon.password" = rconPassword;
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ publicPort ];
|
||||
|
||||
## Automatically start and stop Minecraft server based on player connections
|
||||
|
||||
# Adapted shamelessly from:
|
||||
# https://dataswamp.org/~solene/2022-08-20-on-demand-minecraft-with-systemd.html
|
||||
|
||||
# Prevent Minecraft from starting by default
|
||||
systemd.services.minecraft-server = { wantedBy = pkgs.lib.mkForce [ ]; };
|
||||
|
||||
# Listen for connections on the public port, to trigger the actual
|
||||
# listen-minecraft service.
|
||||
systemd.sockets.listen-minecraft = {
|
||||
wantedBy = [ "sockets.target" ];
|
||||
requires = [ "network.target" ];
|
||||
listenStreams = [ "${toString publicPort}" ];
|
||||
};
|
||||
|
||||
# Proxy traffic to local port, and trigger hook-minecraft
|
||||
systemd.services.listen-minecraft = {
|
||||
path = [ pkgs.systemd ];
|
||||
requires = [ "hook-minecraft.service" "listen-minecraft.socket" ];
|
||||
after = [ "hook-minecraft.service" "listen-minecraft.socket" ];
|
||||
serviceConfig.ExecStart =
|
||||
"${pkgs.systemd.out}/lib/systemd/systemd-socket-proxyd 127.0.0.1:${
|
||||
toString localPort
|
||||
}";
|
||||
};
|
||||
|
||||
# Start Minecraft if required and wait for it to be available
|
||||
# Then unlock the listen-minecraft.service
|
||||
systemd.services.hook-minecraft = {
|
||||
path = with pkgs; [ systemd libressl busybox ];
|
||||
|
||||
# Start Minecraft and the auto-shutdown timer
|
||||
script = ''
|
||||
systemctl start minecraft-server.service
|
||||
systemctl start stop-minecraft.timer
|
||||
'';
|
||||
|
||||
# Keep checking until the service is available
|
||||
postStart = ''
|
||||
for i in $(seq 60); do
|
||||
if ${pkgs.libressl.nc}/bin/nc -z 127.0.0.1 ${
|
||||
toString localPort
|
||||
} > /dev/null ; then
|
||||
exit 0
|
||||
fi
|
||||
${pkgs.busybox.out}/bin/sleep 1
|
||||
done
|
||||
exit 1
|
||||
'';
|
||||
};
|
||||
|
||||
# Run a player check on a schedule for auto-shutdown
|
||||
systemd.timers.stop-minecraft = {
|
||||
timerConfig = {
|
||||
OnCalendar = "*-*-* *:*:0/20"; # Every 20 seconds
|
||||
Unit = "stop-minecraft.service";
|
||||
};
|
||||
};
|
||||
|
||||
# If no players are connected, then stop services and prepare to resume again
|
||||
systemd.services.stop-minecraft = {
|
||||
serviceConfig.Type = "oneshot";
|
||||
script = ''
|
||||
# Check when service was launched
|
||||
servicestartsec=$(
|
||||
date -d \
|
||||
"$(systemctl show \
|
||||
--property=ActiveEnterTimestamp \
|
||||
minecraft-server.service \
|
||||
| cut -d= -f2)" \
|
||||
+%s)
|
||||
|
||||
# Calculate elapsed time
|
||||
serviceelapsedsec=$(( $(date +%s) - servicestartsec))
|
||||
|
||||
# Ignore if service just started
|
||||
if [ $serviceelapsedsec -lt 180 ]
|
||||
then
|
||||
echo "Server was just started"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PLAYERS=$(
|
||||
printf "list\n" \
|
||||
| ${pkgs.rcon.out}/bin/rcon -m \
|
||||
-H 127.0.0.1 -p ${builtins.toString rconPort} -P ${rconPassword} \
|
||||
)
|
||||
|
||||
if echo "$PLAYERS" | grep "are 0 of a"
|
||||
then
|
||||
echo "Stopping server"
|
||||
systemctl stop minecraft-server.service
|
||||
systemctl stop hook-minecraft.service
|
||||
systemctl stop stop-minecraft.timer
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options.gaming.steam.enable = lib.mkEnableOption "Steam game launcher.";
|
||||
|
||||
config = lib.mkIf (config.gaming.steam.enable && pkgs.stdenv.isLinux) {
|
||||
hardware.steam-hardware.enable = true;
|
||||
unfreePackages = [ "steam" "steam-original" "steamcmd" "steam-run" ];
|
||||
environment.systemPackages = with pkgs; [
|
||||
|
||||
steam
|
||||
|
||||
# Enable terminal interaction
|
||||
steamPackages.steamcmd
|
||||
steam-tui
|
||||
|
||||
];
|
||||
};
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
{ lib, ... }: {
|
||||
|
||||
imports =
|
||||
[ ./xorg.nix ./fonts.nix ./i3.nix ./polybar.nix ./picom.nix ./rofi.nix ];
|
||||
|
||||
options = {
|
||||
|
||||
launcherCommand = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Command to use for launching";
|
||||
};
|
||||
systemdSearch = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Command to use for interacting with systemd";
|
||||
};
|
||||
altTabCommand = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Command to use for choosing windows";
|
||||
};
|
||||
toggleBarCommand = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Command to hide and show the status bar.";
|
||||
};
|
||||
wallpaper = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = "Wallpaper background image file";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
|
||||
options.gui.dmenu.enable = lib.mkEnableOption "dmenu launcher.";
|
||||
|
||||
config = lib.mkIf (config.services.xserver.enable && config.dmenu.enable) {
|
||||
|
||||
home-manager.users.${config.user}.home.packages = [ pkgs.dmenu ];
|
||||
gui.launcherCommand = "${pkgs.dmenu}/bin/dmenu_run";
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let fontName = "Victor Mono";
|
||||
|
||||
in {
|
||||
|
||||
config = lib.mkIf (config.gui.enable && pkgs.stdenv.isLinux) {
|
||||
|
||||
fonts.fonts = with pkgs; [
|
||||
victor-mono # Used for Vim and Terminal
|
||||
(nerdfonts.override { fonts = [ "Hack" ]; }) # For Polybar, Rofi
|
||||
];
|
||||
fonts.fontconfig.defaultFonts.monospace = [ fontName ];
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
xsession.windowManager.i3.config.fonts = {
|
||||
names = [ "pango:${fontName}" ];
|
||||
# style = "Regular";
|
||||
# size = 11.0;
|
||||
};
|
||||
services.polybar.config."bar/main".font-0 = "Hack Nerd Font:size=10;2";
|
||||
programs.rofi.font = "Hack Nerd Font 14";
|
||||
programs.alacritty.settings.font.normal.family = fontName;
|
||||
programs.kitty.font.name = fontName;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,284 +0,0 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
|
||||
lockCmd =
|
||||
"${pkgs.betterlockscreen}/bin/betterlockscreen --lock --display 1 --blur 0.5 --span";
|
||||
lockUpdate =
|
||||
"${pkgs.betterlockscreen}/bin/betterlockscreen --update ${config.wallpaper} --display 1 --span";
|
||||
|
||||
in {
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isLinux {
|
||||
|
||||
services.xserver.windowManager = {
|
||||
i3 = {
|
||||
enable = config.services.xserver.enable;
|
||||
package = pkgs.i3-gaps;
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
feh # Wallpaper
|
||||
playerctl # Media control
|
||||
];
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
xsession.windowManager.i3 = {
|
||||
enable = config.services.xserver.enable;
|
||||
package = pkgs.i3-gaps;
|
||||
config = let
|
||||
modifier = "Mod4"; # Super key
|
||||
ws1 = "1:I";
|
||||
ws2 = "2:II";
|
||||
ws3 = "3:III";
|
||||
ws4 = "4:IV";
|
||||
ws5 = "5:V";
|
||||
ws6 = "6:VI";
|
||||
ws7 = "7:VII";
|
||||
ws8 = "8:VIII";
|
||||
ws9 = "9:IX";
|
||||
ws10 = "10:X";
|
||||
in {
|
||||
modifier = modifier;
|
||||
assigns = {
|
||||
"${ws1}" = [{ class = "Firefox"; }];
|
||||
"${ws2}" = [{ class = "kitty"; }];
|
||||
"${ws3}" = [{ class = "discord"; }];
|
||||
"${ws4}" = [{ class = "Steam"; }];
|
||||
};
|
||||
bars = [{ command = "echo"; }]; # Disable i3bar
|
||||
colors = let
|
||||
background = config.theme.colors.base00;
|
||||
inactiveBackground = config.theme.colors.base01;
|
||||
border = config.theme.colors.base01;
|
||||
inactiveBorder = config.theme.colors.base01;
|
||||
text = config.theme.colors.base07;
|
||||
inactiveText = config.theme.colors.base04;
|
||||
urgentBackground = config.theme.colors.base08;
|
||||
indicator = "#00000000";
|
||||
in {
|
||||
background = config.theme.colors.base00;
|
||||
focused = {
|
||||
inherit background indicator text border;
|
||||
childBorder = background;
|
||||
};
|
||||
focusedInactive = {
|
||||
inherit indicator;
|
||||
background = inactiveBackground;
|
||||
border = inactiveBorder;
|
||||
childBorder = inactiveBackground;
|
||||
text = inactiveText;
|
||||
};
|
||||
# placeholder = { };
|
||||
unfocused = {
|
||||
inherit indicator;
|
||||
background = inactiveBackground;
|
||||
border = inactiveBorder;
|
||||
childBorder = inactiveBackground;
|
||||
text = inactiveText;
|
||||
};
|
||||
urgent = {
|
||||
inherit text indicator;
|
||||
background = urgentBackground;
|
||||
border = urgentBackground;
|
||||
childBorder = urgentBackground;
|
||||
};
|
||||
};
|
||||
floating.modifier = modifier;
|
||||
focus = {
|
||||
mouseWarping = true;
|
||||
newWindow = "urgent";
|
||||
followMouse = false;
|
||||
};
|
||||
keybindings = {
|
||||
|
||||
# Adjust screen brightness
|
||||
"Shift+F12" =
|
||||
"exec ${pkgs.ddcutil}/bin/ddcutil --display 1 setvcp 10 + 30 && sleep 1; exec ${pkgs.ddcutil}/bin/ddcutil --display 2 setvcp 10 + 30";
|
||||
"Shift+F11" =
|
||||
"exec ${pkgs.ddcutil}/bin/ddcutil --display 1 setvcp 10 - 30 && sleep 1; exec ${pkgs.ddcutil}/bin/ddcutil --display 2 setvcp 10 - 30";
|
||||
"XF86MonBrightnessUp" =
|
||||
"exec ${pkgs.ddcutil}/bin/ddcutil --display 1 setvcp 10 + 30 && sleep 1; exec ${pkgs.ddcutil}/bin/ddcutil --display 2 setvcp 10 + 30";
|
||||
"XF86MonBrightnessDown" =
|
||||
"exec ${pkgs.ddcutil}/bin/ddcutil --display 1 setvcp 10 - 30 && sleep 1; exec ${pkgs.ddcutil}/bin/ddcutil --display 2 setvcp 10 - 30";
|
||||
|
||||
# Media player controls
|
||||
"XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play-pause";
|
||||
"XF86AudioStop" = "exec ${pkgs.playerctl}/bin/playerctl stop";
|
||||
"XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next";
|
||||
"XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous";
|
||||
|
||||
# Launchers
|
||||
"${modifier}+Return" =
|
||||
"exec --no-startup-id kitty; workspace ${ws2}; layout tabbed";
|
||||
"${modifier}+space" =
|
||||
"exec --no-startup-id ${config.launcherCommand}";
|
||||
"${modifier}+Shift+s" =
|
||||
"exec --no-startup-id ${config.systemdSearch}";
|
||||
"Mod1+Tab" = "exec --no-startup-id ${config.altTabCommand}";
|
||||
"${modifier}+Shift+c" = "reload";
|
||||
"${modifier}+Shift+r" = "restart";
|
||||
"${modifier}+Shift+q" = ''
|
||||
exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"'';
|
||||
"${modifier}+Shift+x" = "exec ${lockCmd}";
|
||||
|
||||
# Window options
|
||||
"${modifier}+q" = "kill";
|
||||
"${modifier}+b" = "exec ${config.toggleBarCommand}";
|
||||
"${modifier}+f" = "fullscreen toggle";
|
||||
"${modifier}+h" = "focus left";
|
||||
"${modifier}+j" = "focus down";
|
||||
"${modifier}+k" = "focus up";
|
||||
"${modifier}+l" = "focus right";
|
||||
"${modifier}+Left" = "focus left";
|
||||
"${modifier}+Down" = "focus down";
|
||||
"${modifier}+Up" = "focus up";
|
||||
"${modifier}+Right" = "focus right";
|
||||
"${modifier}+Shift+h" = "move left";
|
||||
"${modifier}+Shift+j" = "move down";
|
||||
"${modifier}+Shift+k" = "move up";
|
||||
"${modifier}+Shift+l" = "move right";
|
||||
"${modifier}+Shift+Left" = "move left";
|
||||
"${modifier}+Shift+Down" = "move down";
|
||||
"${modifier}+Shift+Up" = "move up";
|
||||
"${modifier}+Shift+Right" = "move right";
|
||||
|
||||
# Tiling
|
||||
"${modifier}+i" = "split h";
|
||||
"${modifier}+v" = "split v";
|
||||
"${modifier}+s" = "layout stacking";
|
||||
"${modifier}+t" = "layout tabbed";
|
||||
"${modifier}+e" = "layout toggle split";
|
||||
"${modifier}+Shift+space" = "floating toggle";
|
||||
"${modifier}+Control+space" = "focus mode_toggle";
|
||||
"${modifier}+a" = "focus parent";
|
||||
|
||||
# Workspaces
|
||||
"${modifier}+1" = "workspace ${ws1}";
|
||||
"${modifier}+2" = "workspace ${ws2}";
|
||||
"${modifier}+3" = "workspace ${ws3}";
|
||||
"${modifier}+4" = "workspace ${ws4}";
|
||||
"${modifier}+5" = "workspace ${ws5}";
|
||||
"${modifier}+6" = "workspace ${ws6}";
|
||||
"${modifier}+7" = "workspace ${ws7}";
|
||||
"${modifier}+8" = "workspace ${ws8}";
|
||||
"${modifier}+9" = "workspace ${ws9}";
|
||||
"${modifier}+0" = "workspace ${ws10}";
|
||||
|
||||
# Move windows
|
||||
"${modifier}+Shift+1" =
|
||||
"move container to workspace ${ws1}; workspace ${ws1}";
|
||||
"${modifier}+Shift+2" =
|
||||
"move container to workspace ${ws2}; workspace ${ws2}";
|
||||
"${modifier}+Shift+3" =
|
||||
"move container to workspace ${ws3}; workspace ${ws3}";
|
||||
"${modifier}+Shift+4" =
|
||||
"move container to workspace ${ws4}; workspace ${ws4}";
|
||||
"${modifier}+Shift+5" =
|
||||
"move container to workspace ${ws5}; workspace ${ws5}";
|
||||
"${modifier}+Shift+6" =
|
||||
"move container to workspace ${ws6}; workspace ${ws6}";
|
||||
"${modifier}+Shift+7" =
|
||||
"move container to workspace ${ws7}; workspace ${ws7}";
|
||||
"${modifier}+Shift+8" =
|
||||
"move container to workspace ${ws8}; workspace ${ws8}";
|
||||
"${modifier}+Shift+9" =
|
||||
"move container to workspace ${ws9}; workspace ${ws9}";
|
||||
"${modifier}+Shift+0" =
|
||||
"move container to workspace ${ws10}; workspace ${ws10}";
|
||||
|
||||
# Move screens
|
||||
"${modifier}+Control+l" = "move workspace to output right";
|
||||
"${modifier}+Control+h" = "move workspace to output left";
|
||||
|
||||
# Resizing
|
||||
"${modifier}+r" = ''mode "resize"'';
|
||||
"${modifier}+Control+Shift+h" =
|
||||
"resize shrink width 10 px or 10 ppt";
|
||||
"${modifier}+Control+Shift+j" =
|
||||
"resize grow height 10 px or 10 ppt";
|
||||
"${modifier}+Control+Shift+k" =
|
||||
"resize shrink height 10 px or 10 ppt";
|
||||
"${modifier}+Control+Shift+l" = "resize grow width 10 px or 10 ppt";
|
||||
};
|
||||
modes = { };
|
||||
startup = [
|
||||
{
|
||||
command = "feh --bg-fill ${config.wallpaper}";
|
||||
always = true;
|
||||
notification = false;
|
||||
}
|
||||
{
|
||||
command =
|
||||
"i3-msg workspace ${ws2}, move workspace to output right";
|
||||
notification = false;
|
||||
}
|
||||
{
|
||||
command =
|
||||
"i3-msg workspace ${ws1}, move workspace to output left";
|
||||
notification = false;
|
||||
}
|
||||
];
|
||||
window = {
|
||||
border = 0;
|
||||
hideEdgeBorders = "smart";
|
||||
titlebar = false;
|
||||
};
|
||||
workspaceAutoBackAndForth = false;
|
||||
workspaceOutputAssign = [ ];
|
||||
# gaps = {
|
||||
# bottom = 8;
|
||||
# top = 8;
|
||||
# left = 8;
|
||||
# right = 8;
|
||||
# horizontal = 15;
|
||||
# vertical = 15;
|
||||
# inner = 15;
|
||||
# outer = 0;
|
||||
# smartBorders = "off";
|
||||
# smartGaps = false;
|
||||
# };
|
||||
};
|
||||
extraConfig = "";
|
||||
};
|
||||
|
||||
programs.fish.functions = {
|
||||
update-lock-screen = lib.mkIf config.services.xserver.enable {
|
||||
description = "Update lockscreen with wallpaper";
|
||||
body = lockUpdate;
|
||||
};
|
||||
};
|
||||
|
||||
# Update lock screen cache only if cache is empty
|
||||
home.activation.updateLockScreenCache =
|
||||
let cacheDir = "${config.homePath}/.cache/betterlockscreen/current";
|
||||
in lib.mkIf config.services.xserver.enable
|
||||
(config.home-manager.users.${config.user}.lib.dag.entryAfter
|
||||
[ "writeBoundary" ] ''
|
||||
if [ ! -d ${cacheDir} ] || [ -z "$(ls ${cacheDir})" ]; then
|
||||
$DRY_RUN_CMD ${lockUpdate}
|
||||
fi
|
||||
'');
|
||||
|
||||
};
|
||||
|
||||
# Ref: https://github.com/betterlockscreen/betterlockscreen/blob/next/system/betterlockscreen%40.service
|
||||
systemd.services.lock = {
|
||||
enable = config.services.xserver.enable;
|
||||
description = "Lock the screen on resume from suspend";
|
||||
before = [ "sleep.target" "suspend.target" ];
|
||||
serviceConfig = {
|
||||
User = config.user;
|
||||
Type = "simple";
|
||||
Environment = "DISPLAY=:0";
|
||||
TimeoutSec = "infinity";
|
||||
ExecStart = lockCmd;
|
||||
ExecStartPost = "${pkgs.coreutils-full}/bin/sleep 1";
|
||||
};
|
||||
wantedBy = [ "sleep.target" "suspend.target" ];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && config.services.xserver.enable) {
|
||||
home-manager.users.${config.user} = {
|
||||
|
||||
services.picom = {
|
||||
enable = true;
|
||||
backend = "glx";
|
||||
settings = {
|
||||
blur = false;
|
||||
blurExclude = [ ];
|
||||
inactiveDim = "0.05";
|
||||
noDNDShadow = false;
|
||||
noDockShadow = false;
|
||||
# shadow-radius = 20
|
||||
# '';
|
||||
# shadow-radius = 20
|
||||
# corner-radius = 10
|
||||
# blur-size = 20
|
||||
# rounded-corners-exclude = [
|
||||
# "window_type = 'dock'",
|
||||
# "class_g = 'i3-frame'"
|
||||
# ]
|
||||
# '';
|
||||
};
|
||||
fade = false;
|
||||
experimentalBackends = true;
|
||||
inactiveOpacity = 1.0;
|
||||
menuOpacity = 1.0;
|
||||
opacityRules = [
|
||||
"0:_NET_WM_STATE@[0]:32a = '_NET_WM_STATE_HIDDEN'" # Hide tabbed windows
|
||||
];
|
||||
shadow = false;
|
||||
shadowExclude = [ ];
|
||||
shadowOffsets = [ (-10) (-10) ];
|
||||
shadowOpacity = 0.5;
|
||||
vSync = true;
|
||||
};
|
||||
|
||||
xsession.windowManager.i3.config.startup = [{
|
||||
command = "systemctl --user restart picom";
|
||||
always = true;
|
||||
notification = false;
|
||||
}];
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && config.services.xserver.enable) {
|
||||
|
||||
toggleBarCommand = "polybar-msg cmd toggle";
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
|
||||
services.polybar = {
|
||||
enable = true;
|
||||
package = pkgs.polybar.override {
|
||||
i3GapsSupport = true;
|
||||
pulseSupport = true;
|
||||
githubSupport = true;
|
||||
};
|
||||
script = "polybar &";
|
||||
config = {
|
||||
"bar/main" = {
|
||||
bottom = false;
|
||||
width = "100%";
|
||||
height = "22pt";
|
||||
radius = 0;
|
||||
# offset-y = -5;
|
||||
# offset-y = "5%";
|
||||
# dpi = 96;
|
||||
background = config.theme.colors.base01;
|
||||
foreground = config.theme.colors.base05;
|
||||
line-size = "3pt";
|
||||
border-top-size = 0;
|
||||
border-right-size = 0;
|
||||
border-left-size = 0;
|
||||
border-bottom-size = "4pt";
|
||||
border-color = config.theme.colors.base00;
|
||||
padding-left = 2;
|
||||
padding-right = 2;
|
||||
module-margin = 1;
|
||||
modules-left = "i3";
|
||||
modules-center = "xwindow";
|
||||
modules-right = "pulseaudio date";
|
||||
cursor-click = "pointer";
|
||||
cursor-scroll = "ns-resize";
|
||||
enable-ipc = true;
|
||||
tray-position = "right";
|
||||
# wm-restack = "generic";
|
||||
# wm-restack = "bspwm";
|
||||
# wm-restack = "i3";
|
||||
# override-redirect = true;
|
||||
};
|
||||
"module/i3" = let padding = 2;
|
||||
in {
|
||||
type = "internal/i3";
|
||||
pin-workspaces = false;
|
||||
show-urgent = true;
|
||||
strip-wsnumbers = true;
|
||||
index-sort = true;
|
||||
enable-click = true;
|
||||
wrapping-scroll = true;
|
||||
fuzzy-match = true;
|
||||
format = "<label-state> <label-mode>";
|
||||
label-focused = "%name%";
|
||||
label-focused-foreground = config.theme.colors.base01;
|
||||
label-focused-background = config.theme.colors.base05;
|
||||
label-focused-underline = config.theme.colors.base03;
|
||||
label-focused-padding = padding;
|
||||
label-unfocused = "%name%";
|
||||
label-unfocused-padding = padding;
|
||||
label-visible = "%name%";
|
||||
label-visible-underline = config.theme.colors.base01;
|
||||
label-visible-padding = padding;
|
||||
label-urgent = "%name%";
|
||||
label-urgent-foreground = config.theme.colors.base00;
|
||||
label-urgent-background = config.theme.colors.base08;
|
||||
label-urgent-underline = config.theme.colors.base0F;
|
||||
label-urgent-padding = padding;
|
||||
};
|
||||
"module/xworkspaces" = {
|
||||
type = "internal/xworkspaces";
|
||||
label-active = "%name%";
|
||||
label-active-background = config.theme.colors.base05;
|
||||
label-active-foreground = config.theme.colors.base01;
|
||||
label-active-underline = config.theme.colors.base03;
|
||||
label-active-padding = 1;
|
||||
label-occupied = "%name%";
|
||||
label-occupied-padding = 1;
|
||||
label-urgent = "%name%";
|
||||
label-urgent-background = config.theme.colors.base08;
|
||||
label-urgent-padding = 1;
|
||||
label-empty = "%name%";
|
||||
label-empty-foreground = config.theme.colors.base06;
|
||||
label-empty-padding = 1;
|
||||
};
|
||||
"module/xwindow" = {
|
||||
type = "internal/xwindow";
|
||||
label = "%title:0:60:...%";
|
||||
};
|
||||
# "module/filesystem" = {
|
||||
# type = "internal/fs";
|
||||
# interval = 25;
|
||||
# mount-0 = "/";
|
||||
# label-mounted = "%{F#F0C674}%mountpoint%%{F-} %percentage_used%%";
|
||||
# label-unmounted = "%mountpoint% not mounted";
|
||||
# label-unmounted-foreground = colors.disabled;
|
||||
# };
|
||||
"module/pulseaudio" = {
|
||||
type = "internal/pulseaudio";
|
||||
# format-volume-prefix = "VOL ";
|
||||
# format-volume-prefix-foreground = colors.primary;
|
||||
format-volume = "<ramp-volume> <label-volume>";
|
||||
# format-volume-background = colors.background;
|
||||
# label-volume-background = colors.background;
|
||||
format-volume-foreground = config.theme.colors.base0B;
|
||||
label-volume = "%percentage%%";
|
||||
label-muted = "ﱝ ---";
|
||||
label-muted-foreground = config.theme.colors.base03;
|
||||
ramp-volume-0 = "";
|
||||
ramp-volume-1 = "墳";
|
||||
ramp-volume-2 = "";
|
||||
};
|
||||
# "module/xkeyboard" = {
|
||||
# type = "internal/xkeyboard";
|
||||
# blacklist-0 = "num lock";
|
||||
# label-layout = "%layout%";
|
||||
# label-layout-foreground = colors.primary;
|
||||
# label-indicator-padding = 2;
|
||||
# label-indicator-margin = 1;
|
||||
# label-indicator-foreground = colors.background;
|
||||
# label-indicator-background = colors.secondary;
|
||||
# };
|
||||
# "module/memory" = {
|
||||
# type = "internal/memory";
|
||||
# interval = 2;
|
||||
# format-prefix = "RAM ";
|
||||
# format-prefix-foreground = colors.primary;
|
||||
# label = "%percentage_used:2%%";
|
||||
# };
|
||||
# "module/cpu" = {
|
||||
# type = "internal/cpu";
|
||||
# interval = 2;
|
||||
# format-prefix = "CPU ";
|
||||
# format-prefix-foreground = colors.primary;
|
||||
# label = "%percentage:2%%";
|
||||
# };
|
||||
# "network-base" = {
|
||||
# type = "internal/network";
|
||||
# interval = 5;
|
||||
# format-connected = "<label-connected>";
|
||||
# format-disconnected = "<label-disconnected>";
|
||||
# label-disconnected = "%{F#F0C674}%ifname%%{F#707880} disconnected";
|
||||
# };
|
||||
# "module/wlan" = {
|
||||
# "inherit" = "network-base";
|
||||
# interface-type = "wireless";
|
||||
# label-connected = "%{F#F0C674}%ifname%%{F-} %essid% %local_ip%";
|
||||
# };
|
||||
# "module/eth" = {
|
||||
# "inherit" = "network-base";
|
||||
# interface-type = "wired";
|
||||
# label-connected = "%{F#F0C674}%ifname%%{F-} %local_ip%";
|
||||
# };
|
||||
"module/date" = {
|
||||
type = "internal/date";
|
||||
interval = 1;
|
||||
date = "%d %b %l:%M %p";
|
||||
date-alt = "%Y-%m-%d %H:%M:%S";
|
||||
label = "%date%";
|
||||
label-foreground = config.theme.colors.base0A;
|
||||
# format-background = colors.background;
|
||||
};
|
||||
"settings" = {
|
||||
screenchange-reload = true;
|
||||
pseudo-transparency = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
xsession.windowManager.i3.config.startup = [{
|
||||
command = "systemctl --user restart polybar";
|
||||
always = true;
|
||||
notification = false;
|
||||
}];
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
}
|
@ -1,153 +0,0 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && config.services.xserver.enable) {
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
|
||||
home.packages = with pkgs;
|
||||
[
|
||||
jq # Required for rofi-systemd
|
||||
];
|
||||
|
||||
programs.rofi = {
|
||||
enable = true;
|
||||
cycle = true;
|
||||
location = "center";
|
||||
pass = { };
|
||||
plugins = [ pkgs.rofi-calc pkgs.rofi-emoji pkgs.rofi-systemd ];
|
||||
theme = let
|
||||
inherit (config.home-manager.users.${config.user}.lib.formats.rasi)
|
||||
mkLiteral;
|
||||
in {
|
||||
|
||||
# Inspired by https://github.com/sherubthakur/dotfiles/blob/master/users/modules/desktop-environment/rofi/launcher.rasi
|
||||
|
||||
"*" = {
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
foreground-color = mkLiteral config.theme.colors.base07;
|
||||
text-color = mkLiteral config.theme.colors.base07;
|
||||
border-color = mkLiteral config.theme.colors.base04;
|
||||
};
|
||||
|
||||
# Holds the entire window
|
||||
"#window" = {
|
||||
transparency = "real";
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
text-color = mkLiteral config.theme.colors.base07;
|
||||
border = mkLiteral "4px";
|
||||
border-color = mkLiteral config.theme.colors.base04;
|
||||
border-radius = mkLiteral "4px";
|
||||
width = mkLiteral "850px";
|
||||
padding = mkLiteral "15px";
|
||||
};
|
||||
|
||||
# Wrapper around bar and results
|
||||
"#mainbox" = {
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
border = mkLiteral "0px";
|
||||
border-radius = mkLiteral "0px";
|
||||
border-color = mkLiteral config.theme.colors.base04;
|
||||
children = map mkLiteral [ "inputbar" "listview" ];
|
||||
spacing = mkLiteral "10px";
|
||||
padding = mkLiteral "10px";
|
||||
};
|
||||
|
||||
# Unknown
|
||||
"#textbox-prompt-colon" = {
|
||||
expand = false;
|
||||
str = ":";
|
||||
margin = mkLiteral "0px 0.3em 0em 0em";
|
||||
text-color = mkLiteral config.theme.colors.base07;
|
||||
};
|
||||
|
||||
# Command prompt left of the input
|
||||
"#prompt" = { enabled = false; };
|
||||
|
||||
# Actual text box
|
||||
"#entry" = {
|
||||
placeholder-color = mkLiteral config.theme.colors.base03;
|
||||
expand = true;
|
||||
horizontal-align = "0";
|
||||
placeholder = "Launch Program";
|
||||
padding = mkLiteral "0px 0px 0px 5px";
|
||||
blink = true;
|
||||
};
|
||||
|
||||
# Top bar
|
||||
"#inputbar" = {
|
||||
children = map mkLiteral [ "prompt" "entry" ];
|
||||
border = mkLiteral "1px";
|
||||
border-radius = mkLiteral "4px";
|
||||
padding = mkLiteral "6px";
|
||||
};
|
||||
|
||||
# Results
|
||||
"#listview" = {
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
padding = mkLiteral "0px";
|
||||
columns = 1;
|
||||
lines = 12;
|
||||
spacing = "5px";
|
||||
cycle = true;
|
||||
dynamic = true;
|
||||
layout = "vertical";
|
||||
};
|
||||
|
||||
# Each result
|
||||
"#element" = {
|
||||
orientation = "vertical";
|
||||
border-radius = mkLiteral "0px";
|
||||
padding = mkLiteral "5px 0px 5px 5px";
|
||||
};
|
||||
"#element.selected" = {
|
||||
border = mkLiteral "1px";
|
||||
border-radius = mkLiteral "4px";
|
||||
border-color = mkLiteral config.theme.colors.base07;
|
||||
background-color = mkLiteral config.theme.colors.base04;
|
||||
text-color = mkLiteral config.theme.colors.base00;
|
||||
};
|
||||
|
||||
"#element-text" = {
|
||||
expand = true;
|
||||
# horizontal-align = mkLiteral "0.5";
|
||||
vertical-align = mkLiteral "0.5";
|
||||
margin = mkLiteral "0px 2.5px 0px 2.5px";
|
||||
};
|
||||
"#element-text.selected" = {
|
||||
background-color = mkLiteral config.theme.colors.base04;
|
||||
text-color = mkLiteral config.theme.colors.base00;
|
||||
};
|
||||
|
||||
# Not sure how to get icons
|
||||
"#element-icon" = {
|
||||
size = mkLiteral "18px";
|
||||
border = mkLiteral "0px";
|
||||
padding = mkLiteral "2px 5px 2px 2px";
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
};
|
||||
"#element-icon.selected" = {
|
||||
background-color = mkLiteral config.theme.colors.base04;
|
||||
text-color = mkLiteral config.theme.colors.base00;
|
||||
};
|
||||
|
||||
};
|
||||
xoffset = 0;
|
||||
yoffset = -20;
|
||||
extraConfig = {
|
||||
show-icons = true;
|
||||
kb-cancel = "Escape,Super+space";
|
||||
modi = "window,run,ssh,emoji,calc,systemd";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
launcherCommand = "${pkgs.rofi}/bin/rofi -show run -modi run";
|
||||
systemdSearch = "${pkgs.rofi-systemd}/bin/rofi-systemd";
|
||||
altTabCommand = "${pkgs.rofi}/bin/rofi -show window -modi window";
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# THEME="$HOME/.config/rofi/config.rasi"
|
||||
|
||||
ICON_UP=""
|
||||
ICON_DOWN=""
|
||||
ICON_OPT=""
|
||||
options="$ICON_UP\n$ICON_OPT\n$ICON_DOWN"
|
||||
chosen="$(echo -e "$options" | rofi -theme-str 'listview { layout:horizontal; }' -dmenu)"
|
||||
echo "$chosen"
|
@ -1,85 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options = {
|
||||
gtk.theme = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Theme name for GTK applications";
|
||||
};
|
||||
package = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Theme package name for GTK applications";
|
||||
default = "gnome-themes-extra";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = let
|
||||
|
||||
gtkTheme = {
|
||||
name = config.gtk.theme.name;
|
||||
package = pkgs."${config.gtk.theme.package}";
|
||||
};
|
||||
|
||||
in lib.mkIf (pkgs.stdenv.isLinux && config.gui.enable) {
|
||||
|
||||
# Enable the X11 windowing system.
|
||||
services.xserver = {
|
||||
enable = config.gui.enable;
|
||||
|
||||
# Enable touchpad support
|
||||
libinput.enable = true;
|
||||
|
||||
# Login screen
|
||||
displayManager = {
|
||||
lightdm = {
|
||||
enable = config.services.xserver.enable;
|
||||
background = config.wallpaper;
|
||||
|
||||
# Make the login screen dark
|
||||
greeters.gtk.theme = gtkTheme;
|
||||
|
||||
# Show default user
|
||||
extraSeatDefaults = ''
|
||||
greeter-hide-users = false
|
||||
'';
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs;
|
||||
[
|
||||
xclip # Clipboard
|
||||
];
|
||||
|
||||
# Required for setting GTK theme (for preferred-color-scheme in browser)
|
||||
services.dbus.packages = [ pkgs.dconf ];
|
||||
programs.dconf.enable = true;
|
||||
|
||||
environment.sessionVariables = { GTK_THEME = config.gtk.theme.name; };
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
|
||||
programs.fish.shellAliases = {
|
||||
pbcopy = "xclip -selection clipboard -in";
|
||||
pbpaste = "xclip -selection clipboard -out";
|
||||
};
|
||||
|
||||
gtk = let
|
||||
gtkExtraConfig = {
|
||||
gtk-application-prefer-dark-theme = config.theme.dark;
|
||||
};
|
||||
in {
|
||||
enable = true;
|
||||
theme = gtkTheme;
|
||||
gtk3.extraConfig = gtkExtraConfig;
|
||||
gtk4.extraConfig = gtkExtraConfig;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
|
||||
# These micro-scripts change the volume while also triggering the volume
|
||||
# notification widget
|
||||
|
||||
increaseVolume = pkgs.writeShellScriptBin "increaseVolume" ''
|
||||
${pkgs.pamixer}/bin/pamixer -i 2
|
||||
volume=$(${pkgs.pamixer}/bin/pamixer --get-volume)
|
||||
${pkgs.volnoti}/bin/volnoti-show $volume
|
||||
'';
|
||||
|
||||
decreaseVolume = pkgs.writeShellScriptBin "decreaseVolume" ''
|
||||
${pkgs.pamixer}/bin/pamixer -d 2
|
||||
volume=$(${pkgs.pamixer}/bin/pamixer --get-volume)
|
||||
${pkgs.volnoti}/bin/volnoti-show $volume
|
||||
'';
|
||||
|
||||
toggleMute = pkgs.writeShellScriptBin "toggleMute" ''
|
||||
${pkgs.pamixer}/bin/pamixer --toggle-mute
|
||||
mute=$(${pkgs.pamixer}/bin/pamixer --get-mute)
|
||||
if [ "$mute" == "true" ]; then
|
||||
${pkgs.volnoti}/bin/volnoti-show --mute
|
||||
else
|
||||
volume=$(${pkgs.pamixer}/bin/pamixer --get-volume)
|
||||
${pkgs.volnoti}/bin/volnoti-show $volume
|
||||
fi
|
||||
'';
|
||||
|
||||
in {
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && config.gui.enable) {
|
||||
sound.enable = true;
|
||||
|
||||
# Enable PipeWire
|
||||
services.pipewire = {
|
||||
enable = true;
|
||||
pulse.enable = true;
|
||||
};
|
||||
|
||||
# Provides audio source with background noise filtered
|
||||
programs.noisetorch.enable = true;
|
||||
|
||||
# These aren't necessary, but helpful for the user
|
||||
environment.systemPackages = with pkgs; [
|
||||
pamixer # Audio control
|
||||
volnoti # Volume notifications
|
||||
];
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
|
||||
# Graphical volume notifications
|
||||
services.volnoti.enable = true;
|
||||
|
||||
xsession.windowManager.i3.config = {
|
||||
|
||||
# Make sure that Volnoti actually starts (home-manager doesn't start
|
||||
# user daemon's automatically)
|
||||
startup = [{
|
||||
command =
|
||||
"systemctl --user restart volnoti --alpha 0.15 --radius 40 --timeout 0.2";
|
||||
always = true;
|
||||
notification = false;
|
||||
}];
|
||||
|
||||
# i3 keybinds for changing the volume
|
||||
keybindings = {
|
||||
"XF86AudioRaiseVolume" =
|
||||
"exec --no-startup-id ${increaseVolume}/bin/increaseVolume";
|
||||
"XF86AudioLowerVolume" =
|
||||
"exec --no-startup-id ${decreaseVolume}/bin/decreaseVolume";
|
||||
"XF86AudioMute" = "exec --no-startup-id ${toggleMute}/bin/toggleMute";
|
||||
# We can mute the mic by adding "--default-source"
|
||||
"XF86AudioMicMute" =
|
||||
"exec --no-startup-id ${pkgs.pamixer}/bin/pamixer --default-source --toggle-mute";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
boot.loader = lib.mkIf (config.physical && pkgs.stdenv.isLinux) {
|
||||
grub = {
|
||||
enable = true;
|
||||
|
||||
# Not sure what this does, but it involves the UEFI/BIOS
|
||||
efiSupport = true;
|
||||
|
||||
# Check for other OSes and make them available
|
||||
useOSProber = true;
|
||||
|
||||
# Attempt to display GRUB on widescreen monitor
|
||||
gfxmodeEfi = "1920x1080";
|
||||
|
||||
# Install GRUB onto the boot disk
|
||||
# device = config.fileSystems."/boot".device;
|
||||
|
||||
# Don't install GRUB, required for UEFI?
|
||||
device = "nodev";
|
||||
|
||||
# Display menu indefinitely if holding shift key
|
||||
extraConfig = ''
|
||||
if keystatus --shift ; then
|
||||
set timeout=-1
|
||||
else
|
||||
set timeout=0
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
# Always display menu indefinitely; default is 5 seconds
|
||||
# timeout = null;
|
||||
|
||||
# Allows GRUB to interact with the UEFI/BIOS I guess
|
||||
efi.canTouchEfiVariables = true;
|
||||
};
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
{ lib, ... }: {
|
||||
|
||||
imports = [
|
||||
./audio.nix
|
||||
./boot.nix
|
||||
./keyboard.nix
|
||||
./monitors.nix
|
||||
./mouse.nix
|
||||
./networking.nix
|
||||
./sleep.nix
|
||||
./wifi.nix
|
||||
];
|
||||
|
||||
options = {
|
||||
physical = lib.mkEnableOption "Whether this machine is a physical device.";
|
||||
server = lib.mkEnableOption "Whether this machine is a server.";
|
||||
};
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
{ ... }: {
|
||||
|
||||
services.xserver = {
|
||||
|
||||
layout = "us";
|
||||
|
||||
# Keyboard responsiveness
|
||||
autoRepeatDelay = 250;
|
||||
autoRepeatInterval = 40;
|
||||
|
||||
# Swap escape key with caps lock key
|
||||
xkbOptions = "eurosign:e,caps:swapescape";
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
# Timezone required for Redshift schedule
|
||||
imports = [ ../nixos/timezone.nix ];
|
||||
|
||||
config =
|
||||
lib.mkIf (config.gui.enable && config.physical && pkgs.stdenv.isLinux) {
|
||||
|
||||
environment.systemPackages = with pkgs;
|
||||
[
|
||||
ddcutil # Monitor brightness control
|
||||
];
|
||||
|
||||
# Reduce blue light at night
|
||||
services.redshift = {
|
||||
enable = true;
|
||||
brightness = {
|
||||
day = "1.0";
|
||||
night = "1.0";
|
||||
};
|
||||
};
|
||||
|
||||
# Detect monitors (brightness) for ddcutil
|
||||
hardware.i2c.enable = true;
|
||||
|
||||
# Grant main user access to external monitors
|
||||
users.users.${config.user}.extraGroups = [ "i2c" ];
|
||||
|
||||
services.xserver.displayManager = {
|
||||
|
||||
# Put the login screen on the left monitor
|
||||
lightdm.greeters.gtk.extraConfig = ''
|
||||
active-monitor=0
|
||||
'';
|
||||
|
||||
# Set up screen position and rotation
|
||||
setupCommands = ''
|
||||
${pkgs.xorg.xrandr}/bin/xrandr --output DisplayPort-0 \
|
||||
--mode 1920x1200 \
|
||||
--pos 1920x0 \
|
||||
--rotate left \
|
||||
--output HDMI-0 \
|
||||
--primary \
|
||||
--mode 1920x1080 \
|
||||
--pos 0x560 \
|
||||
--rotate normal \
|
||||
--output DVI-0 --off \
|
||||
--output DVI-1 --off \
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
config =
|
||||
lib.mkIf (config.gui.enable && config.physical && pkgs.stdenv.isLinux) {
|
||||
|
||||
# Mouse customization
|
||||
services.ratbagd.enable = true;
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
libratbag # Mouse adjustments
|
||||
piper # Mouse adjustments GUI
|
||||
];
|
||||
|
||||
services.xserver.libinput.mouse = {
|
||||
# Disable mouse acceleration
|
||||
accelProfile = "flat";
|
||||
accelSpeed = "1.15";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
config = lib.mkIf (config.physical && pkgs.stdenv.isLinux) {
|
||||
|
||||
# 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.enp0s31f6.useDHCP = true;
|
||||
networking.interfaces.wlp3s0.useDHCP = true;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && config.server) {
|
||||
|
||||
# Servers need a bootloader or they won't start
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
config = lib.mkIf (config.physical && pkgs.stdenv.isLinux) {
|
||||
|
||||
# Prevent wake from keyboard
|
||||
powerManagement.powerDownCommands = ''
|
||||
for wakeup in /sys/bus/usb/devices/1-*/power/wakeup; do echo disabled > $wakeup; done
|
||||
'';
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
config = lib.mkIf (config.physical && config.isLinux) {
|
||||
|
||||
# Enables wireless support via wpa_supplicant.
|
||||
networking.wireless.enable = true;
|
||||
|
||||
# Allows the user to control the WiFi settings.
|
||||
networking.wireless.userControlled.enable = true;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
imports = [ ./user.nix ./timezone.nix ./doas.nix ];
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isLinux {
|
||||
|
||||
# Pin a state version to prevent warnings
|
||||
system.stateVersion =
|
||||
config.home-manager.users.${config.user}.home.stateVersion;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
# Replace sudo with doas
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isLinux {
|
||||
|
||||
security = {
|
||||
|
||||
# Remove sudo
|
||||
sudo.enable = false;
|
||||
|
||||
# Add doas
|
||||
doas = {
|
||||
enable = true;
|
||||
|
||||
# No password required
|
||||
wheelNeedsPassword = false;
|
||||
|
||||
# Pass environment variables from user to root
|
||||
# Also requires removing password here
|
||||
extraRules = [{
|
||||
groups = [ "wheel" ];
|
||||
noPass = true;
|
||||
keepEnv = true;
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
home-manager.users.${config.user}.programs.fish.shellAliases = {
|
||||
sudo = "doas";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isLinux {
|
||||
|
||||
# Service to determine location for time zone
|
||||
services.geoclue2.enable = true;
|
||||
services.geoclue2.enableWifi = false; # Breaks when it can't connect
|
||||
location = { provider = "geoclue2"; };
|
||||
|
||||
# Enable local time based on time zone
|
||||
services.localtimed.enable = true;
|
||||
|
||||
# Required to get localtimed to talk to geoclue2
|
||||
services.geoclue2.appConfig.localtimed.isSystem = true;
|
||||
services.geoclue2.appConfig.localtimed.isAllowed = true;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options = {
|
||||
|
||||
passwordHash = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
description = "Password created with mkpasswd -m sha-512";
|
||||
default = null;
|
||||
# Test it by running: mkpasswd -m sha-512 --salt "PZYiMGmJIIHAepTM"
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux) {
|
||||
|
||||
# Allows us to declaritively set password
|
||||
users.mutableUsers = false;
|
||||
|
||||
# Define a user account. Don't forget to set a password with ‘passwd’.
|
||||
users.users.${config.user} = {
|
||||
|
||||
# Create a home directory for human user
|
||||
isNormalUser = true;
|
||||
|
||||
# Automatically create a password to start
|
||||
hashedPassword = config.passwordHash;
|
||||
|
||||
extraGroups = [
|
||||
"wheel" # Sudo privileges
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
home-manager.users.${config.user}.xdg = {
|
||||
userDirs = {
|
||||
enable = true;
|
||||
createDirectories = true;
|
||||
documents = "$HOME/documents";
|
||||
download = config.userDirs.download;
|
||||
music = "$HOME/media/music";
|
||||
pictures = "$HOME/media/images";
|
||||
videos = "$HOME/media/videos";
|
||||
desktop = "$HOME/other/desktop";
|
||||
publicShare = "$HOME/other/public";
|
||||
templates = "$HOME/other/templates";
|
||||
extraConfig = { XDG_DEV_DIR = "$HOME/dev"; };
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options = {
|
||||
|
||||
backup.s3 = {
|
||||
endpoint = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "S3 endpoint for backups";
|
||||
default = null;
|
||||
};
|
||||
bucket = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "S3 bucket for backups";
|
||||
default = null;
|
||||
};
|
||||
accessKeyId = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "S3 access key ID for backups";
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = {
|
||||
|
||||
users.groups.backup = { };
|
||||
|
||||
secrets.backup = {
|
||||
source = ../../private/backup.age;
|
||||
dest = "${config.secretsDirectory}/backup";
|
||||
group = "backup";
|
||||
permissions = "0440";
|
||||
};
|
||||
|
||||
users.users.litestream.extraGroups = [ "backup" ];
|
||||
|
||||
services.litestream = {
|
||||
enable = true;
|
||||
environmentFile = config.secrets.backup.dest;
|
||||
};
|
||||
|
||||
# Wait for secret to exist
|
||||
systemd.services.litestream = {
|
||||
after = [ "backup-secret.service" ];
|
||||
requires = [ "backup-secret.service" ];
|
||||
environment.AWS_ACCESS_KEY_ID = config.backupS3.accessKeyId;
|
||||
};
|
||||
|
||||
# # Backup library to object storage
|
||||
# services.restic.backups.calibre = {
|
||||
# user = "calibre-web";
|
||||
# repository =
|
||||
# "s3://${config.backupS3.endpoint}/${config.backupS3.bucket}/calibre";
|
||||
# paths = [
|
||||
# "/var/books"
|
||||
# "/var/lib/calibre-web/app.db"
|
||||
# "/var/lib/calibre-web/gdrive.db"
|
||||
# ];
|
||||
# initialize = true;
|
||||
# timerConfig = { OnCalendar = "00:05:00"; };
|
||||
# environmentFile = backupS3File;
|
||||
# };
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options = {
|
||||
caddy.enable = lib.mkEnableOption "Caddy reverse proxy.";
|
||||
caddy.routes = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.attrs;
|
||||
description = "Caddy JSON routes for http servers";
|
||||
default = [ ];
|
||||
};
|
||||
caddy.blocks = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.attrs;
|
||||
description = "Caddy JSON error blocks for http servers";
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.caddy.enable && config.caddy.routes != [ ]) {
|
||||
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
adapter = "''"; # Required to enable JSON
|
||||
configFile = pkgs.writeText "Caddyfile" (builtins.toJSON {
|
||||
apps.http.servers.main = {
|
||||
listen = [ ":443" ];
|
||||
routes = config.caddy.routes;
|
||||
errors.routes = config.caddy.blocks;
|
||||
};
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||
networking.firewall.allowedUDPPorts = [ 443 ];
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options = {
|
||||
bookServer = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Hostname for Calibre library";
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.bookServer != null) {
|
||||
|
||||
services.calibre-web = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
options = {
|
||||
reverseProxyAuth.enable = false;
|
||||
enableBookConversion = true;
|
||||
enableBookUploading = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Fix: https://github.com/janeczku/calibre-web/issues/2422
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
calibre-web = prev.calibre-web.overrideAttrs (old: {
|
||||
patches = (old.patches or [ ])
|
||||
++ [ ../../patches/calibre-web-cloudflare.patch ];
|
||||
});
|
||||
})
|
||||
];
|
||||
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.bookServer ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = "localhost:8083"; }];
|
||||
headers.request.add."X-Script-Name" = [ "/calibre-web" ];
|
||||
}];
|
||||
}];
|
||||
|
||||
# Run a backup on a schedule
|
||||
systemd.timers.calibre-backup = {
|
||||
timerConfig = {
|
||||
OnCalendar = "*-*-* 00:00:00"; # Once per day
|
||||
Unit = "calibre-backup.service";
|
||||
};
|
||||
wantedBy = [ "timers.target" ];
|
||||
};
|
||||
|
||||
# Backup Calibre data to object storage
|
||||
systemd.services.calibre-backup =
|
||||
let libraryPath = "/var/lib/calibre-web"; # Default location
|
||||
in {
|
||||
description = "Backup Calibre data";
|
||||
environment.AWS_ACCESS_KEY_ID = config.backupS3.accessKeyId;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "calibre-web";
|
||||
Group = "backup";
|
||||
EnvironmentFile = config.secrets.backup.dest;
|
||||
};
|
||||
script = ''
|
||||
${pkgs.awscli2}/bin/aws s3 sync \
|
||||
${libraryPath}/ \
|
||||
s3://${config.backupS3.bucket}/calibre/ \
|
||||
--endpoint-url=https://${config.backupS3.endpoint}
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
# This module is necessary for hosts that are serving through Cloudflare.
|
||||
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
|
||||
cloudflareIpRanges = [
|
||||
|
||||
# Cloudflare IPv4: https://www.cloudflare.com/ips-v4
|
||||
"173.245.48.0/20"
|
||||
"103.21.244.0/22"
|
||||
"103.22.200.0/22"
|
||||
"103.31.4.0/22"
|
||||
"141.101.64.0/18"
|
||||
"108.162.192.0/18"
|
||||
"190.93.240.0/20"
|
||||
"188.114.96.0/20"
|
||||
"197.234.240.0/22"
|
||||
"198.41.128.0/17"
|
||||
"162.158.0.0/15"
|
||||
"104.16.0.0/13"
|
||||
"104.24.0.0/14"
|
||||
"172.64.0.0/13"
|
||||
"131.0.72.0/22"
|
||||
|
||||
# Cloudflare IPv6: https://www.cloudflare.com/ips-v6
|
||||
"2400:cb00::/32"
|
||||
"2606:4700::/32"
|
||||
"2803:f800::/32"
|
||||
"2405:b500::/32"
|
||||
"2405:8100::/32"
|
||||
"2a06:98c0::/29"
|
||||
"2c0f:f248::/32"
|
||||
|
||||
];
|
||||
|
||||
in {
|
||||
|
||||
options.cloudflare.enable = lib.mkEnableOption "Use Cloudflare.";
|
||||
|
||||
config = lib.mkIf config.cloudflare.enable {
|
||||
|
||||
# Forces Caddy to error if coming from a non-Cloudflare IP
|
||||
caddy.blocks = [{
|
||||
match = [{ not = [{ remote_ip.ranges = cloudflareIpRanges; }]; }];
|
||||
handle = [{
|
||||
handler = "static_response";
|
||||
abort = true;
|
||||
}];
|
||||
}];
|
||||
|
||||
# Allows Nextcloud to trust Cloudflare IPs
|
||||
services.nextcloud.config.trustedProxies = cloudflareIpRanges;
|
||||
|
||||
};
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
{ ... }: {
|
||||
|
||||
imports = [
|
||||
./backups.nix
|
||||
./caddy.nix
|
||||
./calibre.nix
|
||||
./cloudflare.nix
|
||||
./gitea.nix
|
||||
./gnupg.nix
|
||||
./honeypot.nix
|
||||
./jellyfin.nix
|
||||
./keybase.nix
|
||||
./mullvad.nix
|
||||
./n8n.nix
|
||||
./netdata.nix
|
||||
./nextcloud.nix
|
||||
./prometheus.nix
|
||||
./secrets.nix
|
||||
./sshd.nix
|
||||
./transmission.nix
|
||||
./vaultwarden.nix
|
||||
./wireguard.nix
|
||||
];
|
||||
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
let giteaPath = "/var/lib/gitea"; # Default service directory
|
||||
|
||||
in {
|
||||
|
||||
options = {
|
||||
|
||||
giteaServer = lib.mkOption {
|
||||
description = "Hostname for Gitea.";
|
||||
type = lib.types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.giteaServer != null) {
|
||||
services.gitea = {
|
||||
enable = true;
|
||||
httpPort = 3001;
|
||||
httpAddress = "127.0.0.1";
|
||||
rootUrl = "https://${config.giteaServer}/";
|
||||
database.type = "sqlite3";
|
||||
settings = {
|
||||
repository = {
|
||||
DEFAULT_PUSH_CREATE_PRIVATE = true;
|
||||
DISABLE_HTTP_GIT = false;
|
||||
ACCESS_CONTROL_ALLOW_ORIGIN = config.giteaServer;
|
||||
ENABLE_PUSH_CREATE_USER = true;
|
||||
ENABLE_PUSH_CREATE_ORG = true;
|
||||
DEFAULT_BRANCH = "main";
|
||||
};
|
||||
server = {
|
||||
SSH_PORT = 22;
|
||||
START_SSH_SERVER = false; # Use sshd instead
|
||||
DISABLE_SSH = false;
|
||||
# SSH_LISTEN_HOST = "0.0.0.0";
|
||||
# SSH_LISTEN_PORT = 122;
|
||||
};
|
||||
service.DISABLE_REGISTRATION = true;
|
||||
session.COOKIE_SECURE = true;
|
||||
ui.SHOW_USER_EMAIL = false;
|
||||
};
|
||||
extraConfig = null;
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 122 ];
|
||||
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.giteaServer ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = "localhost:3001"; }];
|
||||
}];
|
||||
}];
|
||||
|
||||
## Backup config
|
||||
|
||||
# Open to groups, allowing for backups
|
||||
systemd.services.gitea.serviceConfig.StateDirectoryMode =
|
||||
lib.mkForce "0770";
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${giteaPath}/data 0775 gitea gitea"
|
||||
"f ${giteaPath}/data/gitea.db 0660 gitea gitea"
|
||||
];
|
||||
|
||||
# Allow litestream and gitea to share a sqlite database
|
||||
users.users.litestream.extraGroups = [ "gitea" ];
|
||||
users.users.gitea.extraGroups = [ "litestream" ];
|
||||
|
||||
# Backup sqlite database with litestream
|
||||
services.litestream = {
|
||||
settings = {
|
||||
dbs = [{
|
||||
path = "${giteaPath}/data/gitea.db";
|
||||
replicas = [{
|
||||
url =
|
||||
"s3://${config.backupS3.bucket}.${config.backupS3.endpoint}/gitea";
|
||||
}];
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
# Don't start litestream unless gitea is up
|
||||
systemd.services.litestream = {
|
||||
after = [ "gitea.service" ];
|
||||
requires = [ "gitea.service" ];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options.gpg.enable = lib.mkEnableOption "GnuPG encryption.";
|
||||
|
||||
config.home-manager.users.${config.user} = lib.mkIf config.gpg.enable {
|
||||
programs.gpg.enable = true;
|
||||
services.gpg-agent = {
|
||||
enable = true;
|
||||
defaultCacheTtl = 86400; # Resets when used
|
||||
defaultCacheTtlSsh = 86400; # Resets when used
|
||||
maxCacheTtl = 34560000; # Can never reset
|
||||
maxCacheTtlSsh = 34560000; # Can never reset
|
||||
pinentryFlavor = "tty";
|
||||
};
|
||||
home = lib.mkIf config.gui.enable { packages = with pkgs; [ pinentry ]; };
|
||||
};
|
||||
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
# Currently has some issues that don't make this viable.
|
||||
|
||||
# Taken from:
|
||||
# https://dataswamp.org/~solene/2022-09-29-iblock-implemented-in-nixos.html
|
||||
|
||||
# You will need to flush all rules when removing:
|
||||
# https://serverfault.com/questions/200635/best-way-to-clear-all-iptables-rules
|
||||
|
||||
let
|
||||
|
||||
portsToBlock = [ 25545 25565 25570 ];
|
||||
portsString =
|
||||
builtins.concatStringsSep "," (builtins.map builtins.toString portsToBlock);
|
||||
|
||||
# Block IPs for 20 days
|
||||
expire = 60 * 60 * 24 * 20;
|
||||
|
||||
rules = table: [
|
||||
"INPUT -i eth0 -p tcp -m multiport --dports ${portsString} -m state --state NEW -m recent --set"
|
||||
"INPUT -i eth0 -p tcp -m multiport --dports ${portsString} -m state --state NEW -m recent --update --seconds 10 --hitcount 1 -j SET --add-set ${table} src"
|
||||
"INPUT -i eth0 -p tcp -m set --match-set ${table} src -j nixos-fw-refuse"
|
||||
"INPUT -i eth0 -p udp -m set --match-set ${table} src -j nixos-fw-refuse"
|
||||
];
|
||||
|
||||
create-rules = lib.concatStringsSep "\n"
|
||||
(builtins.map (rule: "iptables -C " + rule + " || iptables -A " + rule)
|
||||
(rules "blocked") ++ builtins.map
|
||||
(rule: "ip6tables -C " + rule + " || ip6tables -A " + rule)
|
||||
(rules "blocked6"));
|
||||
|
||||
delete-rules = lib.concatStringsSep "\n"
|
||||
(builtins.map (rule: "iptables -C " + rule + " && iptables -D " + rule)
|
||||
(rules "blocked") ++ builtins.map
|
||||
(rule: "ip6tables -C " + rule + " && ip6tables -D " + rule)
|
||||
(rules "blocked6"));
|
||||
|
||||
in {
|
||||
|
||||
options.honeypot.enable = lib.mkEnableOption "Honeypot fail2ban system.";
|
||||
|
||||
config.networking.firewall = lib.mkIf config.honeypot.enable {
|
||||
|
||||
extraPackages = [ pkgs.ipset ];
|
||||
# allowedTCPPorts = portsToBlock;
|
||||
|
||||
# Restore ban list when starting up
|
||||
extraCommands = ''
|
||||
if test -f /var/lib/ipset.conf
|
||||
then
|
||||
ipset restore -! < /var/lib/ipset.conf
|
||||
else
|
||||
ipset -exist create blocked hash:ip ${
|
||||
if expire > 0 then "timeout ${toString expire}" else ""
|
||||
}
|
||||
ipset -exist create blocked6 hash:ip family inet6 ${
|
||||
if expire > 0 then "timeout ${toString expire}" else ""
|
||||
}
|
||||
fi
|
||||
${create-rules}
|
||||
'';
|
||||
|
||||
# Save list when shutting down
|
||||
extraStopCommands = ''
|
||||
ipset -exist create blocked hash:ip ${
|
||||
if expire > 0 then "timeout ${toString expire}" else ""
|
||||
}
|
||||
ipset -exist create blocked6 hash:ip family inet6 ${
|
||||
if expire > 0 then "timeout ${toString expire}" else ""
|
||||
}
|
||||
ipset save > /var/lib/ipset.conf
|
||||
${delete-rules}
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
{ config, lib, ... }: {
|
||||
|
||||
options = {
|
||||
streamServer = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Hostname for Jellyfin library";
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.streamServer != null) {
|
||||
|
||||
services.jellyfin.enable = true;
|
||||
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.streamServer ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = "localhost:8096"; }];
|
||||
}];
|
||||
}];
|
||||
|
||||
# Create videos directory, allow anyone in Jellyfin group to manage it
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/jellyfin 0775 jellyfin jellyfin"
|
||||
"d /var/lib/jellyfin/library 0775 jellyfin jellyfin"
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options.keybase.enable = lib.mkEnableOption "Keybase.";
|
||||
|
||||
config = lib.mkIf config.keybase.enable {
|
||||
|
||||
services.keybase.enable = true;
|
||||
services.kbfs = {
|
||||
enable = true;
|
||||
# enableRedirector = true;
|
||||
mountPoint = "/run/user/1000/keybase/kbfs";
|
||||
};
|
||||
security.wrappers.keybase-redirector = {
|
||||
setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
source = "${pkgs.kbfs}/bin/redirector";
|
||||
};
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
home.packages = [ (lib.mkIf config.gui.enable pkgs.keybase-gui) ];
|
||||
home.file = let
|
||||
ignorePatterns = ''
|
||||
keybase/
|
||||
kbfs/'';
|
||||
in {
|
||||
".rgignore".text = ignorePatterns;
|
||||
".fdignore".text = ignorePatterns;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options.mullvad.enable = lib.mkEnableOption "Mullvad VPN.";
|
||||
|
||||
config = lib.mkIf config.mullvad.enable {
|
||||
|
||||
services.mullvad-vpn.enable = true;
|
||||
environment.systemPackages = [ pkgs.mullvad-vpn ];
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options = {
|
||||
n8nServer = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Hostname for n8n automation";
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.n8nServer != null) {
|
||||
|
||||
services.n8n = {
|
||||
enable = true;
|
||||
settings = {
|
||||
n8n = {
|
||||
listenAddress = "127.0.0.1";
|
||||
port = 5678;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.n8nServer ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = "localhost:5678"; }];
|
||||
}];
|
||||
}];
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options.netdata.enable = lib.mkEnableOption "Netdata metrics.";
|
||||
|
||||
config = lib.mkIf config.netdata.enable {
|
||||
|
||||
services.netdata = {
|
||||
enable = true;
|
||||
|
||||
# Disable local dashboard (unsecured)
|
||||
config = { web.mode = "none"; };
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options = {
|
||||
|
||||
nextcloudServer = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Hostname for Nextcloud";
|
||||
default = null;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.nextcloudServer != null) {
|
||||
|
||||
services.nextcloud = {
|
||||
enable = true;
|
||||
package = pkgs.nextcloud25; # Required to specify
|
||||
https = true;
|
||||
hostName = "localhost";
|
||||
maxUploadSize = "50G";
|
||||
config = {
|
||||
adminpassFile = config.secrets.nextcloud.dest;
|
||||
extraTrustedDomains = [ config.nextcloudServer ];
|
||||
};
|
||||
};
|
||||
|
||||
# Don't let Nginx use main ports (using Caddy instead)
|
||||
services.nginx.virtualHosts."localhost".listen = [{
|
||||
addr = "127.0.0.1";
|
||||
port = 8080;
|
||||
}];
|
||||
|
||||
# Point Caddy to Nginx
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.nextcloudServer ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = "localhost:8080"; }];
|
||||
}];
|
||||
}];
|
||||
|
||||
# Create credentials file for nextcloud
|
||||
secrets.nextcloud = {
|
||||
source = ../../private/nextcloud.age;
|
||||
dest = "${config.secretsDirectory}/nextcloud";
|
||||
owner = "nextcloud";
|
||||
group = "nextcloud";
|
||||
permissions = "0440";
|
||||
};
|
||||
systemd.services.nextcloud-secret = {
|
||||
requiredBy = [ "nextcloud-setup.service" ];
|
||||
before = [ "nextcloud-setup.service" ];
|
||||
};
|
||||
|
||||
## Backup config
|
||||
|
||||
# Open to groups, allowing for backups
|
||||
systemd.services.phpfpm-nextcloud.serviceConfig.StateDirectoryMode =
|
||||
lib.mkForce "0770";
|
||||
|
||||
# Allow litestream and nextcloud to share a sqlite database
|
||||
users.users.litestream.extraGroups = [ "nextcloud" ];
|
||||
users.users.nextcloud.extraGroups = [ "litestream" ];
|
||||
|
||||
# Backup sqlite database with litestream
|
||||
services.litestream = {
|
||||
settings = {
|
||||
dbs = [{
|
||||
path = "${config.services.nextcloud.datadir}/data/nextcloud.db";
|
||||
replicas = [{
|
||||
url =
|
||||
"s3://${config.backupS3.bucket}.${config.backupS3.endpoint}/nextcloud";
|
||||
}];
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
# Don't start litestream unless nextcloud is up
|
||||
systemd.services.litestream = {
|
||||
after = [ "phpfpm-nextcloud.service" ];
|
||||
requires = [ "phpfpm-nextcloud.service" ];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options.metricsServer = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Hostname of the Grafana server.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.metricsServer != null) {
|
||||
|
||||
services.grafana.enable = true;
|
||||
|
||||
# Required to fix error in latest nixpkgs
|
||||
services.grafana.settings = { };
|
||||
|
||||
services.prometheus = {
|
||||
enable = true;
|
||||
exporters.node.enable = true;
|
||||
scrapeConfigs = [{
|
||||
job_name = "local";
|
||||
static_configs = [{ targets = [ "127.0.0.1:9100" ]; }];
|
||||
}];
|
||||
};
|
||||
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.metricsServer ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = "localhost:3000"; }];
|
||||
}];
|
||||
}];
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
# Secrets management method taken from here:
|
||||
# https://xeiaso.net/blog/nixos-encrypted-secrets-2021-01-20
|
||||
|
||||
# In my case, I pre-encrypt my secrets and commit them to git.
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options = {
|
||||
|
||||
secretsDirectory = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Default path to place secrets.";
|
||||
default = "/var/private";
|
||||
};
|
||||
|
||||
secrets = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule {
|
||||
options = {
|
||||
source = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = "Path to encrypted secret.";
|
||||
};
|
||||
dest = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Resulting path for decrypted secret.";
|
||||
};
|
||||
owner = lib.mkOption {
|
||||
default = "root";
|
||||
type = lib.types.str;
|
||||
description = "User to own the secret.";
|
||||
};
|
||||
group = lib.mkOption {
|
||||
default = "root";
|
||||
type = lib.types.str;
|
||||
description = "Group to own the secret.";
|
||||
};
|
||||
permissions = lib.mkOption {
|
||||
default = "0400";
|
||||
type = lib.types.str;
|
||||
description = "Permissions expressed as octal.";
|
||||
};
|
||||
};
|
||||
});
|
||||
description = "Set of secrets to decrypt to disk.";
|
||||
default = { };
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && !config.wsl.enable) {
|
||||
|
||||
# Create a default directory to place secrets
|
||||
|
||||
systemd.tmpfiles.rules = [ "d ${config.secretsDirectory} 0755 root wheel" ];
|
||||
|
||||
# Declare oneshot service to decrypt secret using SSH host key
|
||||
# - Requires that the secret is already encrypted for the host
|
||||
# - Encrypt secrets: nix run github:nmasur/dotfiles#encrypt-secret
|
||||
|
||||
systemd.services = lib.mapAttrs' (name: attrs: {
|
||||
name = "${name}-secret";
|
||||
value = {
|
||||
|
||||
description = "Decrypt secret for ${name}";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig.Type = "oneshot";
|
||||
script = ''
|
||||
${pkgs.age}/bin/age --decrypt \
|
||||
--identity ${config.identityFile} \
|
||||
--output ${attrs.dest} \
|
||||
${attrs.source}
|
||||
|
||||
chown '${attrs.owner}':'${attrs.group}' '${attrs.dest}'
|
||||
chmod '${attrs.permissions}' '${attrs.dest}'
|
||||
'';
|
||||
|
||||
};
|
||||
}) config.secrets;
|
||||
|
||||
# Example declaration
|
||||
# config.secrets.my-secret = {
|
||||
# source = ../../private/my-secret.age;
|
||||
# dest = "/var/lib/private/my-secret";
|
||||
# owner = "my-app";
|
||||
# group = "my-app";
|
||||
# permissions = "0440";
|
||||
# };
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options = {
|
||||
publicKey = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Public SSH key authorized for this system.";
|
||||
};
|
||||
permitRootLogin = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Root login settings.";
|
||||
default = "no";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && !config.wsl.enable) {
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
ports = [ 22 ];
|
||||
passwordAuthentication = false;
|
||||
gatewayPorts = "no";
|
||||
forwardX11 = false;
|
||||
allowSFTP = true;
|
||||
permitRootLogin = config.permitRootLogin;
|
||||
};
|
||||
|
||||
users.users.${config.user}.openssh.authorizedKeys.keys =
|
||||
[ config.publicKey ];
|
||||
|
||||
# Implement a simple fail2ban service for sshd
|
||||
services.sshguard.enable = true;
|
||||
|
||||
# Add terminfo for SSH from popular terminal emulators
|
||||
environment.enableAllTerminfo = true;
|
||||
};
|
||||
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options = {
|
||||
transmissionServer = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Hostname for Transmission";
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
|
||||
config = let
|
||||
namespace = config.networking.wireguard.interfaces.wg0.interfaceNamespace;
|
||||
vpnIp = lib.strings.removeSuffix "/32"
|
||||
(builtins.head config.networking.wireguard.interfaces.wg0.ips);
|
||||
in lib.mkIf (config.wireguard.enable && config.transmissionServer != null) {
|
||||
|
||||
# Setup transmission
|
||||
services.transmission = {
|
||||
enable = true;
|
||||
settings = {
|
||||
port-forwarding-enabled = false;
|
||||
rpc-authentication-required = true;
|
||||
rpc-port = 9091;
|
||||
rpc-bind-address = "0.0.0.0";
|
||||
rpc-username = config.user;
|
||||
rpc-host-whitelist = config.transmissionServer;
|
||||
rpc-host-whitelist-enabled = true;
|
||||
rpc-whitelist = "127.0.0.1,${vpnIp}";
|
||||
rpc-whitelist-enabled = true;
|
||||
};
|
||||
credentialsFile = config.secrets.transmission.dest;
|
||||
};
|
||||
|
||||
# Bind transmission to wireguard namespace
|
||||
systemd.services.transmission = {
|
||||
bindsTo = [ "netns@${namespace}.service" ];
|
||||
requires = [ "network-online.target" "transmission-secret.service" ];
|
||||
after = [ "wireguard-wg0.service" "transmission-secret.service" ];
|
||||
unitConfig.JoinsNamespaceOf = "netns@${namespace}.service";
|
||||
serviceConfig.NetworkNamespacePath = "/var/run/netns/${namespace}";
|
||||
};
|
||||
|
||||
# Create reverse proxy for web UI
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.transmissionServer ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = "localhost:9091"; }];
|
||||
}];
|
||||
}];
|
||||
|
||||
# Caddy and Transmission both try to set rmem_max for larger UDP packets.
|
||||
# We will choose Transmission's recommendation (4 MB).
|
||||
boot.kernel.sysctl."net.core.rmem_max" = 4194304;
|
||||
|
||||
# Allow inbound connections to reach namespace
|
||||
systemd.services.transmission-web-netns = {
|
||||
description = "Forward to transmission in wireguard namespace";
|
||||
requires = [ "transmission.service" ];
|
||||
after = [ "transmission.service" ];
|
||||
serviceConfig = {
|
||||
Restart = "on-failure";
|
||||
TimeoutStopSec = 300;
|
||||
};
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
script = ''
|
||||
${pkgs.iproute2}/bin/ip netns exec ${namespace} ${pkgs.iproute2}/bin/ip link set dev lo up
|
||||
${pkgs.socat}/bin/socat tcp-listen:9091,fork,reuseaddr exec:'${pkgs.iproute2}/bin/ip netns exec ${namespace} ${pkgs.socat}/bin/socat STDIO "tcp-connect:${vpnIp}:9091"',nofork
|
||||
'';
|
||||
};
|
||||
|
||||
# Create credentials file for transmission
|
||||
secrets.transmission = {
|
||||
source = ../../private/transmission.json.age;
|
||||
dest = "${config.secretsDirectory}/transmission.json";
|
||||
owner = "transmission";
|
||||
group = "transmission";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let vaultwardenPath = "/var/lib/bitwarden_rs"; # Default service directory
|
||||
|
||||
in {
|
||||
|
||||
options = {
|
||||
|
||||
vaultwardenServer = lib.mkOption {
|
||||
description = "Hostname for Vaultwarden.";
|
||||
type = lib.types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.vaultwardenServer != null) {
|
||||
services.vaultwarden = {
|
||||
enable = true;
|
||||
config = {
|
||||
DOMAIN = "https://${config.vaultwardenServer}";
|
||||
SIGNUPS_ALLOWED = false;
|
||||
SIGNUPS_VERIFY = true;
|
||||
INVITATIONS_ALLOWED = true;
|
||||
WEB_VAULT_ENABLED = true;
|
||||
ROCKET_ADDRESS = "127.0.0.1";
|
||||
ROCKET_PORT = 8222;
|
||||
WEBSOCKET_ENABLED = true;
|
||||
WEBSOCKET_ADDRESS = "0.0.0.0";
|
||||
WEBSOCKET_PORT = 3012;
|
||||
LOGIN_RATELIMIT_SECONDS = 60;
|
||||
LOGIN_RATELIMIT_MAX_BURST = 10;
|
||||
ADMIN_RATELIMIT_SECONDS = 300;
|
||||
ADMIN_RATELIMIT_MAX_BURST = 3;
|
||||
};
|
||||
environmentFile = config.secrets.vaultwarden.dest;
|
||||
dbBackend = "sqlite";
|
||||
};
|
||||
|
||||
secrets.vaultwarden = {
|
||||
source = ../../private/vaultwarden.age;
|
||||
dest = "${config.secretsDirectory}/vaultwarden";
|
||||
owner = "vaultwarden";
|
||||
group = "vaultwarden";
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 3012 ];
|
||||
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.vaultwardenServer ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = "localhost:8222"; }];
|
||||
headers.request.add."X-Real-IP" = [ "{http.request.remote.host}" ];
|
||||
}];
|
||||
}];
|
||||
|
||||
## Backup config
|
||||
|
||||
# Open to groups, allowing for backups
|
||||
systemd.services.vaultwarden.serviceConfig.StateDirectoryMode =
|
||||
lib.mkForce "0770";
|
||||
systemd.tmpfiles.rules = [
|
||||
"f ${vaultwardenPath}/db.sqlite3 0660 vaultwarden vaultwarden"
|
||||
"f ${vaultwardenPath}/db.sqlite3-shm 0660 vaultwarden vaultwarden"
|
||||
"f ${vaultwardenPath}/db.sqlite3-wal 0660 vaultwarden vaultwarden"
|
||||
];
|
||||
|
||||
# Allow litestream and vaultwarden to share a sqlite database
|
||||
users.users.litestream.extraGroups = [ "vaultwarden" ];
|
||||
users.users.vaultwarden.extraGroups = [ "litestream" ];
|
||||
|
||||
# Backup sqlite database with litestream
|
||||
services.litestream = {
|
||||
settings = {
|
||||
dbs = [{
|
||||
path = "${vaultwardenPath}/db.sqlite3";
|
||||
replicas = [{
|
||||
url =
|
||||
"s3://${config.backupS3.bucket}.${config.backupS3.endpoint}/vaultwarden";
|
||||
}];
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
# Don't start litestream unless vaultwarden is up
|
||||
systemd.services.litestream = {
|
||||
after = [ "vaultwarden.service" ];
|
||||
requires = [ "vaultwarden.service" ];
|
||||
};
|
||||
|
||||
# Run a separate file backup on a schedule
|
||||
systemd.timers.vaultwarden-backup = {
|
||||
timerConfig = {
|
||||
OnCalendar = "*-*-* 06:00:00"; # Once per day
|
||||
Unit = "vaultwarden-backup.service";
|
||||
};
|
||||
wantedBy = [ "timers.target" ];
|
||||
};
|
||||
|
||||
# Backup other Vaultwarden data to object storage
|
||||
systemd.services.vaultwarden-backup = {
|
||||
description = "Backup Vaultwarden files";
|
||||
environment.AWS_ACCESS_KEY_ID = config.backupS3.accessKeyId;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "vaultwarden";
|
||||
Group = "backup";
|
||||
EnvironmentFile = config.secrets.backup.dest;
|
||||
};
|
||||
script = ''
|
||||
${pkgs.awscli2}/bin/aws s3 sync \
|
||||
${vaultwardenPath}/ \
|
||||
s3://${config.backupS3.bucket}/vaultwarden/ \
|
||||
--endpoint-url=https://${config.backupS3.endpoint} \
|
||||
--exclude "*db.sqlite3*" \
|
||||
--exclude ".db.sqlite3*"
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
options.wireguard.enable = lib.mkEnableOption "Wireguard VPN setup.";
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && config.wireguard.enable) {
|
||||
|
||||
networking.wireguard = {
|
||||
enable = true;
|
||||
interfaces = {
|
||||
wg0 = {
|
||||
|
||||
# Establishes identity of this machine
|
||||
generatePrivateKeyFile = false;
|
||||
privateKeyFile = config.secrets.wireguard.dest;
|
||||
|
||||
# Move to network namespace for isolating programs
|
||||
interfaceNamespace = "wg";
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Create namespace for Wireguard
|
||||
# This allows us to isolate specific programs to Wireguard
|
||||
systemd.services."netns@" = {
|
||||
description = "%I network namespace";
|
||||
before = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = "${pkgs.iproute2}/bin/ip netns add %I";
|
||||
ExecStop = "${pkgs.iproute2}/bin/ip netns del %I";
|
||||
};
|
||||
};
|
||||
|
||||
# Create private key file for wireguard
|
||||
secrets.wireguard = {
|
||||
source = ../../private/wireguard.age;
|
||||
dest = "${config.secretsDirectory}/wireguard";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && config.wsl.enable) {
|
||||
|
||||
# Systemd doesn't work in WSL so these must be disabled
|
||||
services.geoclue2.enable = lib.mkForce false;
|
||||
location = { provider = lib.mkForce "manual"; };
|
||||
services.localtimed.enable = lib.mkForce false;
|
||||
|
||||
# Used by NeoVim for clipboard sharing with Windows
|
||||
# home-manager.users.${config.user}.home.sessionPath =
|
||||
# [ "/mnt/c/Program Files/win32yank/" ];
|
||||
|
||||
# Replace config directory with our repo, since it sources from config on
|
||||
# every launch
|
||||
system.activationScripts.configDir.text = ''
|
||||
rm -rf /etc/nixos
|
||||
ln --symbolic --no-dereference --force ${config.dotfilesPath} /etc/nixos
|
||||
'';
|
||||
|
||||
};
|
||||
|
||||
}
|
Reference in New Issue
Block a user