Compare commits

..

1 Commits

Author SHA1 Message Date
384d6939f8 Removes lock on vega. Fixes some aliases 2025-05-28 11:31:46 -07:00
132 changed files with 2096 additions and 4984 deletions

View File

@@ -1,17 +1,9 @@
keys:
- &drew_mcp age1yvdzvuvu5wqztcx6ll2xk6x547uuyqy735tjjdd7zftkz53jsf9qf5ahue
- &drew_vega age1dstsz5g4qthwt2sssu5wp787ku7rnfqm02mqaefsaueswhvqzpmsyft4p4
- &admin_drew age1yvdzvuvu5wqztcx6ll2xk6x547uuyqy735tjjdd7zftkz53jsf9qf5ahue
- &server_mcp age1jrk4h7x4qzhr6z5m4d099mlfyjc4n5n9s52r4gfsdz0slnqlqa9sss735v
- &server_sirius age17q5n3pngyf0dvcg23h7ph8fqcraw0phjzgsra5cynzq4mf7hw3wsarmepw
creation_rules:
- path_regex: secrets/mcp.yaml
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age:
- *drew_vega
- *drew_mcp
- *server_mcp
- path_regex: secrets/sirius.yaml
key_groups:
- age:
- *drew_vega
- *admin_drew
- *server_mcp

593
flake.lock generated
View File

@@ -1,81 +1,5 @@
{
"nodes": {
"aquamarine": {
"inputs": {
"hyprutils": [
"hyprsplit",
"hyprland",
"hyprutils"
],
"hyprwayland-scanner": [
"hyprsplit",
"hyprland",
"hyprwayland-scanner"
],
"nixpkgs": [
"hyprsplit",
"hyprland",
"nixpkgs"
],
"systems": [
"hyprsplit",
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1778620495,
"narHash": "sha256-Gu7UhWjwKCgSiVC3Qz/Rc7cYi9DNuDTBxYzg3kfLvfM=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "be35f75ac305f430f5f9d89b5f5a4af59ca7567e",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "aquamarine",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"hyprsplit",
"hyprland",
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
@@ -83,453 +7,78 @@
]
},
"locked": {
"lastModified": 1784350909,
"narHash": "sha256-ZWyzLbS1yKUTeFJLmdVuWNnHttL333/ldJbEE+KzCrM=",
"lastModified": 1748226808,
"narHash": "sha256-GaBRgxjWO1bAQa8P2+FDxG4ANBVhjnSjBms096qQdxo=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "4ce190229c73d44536caa7072f6308fb2d8feeb3",
"rev": "83665c39fa688bd6a1f7c43cf7997a70f6a109f9",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-26.05",
"ref": "release-25.05",
"repo": "home-manager",
"type": "github"
}
},
"hyprcursor": {
"home-manager-unstable": {
"inputs": {
"hyprlang": [
"hyprsplit",
"hyprland",
"hyprlang"
],
"nixpkgs": [
"hyprsplit",
"hyprland",
"nixpkgs"
],
"systems": [
"hyprsplit",
"hyprland",
"systems"
"nixpkgs-unstable"
]
},
"locked": {
"lastModified": 1776511930,
"narHash": "sha256-fCpwFiTW0rT7oKJqr3cqHMnkwypSwQKpbtUEtxdkgrM=",
"owner": "hyprwm",
"repo": "hyprcursor",
"rev": "39435900785d0c560c6ae8777d29f28617d031ef",
"lastModified": 1748227609,
"narHash": "sha256-SaSdslyo6UGDpPUlmrPA4dWOEuxCy2ihRN9K6BnqYsA=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "d23d20f55d49d8818ac1f1b2783671e8a6725022",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprcursor",
"type": "github"
}
},
"hyprgraphics": {
"inputs": {
"hyprutils": [
"hyprsplit",
"hyprland",
"hyprutils"
],
"nixpkgs": [
"hyprsplit",
"hyprland",
"nixpkgs"
],
"systems": [
"hyprsplit",
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1776426399,
"narHash": "sha256-RUESLKNikIeEq9ymGJ6nmcDXiSFQpUW1IhJ245nL3xM=",
"owner": "hyprwm",
"repo": "hyprgraphics",
"rev": "68d064434787cf1ed4a2fe257c03c5f52f33cf84",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprgraphics",
"type": "github"
}
},
"hyprland": {
"inputs": {
"aquamarine": "aquamarine",
"hyprcursor": "hyprcursor",
"hyprgraphics": "hyprgraphics",
"hyprland-guiutils": "hyprland-guiutils",
"hyprland-protocols": "hyprland-protocols",
"hyprlang": "hyprlang",
"hyprutils": "hyprutils",
"hyprwayland-scanner": "hyprwayland-scanner",
"hyprwire": "hyprwire",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks",
"systems": "systems",
"xdph": "xdph"
},
"locked": {
"lastModified": 1779392427,
"narHash": "sha256-0SiD8JBx6cU0KL1XjLMsR/nSZB5XY3uhrNnvfxH0CtY=",
"owner": "hyprwm",
"repo": "Hyprland",
"rev": "8b7110cc68662a343b6839f292ac0f44a64c3364",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "Hyprland",
"type": "github"
}
},
"hyprland-guiutils": {
"inputs": {
"aquamarine": [
"hyprsplit",
"hyprland",
"aquamarine"
],
"hyprgraphics": [
"hyprsplit",
"hyprland",
"hyprgraphics"
],
"hyprlang": [
"hyprsplit",
"hyprland",
"hyprlang"
],
"hyprtoolkit": "hyprtoolkit",
"hyprutils": [
"hyprsplit",
"hyprland",
"hyprutils"
],
"hyprwayland-scanner": [
"hyprsplit",
"hyprland",
"hyprwayland-scanner"
],
"nixpkgs": [
"hyprsplit",
"hyprland",
"nixpkgs"
],
"systems": [
"hyprsplit",
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1776426575,
"narHash": "sha256-KI6nIfVihn/DPaeB5Et46Xg3dkNHrrEtUd5LBBVomB0=",
"owner": "hyprwm",
"repo": "hyprland-guiutils",
"rev": "a968d211048e3ed538e47b84cb3649299578f19d",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprland-guiutils",
"type": "github"
}
},
"hyprland-protocols": {
"inputs": {
"nixpkgs": [
"hyprsplit",
"hyprland",
"nixpkgs"
],
"systems": [
"hyprsplit",
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1772460177,
"narHash": "sha256-/6G/MsPvtn7bc4Y32pserBT/Z4SUUdBd4XYJpOEKVR4=",
"owner": "hyprwm",
"repo": "hyprland-protocols",
"rev": "1cb6db5fd6bb8aee419f4457402fa18293ace917",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprland-protocols",
"type": "github"
}
},
"hyprlang": {
"inputs": {
"hyprutils": [
"hyprsplit",
"hyprland",
"hyprutils"
],
"nixpkgs": [
"hyprsplit",
"hyprland",
"nixpkgs"
],
"systems": [
"hyprsplit",
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1777320127,
"narHash": "sha256-Qu+Wf2Bp5qUjyn2YpZNq8a7JyzTGowhT1knrwE38a9U=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "090117506ddc3d7f26e650ff344d378c2ec329cc",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprlang",
"type": "github"
}
},
"hyprsplit": {
"inputs": {
"hyprland": "hyprland",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1781205112,
"narHash": "sha256-PaoUtmk+qIP/ESdxkxnY7mUMpMHjix88qu22R5GLQqE=",
"owner": "shezdy",
"repo": "hyprsplit",
"rev": "6b00b677d8905fb38779c91e12d6294e0e586a44",
"type": "github"
},
"original": {
"owner": "shezdy",
"repo": "hyprsplit",
"type": "github"
}
},
"hyprtoolkit": {
"inputs": {
"aquamarine": [
"hyprsplit",
"hyprland",
"hyprland-guiutils",
"aquamarine"
],
"hyprgraphics": [
"hyprsplit",
"hyprland",
"hyprland-guiutils",
"hyprgraphics"
],
"hyprlang": [
"hyprsplit",
"hyprland",
"hyprland-guiutils",
"hyprlang"
],
"hyprutils": [
"hyprsplit",
"hyprland",
"hyprland-guiutils",
"hyprutils"
],
"hyprwayland-scanner": [
"hyprsplit",
"hyprland",
"hyprland-guiutils",
"hyprwayland-scanner"
],
"nixpkgs": [
"hyprsplit",
"hyprland",
"hyprland-guiutils",
"nixpkgs"
],
"systems": [
"hyprsplit",
"hyprland",
"hyprland-guiutils",
"systems"
]
},
"locked": {
"lastModified": 1772462885,
"narHash": "sha256-5pHXrQK9zasMnIo6yME6EOXmWGFMSnCITcfKshhKJ9I=",
"owner": "hyprwm",
"repo": "hyprtoolkit",
"rev": "9af245a69fa6b286b88ddfc340afd288e00a6998",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprtoolkit",
"type": "github"
}
},
"hyprutils": {
"inputs": {
"nixpkgs": [
"hyprsplit",
"hyprland",
"nixpkgs"
],
"systems": [
"hyprsplit",
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1778234770,
"narHash": "sha256-jAcsogZwWMfXT9MfXxZzkwliAqIuZUV0p71h6Ba9ReE=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "a2dbd8a4cc51f7cbe4224732668392bb1aa79df2",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprutils",
"type": "github"
}
},
"hyprwayland-scanner": {
"inputs": {
"nixpkgs": [
"hyprsplit",
"hyprland",
"nixpkgs"
],
"systems": [
"hyprsplit",
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1777159683,
"narHash": "sha256-Jxixw6wZphUp+nHYxOKUYSckL17QMBx2d5Zp0rJHr1g=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "b8632713a6beaf28b56f2a7b0ab2fb7088dbb404",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"type": "github"
}
},
"hyprwire": {
"inputs": {
"hyprutils": [
"hyprsplit",
"hyprland",
"hyprutils"
],
"nixpkgs": [
"hyprsplit",
"hyprland",
"nixpkgs"
],
"systems": [
"hyprsplit",
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1778410714,
"narHash": "sha256-o6RzFj4nJXaPRY7EM01siuCQeT41RfwwmcmFQqwFJJg=",
"owner": "hyprwm",
"repo": "hyprwire",
"rev": "85148a8e612808cf5ddb25d0b3c5840f3498a7dc",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprwire",
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1778443072,
"narHash": "sha256-zi7/fsqM/kFdNuED//4WOCUtezGtKKqRNORjMvfwjnA=",
"owner": "NixOS",
"lastModified": 1748162331,
"narHash": "sha256-rqc2RKYTxP3tbjA+PB3VMRQNnjesrT0pEofXQTrMsS8=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "da5ad661ba4e5ef59ba743f0d112cbc30e474f32",
"rev": "7c43f080a7f28b2774f3b3f43234ca11661bf334",
"type": "github"
},
"original": {
"owner": "NixOS",
"owner": "nixos",
"ref": "nixos-25.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1748190013,
"narHash": "sha256-R5HJFflOfsP5FBtk+zE8FpL8uqE7n62jqOsADvVshhE=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "62b852f6c6742134ade1abdd2a21685fd617a291",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1784707089,
"narHash": "sha256-2V/6imsUgB7mPZlHY54oeVBRDoZbPKnvzwkAHUSSufk=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "b3fe9581c9061c749abef42b6d4ee7b7c05c33fa",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-26.05",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"hyprsplit",
"hyprland",
"nixpkgs"
]
},
"locked": {
"lastModified": 1778507602,
"narHash": "sha256-kTwur1wV+01SdqskVMSo6JMEpg71ps3HpbFY2GsflKs=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "61ab0e80d9c7ab14c256b5b453d8b3fb0189ba0a",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"home-manager": "home-manager",
"hyprsplit": "hyprsplit",
"nixpkgs": "nixpkgs_2",
"home-manager-unstable": "home-manager-unstable",
"nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable",
"sops-nix": "sops-nix"
}
},
@@ -540,11 +89,11 @@
]
},
"locked": {
"lastModified": 1783174389,
"narHash": "sha256-aCWC8ngycU7OdJrU2+Je3qf+1a2ykuBvpPhZT/9tXMc=",
"lastModified": 1747603214,
"narHash": "sha256-lAblXm0VwifYCJ/ILPXJwlz0qNY07DDYdLD+9H+Wc8o=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "f1406619a3884cd5c47992a70b8b35c9c0fcb4c9",
"rev": "8d215e1c981be3aa37e47aeabd4e61bb069548fd",
"type": "github"
},
"original": {
@@ -552,68 +101,6 @@
"repo": "sops-nix",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
"owner": "nix-systems",
"repo": "default-linux",
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default-linux",
"type": "github"
}
},
"xdph": {
"inputs": {
"hyprland-protocols": [
"hyprsplit",
"hyprland",
"hyprland-protocols"
],
"hyprlang": [
"hyprsplit",
"hyprland",
"hyprlang"
],
"hyprutils": [
"hyprsplit",
"hyprland",
"hyprutils"
],
"hyprwayland-scanner": [
"hyprsplit",
"hyprland",
"hyprwayland-scanner"
],
"nixpkgs": [
"hyprsplit",
"hyprland",
"nixpkgs"
],
"systems": [
"hyprsplit",
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1778265244,
"narHash": "sha256-8jlPtGSsv/CQY6tVVyLF4Jjd0gnS+Zbn9yk/V13A9nM=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "813ea5ca9a1702a9a2d1f5836bc00172ef698968",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"type": "github"
}
}
},
"root": "root",

View File

@@ -2,27 +2,28 @@
description = "System Configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-26.05";
nixpkgs = {
url = "github:nixos/nixpkgs?ref=nixos-25.05";
};
home-manager = {
url = "github:nix-community/home-manager?ref=release-26.05";
url = "github:nix-community/home-manager?ref=release-25.05";
inputs.nixpkgs.follows = "nixpkgs";
};
nixpkgs-unstable = {
url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
home-manager-unstable = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
hyprsplit = {
url = "github:shezdy/hyprsplit";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
self,
nixpkgs,
...
}@inputs:
{ self, nixpkgs, ... }@inputs:
let
local = import ./lib;
mkNixosConfig =
@@ -37,13 +38,11 @@
modules = [
home-manager.nixosModules.home-manager
{
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = {
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.extraSpecialArgs = {
inherit inputs local;
};
};
}
path
];
@@ -63,12 +62,9 @@
mcp = mkNixosConfig {
path = ./system/hosts/mcp;
};
sirius-a = mkNixosConfig {
path = ./system/hosts/sirius-a;
};
};
features = {
development = import ./home-manager/features/development/development.nix;
development = (import ./home-manager/features/development/development.nix);
};
};
}

View File

@@ -8,7 +8,7 @@
discord = {
name = "Discord";
# Custom options to reduce flickering under wayland.
exec = "env ELECTRON_OZONE_PLATFORM_HINT= discord --enable-features=UseOzonePlatform --ozone-platform=wayland --disable-gpu";
exec = "discord --enable-features=UseOzonePlatform --ozone-platform=wayland --disable-gpu";
};
};
}

View File

@@ -1,64 +0,0 @@
{
pkgs,
...
}:
let
freecad-wrapped = pkgs.symlinkJoin {
name = "freecad-wrapped";
paths = [ pkgs.freecad ];
buildInputs = [ pkgs.makeWrapper ];
postBuild = ''
wrapProgram $out/bin/freecad \
--prefix MESA_LOADER_DRIVER_OVERRIDE : zink \
--prefix __EGL_VENDOR_LIBRARY_FILENAMES : ${pkgs.mesa}/share/glvnd/egl_vendor.d/50_mesa.json
'';
};
# bambu-studio-wrapped = pkgs.symlinkJoin {
# name = "bambu-studio-wrapped";
# paths = [ pkgs.bambu-studio ];
# buildInputs = [ pkgs.makeWrapper ];
# postBuild = ''
# wrapProgram $out/bin/bambu-studio \
# --prefix MESA_LOADER_DRIVER_OVERRIDE : zink \
# --prefix __EGL_VENDOR_LIBRARY_FILENAMES : ${pkgs.mesa}/share/glvnd/egl_vendor.d/50_mesa.json
# '';
# };
in
{
home.packages = with pkgs; [
# bambu-studio-wrapped
lycheeslicer
orca-slicer
blender
freecad-wrapped
openscad
];
xdg.desktopEntries.orynt3d =
let
orynt3d-appimage = pkgs.fetchurl {
name = "orynt3d-appimage";
url = "https://files.orynt3d.com/client/Orynt3D-0.15.3.AppImage";
sha256 = "0j10myj06ff4frsd4yv7z3lb3qgw3ha70hc5hdc9idbryica801y";
};
in
{
name = "Orynt3D";
exec = "env __EGL_VENDOR_LIBRARY_FILENAMES=/run/opengl-driver/share/glvnd/egl_vendor.d/10_nvidia.json ${pkgs.appimage-run}/bin/appimage-run ${orynt3d-appimage}";
terminal = false;
type = "Application";
# icon = "";
comment = "3D model viewer and organizer";
categories = [
"Science"
"Development"
];
};
# Options to get Bambu Studio to run:
# __GLX_VENDOR_LIBRARY_NAME=mesa __EGL_VENDOR_LIBRARY_FILENAMES=/nix/store/js9cfbjvlsls14nddk39fw74vyvlhz4l-mesa-25.0.7/share/glvnd/egl_vendor.d/50_mesa.json MESA_LOADER_DRIVER_OVERRIDE=zink GALLIUM_DRIVER=zink WEBKIT_DISABLE_DMABUF_RENDERER=1 bambu-studio
}

View File

@@ -1,8 +0,0 @@
{ pkgs, ... }:
{
home.packages = with pkgs; [
stellarium
kstars
celestia
];
}

View File

@@ -1,41 +1,7 @@
{ pkgs, ... }:
with pkgs;
let
# A script that runs as long as media is playing.
isMediaPlaying = writeShellApplication {
name = "isMediaPlaying";
runtimeInputs = [
playerctl
];
text = ''
set -e
while [ "$(playerctl status)" = "Playing" ]; do
echo -n "."
sleep 1
done
'';
};
# A script that prevents the system from going to sleep while media is playing
mediaCaffeine = writeShellApplication {
name = "media-caffeine";
runtimeInputs = [
isMediaPlaying
systemd
];
text = ''
set -e
systemd-inhibit --what=sleep --why="Media is playing" --mode=block isMediaPlaying
'';
};
in
{
home.packages = with pkgs; [
pulseaudio # for pactl and other tools
pavucontrol # GUI volume control with lots of options
mediaCaffeine
];
}

View File

@@ -1,11 +0,0 @@
{ pkgs, ... }:
{
imports = [
../apps/element.nix
../apps/discord.nix
];
home.packages = with pkgs; [
signal-desktop
];
}

View File

@@ -1,14 +0,0 @@
_: {
programs.claude-code = {
enable = true;
settings = {
effortLevel = "medium";
skipAutoPermissionsPrompt = true;
includeCoAuthoredBy = false;
skills = ./claude/skills;
};
};
}

View File

@@ -1,291 +0,0 @@
---
name: better-documents
description: >
Apply communication best practices when generating or reviewing any document,
presentation, report, memo, slide deck, proposal, or email. Use this skill when
asked to write, create, draft, or generate a document or presentation — apply
the principles at generation time, not as an afterthought. Also use when asked
to "review my doc," "look at this deck," "does this make sense," "is this clear,"
"improve this proposal," "check this before I send it," "make this more effective,"
or any request to evaluate whether a document will land with its audience. Use
this skill even when the request seems minor — a "quick look" or a "short memo"
is exactly when these principles matter most.
---
# Document Review
Apply communication best practices when generating or reviewing any business
document — whether the right message reaches the right audience in the right order.
This skill applies the principles from Anil Dash's [Make better documents](https://www.anildash.com/2024/03/10/make-better-documents/).
## Scope
This skill covers **business documents**: presentations, slide decks, proposals,
reports, memos, briefs, one-pagers, and emails. It does not cover UI, web
components, or frontend applications — use the `frontend-design` skill for those.
The design principles here differ from frontend design in important ways:
- **Restraint over expression.** UI benefits from bold aesthetic commitment and
distinctive visual personality. Business documents benefit from getting out of
the way of the content. Most people reading a board deck or a client proposal
are not there to appreciate the typography.
- **The audience can't interact.** A web interface rewards exploration. A
document gets one read, often skimmed, often on a small screen or printed.
Every design choice that draws attention to itself is attention stolen from
the message.
- **Non-designers are both authors and audiences.** UI is built and consumed
with design context nearby. Business documents are built by operators, analysts,
executives, and founders — and read by the same. Complexity that a designer
handles intuitively becomes noise for everyone else.
## Modes
| Mode | When to use | What you do |
|---|---|---|
| **generate** | "write me a proposal," "create a deck," "draft a memo" | Apply all five principles while producing the document; no separate report needed |
| **full-review** | "review this doc," "look at this deck" | Run all five passes on an existing document, produce a structured report |
| **rewrite** | "fix this," "make this better" | Apply all passes and return a revised version |
| **targeted** | "is the ask clear," "check the formatting" | Run only the relevant pass(es) |
| **quick** | "quick look," "does this work" | Run passes 1 and 2 only, flag the top 3 issues |
Default to **generate** when producing a new document from scratch. Default to
**full-review** when given an existing document with no other instruction.
---
## The Five Passes
Apply these in order. Each targets a different failure mode.
### Pass 1 — Audience and Purpose
The most common reason documents fail: they were written for the author, not the reader.
Check for:
- **Missing context**: Does the document assume knowledge the audience may not have?
Flag any jargon, backstory, or acronyms that aren't explained.
- **Buried ask**: Is there a request, decision, or action needed? If so, is it
stated clearly in the first third of the document — not saved for the end?
- **Author-centered opening**: Does the document open with the author's anxieties,
backstory, or credentials rather than shared context? Flag and suggest reordering
to start from common ground.
- **Missing deadline or stakes**: If a decision or response is needed, is the
timeline stated? Is the reason for that timeline explained from the *audience's*
perspective, not just the author's?
Severity guide:
- **CRITICAL** — The core ask or decision is absent or appears only at the end
- **MAJOR** — The audience is unclear; the document would read differently to
different readers
- **MINOR** — Context gaps that could be filled with a sentence
---
### Pass 2 — Structure and Sequencing
Order signals importance. Audiences assume the first thing is the most important.
If it isn't, they'll be confused or dismissive before they reach the part that matters.
Check for:
- **Creation-order sequencing**: Is the document structured in the order it was
*written* (background → analysis → conclusion) rather than the order the
audience needs (conclusion → supporting logic → call to action)?
- **Murder mystery structure**: Does the document build to a reveal rather than
stating the point upfront? Flag any document where the key conclusion or request
appears in the second half.
- **Unannounced ordering**: If the document is ordered chronologically, by category,
or by any logic other than importance, is that explicitly stated? If not, the
audience will hunt for meaning in the sequence.
- **Key information in the wrong channel**: Is anything critical only in speaker
notes, appendices, or footnotes? Central points belong in the main body.
Severity guide:
- **CRITICAL** — The document's conclusion or core request is in the second half
- **MAJOR** — Ordering is non-obvious and unexplained
- **MINOR** — Supporting points could be reordered for better flow
---
### Pass 3 — Formatting Restraint
Over-formatting is the most visible symptom of unclear thinking. When everything
is emphasized, nothing is.
Check for:
- **Emphasis overload**: Are bold, italic, underline, and color being combined
on the same text? Flag any instance of two or more emphasis types on a single
element.
- **Underlines on non-links**: Flag every underline that isn't a hyperlink.
Underlines exist to signal clickability; using them decoratively confuses readers.
- **Color proliferation**: Count distinct colors in use (excluding images). More
than two is almost always a problem. More than three is always a problem.
- **Border clutter**: Flag tables and sections with heavy borders. White space
separates content more clearly than lines.
- **Filler visuals**: Flag images, icons, or clip art that aren't specific to
the content's message. A blank space is better than a stock photo.
- **Formatting inconsistency**: Do headings, titles, or labels vary in size,
weight, or color across the document without clear reason? Flag inconsistencies
that will read as meaningful to an audience even if they weren't intentional.
Severity guide:
- **CRITICAL** — Formatting inconsistencies that will be read as semantic signals
(e.g., random font changes mid-document)
- **MAJOR** — Overuse of emphasis that makes it impossible to identify what's
actually important
- **MINOR** — Minor decoration that adds noise without adding meaning
---
### Pass 4 — Wayfinding and Density
Audiences need to know where they are in the story, and how far they have to go.
Check for:
- **No orientation**: Is there any indication of structure — a brief outline,
section headers, or progress markers — for documents longer than one page or
five slides? If not, flag.
- **Unsummarized data**: Does any chart, table, or data display appear without
a title or caption that states what it shows? Readers shouldn't have to interpret
data cold. Flag every chart or table whose title doesn't answer "what does this show?"
- **Unanswerable questions**: Are there open-ended questions in the document
(as headers or prompts) that can't actually be answered with a choice? Flag
any question that could lead to a philosophical discussion instead of a decision.
Good: "Do we go with Option A or Option B?" Bad: "How do we improve?"
- **Dense, unbroken prose**: Are there paragraphs longer than ~6 lines where
bullet points would make the content more skimmable without losing meaning?
Severity guide:
- **CRITICAL** — Data presented without any interpretive framing
- **MAJOR** — Long documents with no structural signposts
- **MINOR** — Questions that could be made more answerable
---
### Pass 5 — Naming and Versioning
The title is information. Most document names throw it away.
Check for:
- **Generic or auto-generated title**: Does the document have a title like
"Untitled," "Draft," "Meeting Notes," or the name of the person it's addressed
to? Flag and suggest a title that includes: topic, date, and context/owner.
- **Recipient-first naming**: If the document is *for* someone at another
organization, is it named after *them* rather than the author or topic?
They'll search for it by *your* name, not theirs.
- **Version ambiguity**: Does the filename or title use `_v2`, `_final`,
`_final_final`, or sequential numbers without dates? Flag and suggest
date-based versioning.
- **Meeting invites named after participants**: If the document is tied to a
meeting, flag any invite titled "Meeting with [Name]" — it tells the recipient
nothing about what the conversation is for.
Severity guide:
- **CRITICAL** — Untitled or generic title on a document intended for external sharing
- **MAJOR** — Version ambiguity on any document with multiple drafts
- **MINOR** — Suboptimal naming that could make retrieval difficult later
---
## Output Format
### Generate mode
Produce the document applying all five principles as first-order constraints —
not as a post-hoc checklist. Specifically:
- State the conclusion or ask in the opening, not the closing
- Order content by importance to the audience, not by how it was assembled
- Use the minimum formatting necessary: one emphasis type at a time, no decorative
underlines, no more than two colors, white space over borders
- Include structural signposts if the document is longer than one page or five slides
- Give the document a title that includes topic, date, and relevant context
**Override default visual style.** Claude's default document aesthetic — warm
cream backgrounds, serif display type (Georgia, Fraunces, Playfair), terracotta
or amber accents, and a "small all-caps label over a body copy block" slide
layout — is recognizable as AI-generated and inappropriate for most professional
contexts. Do not apply it.
Instead, before generating any document with visual styling:
1. **Ask about brand or visual constraints first.** Does the user have brand
colors, a template, or a font they use? If so, follow those exactly.
2. **If no brand guidance is given**, default to neutral: black text on white,
one sans-serif typeface throughout, no decorative color. The content should
do the work, not the palette.
3. **If the user explicitly wants visual styling**, offer 23 distinct directions
in one sentence each (e.g. "neutral/minimal," "bold/high-contrast," "warm/
editorial") and let them choose before proceeding. Do not pick for them.
No review report is needed in generate mode. The principles are baked in.
---
### Full-review and targeted mode
```
## Document Review: [Title or description]
### Summary
[23 sentences: what the document is trying to do, how well it's set up to succeed,
the one or two changes that would most improve it]
### Pass 1 — Audience and Purpose [N issues]
[Each issue: location → what the problem is → suggested fix → severity]
### Pass 2 — Structure and Sequencing [N issues]
[Same format]
### Pass 3 — Formatting Restraint [N issues]
[Same format]
### Pass 4 — Wayfinding and Density [N issues]
[Same format]
### Pass 5 — Naming and Versioning [N issues]
[Same format]
### Top 3 Changes
[The three revisions that would most improve this document's effectiveness,
ranked by impact]
```
### Rewrite mode
Return the revised document with a brief note after explaining what was changed
in each pass and why. Do not explain every small edit — only the structural
decisions that changed meaning or order.
### Quick mode
Return only the top 3 issues found across passes 1 and 2, with a one-line
suggested fix for each. No full report.
---
## Constraints
- **Don't alter the author's voice.** In review and rewrite modes, improve
structure and clarity, not style. If a sentence is clear and effective, leave it.
- **Don't invent content.** If information is missing (a deadline, a decision
option, context for the audience), flag the gap — don't fill it in.
- **Ask before generating if the audience is unclear.** In generate mode, if the
request doesn't specify who will read the document or what decision it needs to
drive, ask before writing. A document built for the wrong audience fails
regardless of how well it's structured.
- **Be specific.** Every finding must reference the actual text or location.
Never say "consider clarifying" without showing where and how.
- **Respect intentional choices.** If the document is explicitly a narrative
or a mystery-format pitch, flag the structural convention as a risk rather
than a violation.
---
*Based on [Make better documents](https://www.anildash.com/2024/03/10/make-better-documents/) by Anil Dash.*

View File

@@ -1,57 +0,0 @@
---
name: elegance
description: Iteratively study and apply elegant coding patterns. Each iteration - understand the code, research what simple and elegant code looks like, apply learnings, verify with CI. Use as a standalone refactoring pass or when the user asks to make code more elegant, simple, or idiomatic.
source: https://github.com/srid/emanote/tree/master/.claude/skills/elegance
---
# Elegance
Iteratively study and apply elegant coding patterns. Each iteration: understand the code, research what simple & elegant code looks like, apply learnings, verify with CI.
Run for **3 iterations** (or as specified by the user — can be a number or a duration like `2h`).
## 0. Determine Scope
- Before starting, use the `AskUserQuestion` tool to ask: should this operate on the **whole codebase** or only on **changes in the current branch/PR**?
- If scoped to current branch/PR, use `git diff main...HEAD` (or the appropriate base branch) to identify changed files and limit all subsequent steps to those files only.
## For each iteration (1 to N)
### 1. Understand
- Read through the relevant source files.
- Note patterns, repetition, unnecessary complexity, non-idiomatic code.
### 2. Research
- Use WebSearch/WebFetch to research what simple, elegant (yet readable!) code looks like for the relevant technology.
- Look for idiomatic patterns, standard simplifications, and community best practices.
- Focus on: simplicity, readability, removing unnecessary abstraction, leveraging language features.
### 3. Apply
- Refactor based on what you learned.
- This can include: edits, code reorganization, or even rewrites where simplicity demands it.
- Prefer fewer lines, clearer intent, and idiomatic style.
- Don't add abstractions — remove them.
### 4. Verify
- Run tests/CI to check edits.
- If CI fails, fix the issues before proceeding to the next iteration.
### 5. Log Progress
- Briefly note what changed in this iteration and why.
## After all iterations
- Do **not** git commit. Leave all changes in the working directory for the user to review.
- Present a summary of what changed across all iterations.
## Principles
- **Simple over clever**: Elegant code is simple code.
- **Readable over terse**: Brevity is good, but not at the cost of clarity.
- **Idiomatic over generic**: Use the language's strengths. Write TypeScript like TypeScript, not like Java.
- **Each iteration builds on the last**: Don't undo previous improvements. Deepen them.

View File

@@ -11,9 +11,6 @@
./nix.nix
./lua.nix
./sh.nix
# Claude Code
./claude.nix
];
home.packages = with pkgs; [

View File

@@ -4,9 +4,8 @@
# Useful for getting the import information from github info for flakes.
nix-prefetch-github
nixfmt # Formatter
nixfmt-rfc-style # Formatter
nil # Language Server
statix # Lints and suggestions for Nix
];
home.shellAliases = {
@@ -15,3 +14,4 @@
rebuild-boot = "sudo nixos-rebuild boot --flake ~/system-config --show-trace --print-build-logs --verbose";
};
}

View File

@@ -3,7 +3,7 @@
home.packages = with pkgs; [
vtsls # Language Server
eslint # Linter
prettier # Formatter
nodePackages.prettier # Formatter
tailwindcss-language-server # Language server for tailwind CSS
];
}

View File

@@ -3,7 +3,7 @@
programs.vscode = {
enable = true;
profiles.default.extensions = with pkgs.vscode-extensions; [
extensions = with pkgs.vscode-extensions; [
asvetliakov.vscode-neovim # Use embedded neovim editor
enkia.tokyo-night # Color theme
eamodio.gitlens # Show git info inline

View File

@@ -1,23 +0,0 @@
_: {
# Proton Mail is fully encrypted on the servers so it doesn't support POP3 or
# IMAP. In order to connect we need to set up a bridge that will download
# the mail and decrypt it locally.
services.protonmail-bridge.enable = true;
programs.thunderbird = {
enable = true;
profiles.drew = {
isDefault = true;
accountsOrder = [ "proton" ];
};
};
accounts.email.accounts.proton = {
enable = true;
primary = true;
address = "drew@blazestar.net";
realName = "Drew Haven";
thunderbird.enable = true;
};
}

View File

@@ -3,4 +3,5 @@
(include "./primary-statusbar.yuck")
(include "./secondary-statusbar.yuck")
(include "./system-monitor.yuck")
(include "./launcher.yuck")
(include "./vertical-statusbar.yuck")

View File

@@ -0,0 +1,74 @@
(defwindow launcher
:monitor '[ "<primary>", "DP-2", 0 ]'
:geometry (geometry
:x "100px"
:y "100px"
:anchor "top left"
)
:stacking "bottom"
:exclusive false
:focusable false
(box
:class "launcher-window stand-alone"
:orientation "v"
:spacing 4
:visible { arraylength(jq(workspaces-json-dp2, "map(select(.active and (.has_windows | not)))")) > 0 }
(box
:orientation "v"
:halign "start"
:spacing 4
:space-evenly false
(label
:text "Apps"
:halign "start"
)
(box
:orientation "h"
:halign "start"
:spacing 4
:space-evenly false
(button
:onclick "firefox"
(image
:class "launcher-icon"
:icon "firefox"
:icon-size "dialog"
)
)
)
)
(box
:orientation "v"
:halign "start"
:spacing 4
:space-evenly false
(label
:text "Games"
:halign "start"
)
(box
:orientation "h"
:halign "start"
:spacing 4
:space-evenly false
(button
;; :onclick "env LUTRIS_SKIP_INIT=1 lutris lutris:rungameid/1"
:onclick "/home/drew/.local/bin/wow.sh >/tmp/wow.log 2>&1"
(image
:class "launcher-icon"
:image-width 48
:path "/home/drew/.local/share/icons/hicolor/128x128/apps/lutris_battlenet.png"
)
)
;; (button
;; :onclick "steam steam://rungameid/1145350"
;; (image
;; :class "launcher-icon"
;; :icon "steam_icon_1145350"
;; :icon-size "dialog"
;; )
;;)
)
)
)
)

View File

@@ -5,7 +5,8 @@ set -e
handle_event() {
case $1 in
focusedmon* | workspace*) active_workspace;;
focusedmon*) active_workspace;;
workspace*) active_workspace;;
esac
}

View File

@@ -12,8 +12,8 @@ handle_event() {
}
list_workspaces() {
focused=$(hyprctl activeworkspace -j | jq '.id')
active=$(hyprctl monitors -j | jq 'map(select(.name == "'$monitor'")) | .[0].activeWorkspace.id')
focused=`hyprctl activeworkspace -j | jq '.id'`
active=`hyprctl monitors -j | jq 'map(select(.name == "'$monitor'")) | .[0].activeWorkspace.id'`
# Explanation
# 1. Select only workspaces on the current monitor.
# 2. Remove duplicates (might be a bug with split-monitor-workspaces

View File

@@ -45,7 +45,7 @@
(system-monitor-perf-gpu)
)
(disks-vega)
(system-monitor-net :interface "wlp5s0")
(system-monitor-net :interface "enp3s0")
(system-monitor-audio)
)
)

View File

@@ -1,50 +1,15 @@
{ pkgs, ... }:
{
config,
pkgs,
lib,
...
}:
{
options = {
programs.eww.widgets = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
};
config = {
home = {
packages = with pkgs; [
home.packages = with pkgs; [
# For Noto Sans NF
# 25.05
nerd-fonts.noto
# 24.11
# (nerdfonts.override { fonts = [ "NotoSans" ]; })
];
file.".config/eww".source = ./config;
};
programs.eww = {
enable = true;
};
systemd.user.services.eww = lib.mkIf (config.programs.eww.widgets != [ ]) {
Unit = {
Description = "Eww widgets";
After = [ "hyprland-session.target" ]; # Start only after hyprland
PartOf = [ "hyprland-session.target" ]; # Force it to exit if hyprland does
};
Service = {
Type = "oneshot";
RemainAfterExit = true;
# Eww won't start right if Hyprland hasn't finished initializing.
ExecStartPre = "${pkgs.coreutils}/bin/sleep 2";
ExecStart = "${pkgs.eww}/bin/eww open-many ${lib.concatStringsSep " " config.programs.eww.widgets}";
ExecStop = "${pkgs.eww}/bin/eww kill";
};
Install = {
WantedBy = [ "hyprland-session.target" ];
};
};
configDir = ./config;
};
}

View File

@@ -16,7 +16,7 @@ let
# '';
warcraftLogsUploader = pkgs.fetchurl {
name = "warcraftlogs-client";
url = "https://github.com/RPGLogs/Uploaders-warcraftlogs/releases/download/v8.17.47/warcraftlogs-v8.17.47.AppImage";
url = "https://github.com/RPGLogs/Uploaders-warcraftlogs/releases/download/v8.16.56/warcraftlogs-v8.16.56.AppImage";
sha256 = "1aypr3ffy6lq0qj64d48c7n54nfs72404xb2kpxsw5slqh66imw6";
};
warcraftLogsIcon = pkgs.fetchurl {
@@ -27,18 +27,13 @@ let
raiderioClient = pkgs.fetchurl {
name = "raiderio-client";
url = "https://raider.io/client/download/linux";
sha256 = "0wcw53bgr9dr02x1ci2jlnc5irpiqxqxgs2hpbrsnj67q50nvlm9";
sha256 = "1iny8zhp12x40mnxxr7p6kbyyvxf16373d2qa8idxs3hw5fz7gnx";
};
raiderioIcon = pkgs.fetchurl {
name = "raiderio-icon";
url = "https://cdn.raiderio.net/images/brand/Mark_2ColorWhite.png";
sha256 = "13311vi04lv6bp7jvnhinbcfah4ghn9h85cl3wzysz3aaxs73988";
};
curseForgeClient = pkgs.fetchurl {
name = "curseforge-client";
url = "https://curseforge.overwolf.com/downloads/curseforge-latest-linux.AppImage";
sha256 = "0w4snj0f0lpdc2z0iq8r10add49jaa3z10xz2cpmfh1dmm57yvlf";
};
in
{
home.packages = with pkgs; [
@@ -52,7 +47,6 @@ in
})
protonup-ng
protonplus
protontricks
vulkan-tools # useful for debugging Vulkan issues
# WoW addon updater
@@ -65,8 +59,7 @@ in
# };
# wowup needs options to work under wayland.
xdg.desktopEntries = {
wowup-cf = local.electronDesktopEntry {
xdg.desktopEntries.wowup-cf = local.electronDesktopEntry {
name = "WowUp";
exec = "wowup-cf --no-sandbox";
terminal = false;
@@ -78,7 +71,7 @@ in
];
};
warcraftLogs = {
xdg.desktopEntries.warcraftLogs = {
name = "WarcraftLogs";
exec = "${pkgs.appimage-run}/bin/appimage-run ${warcraftLogsUploader}";
terminal = false;
@@ -88,7 +81,7 @@ in
categories = [ "Game" ];
};
raiderio = {
xdg.desktopEntries.raiderio = local.electronDesktopEntry {
name = "Raider.io";
exec = "${pkgs.appimage-run}/bin/appimage-run ${raiderioClient}";
terminal = false;
@@ -98,35 +91,20 @@ in
categories = [ "Game" ];
};
curseforge = {
name = "Curseforge";
exec = "${pkgs.appimage-run}/bin/appimage-run ${curseForgeClient}";
terminal = false;
type = "Application";
comment = "Curseforge Client";
categories = [ "Game" ];
};
};
wayland.windowManager.hyprland.settings.windowrulev2 = [
# Set up full-screen games on monitor 1 (since window 0 is the vertical one.)
"monitor 1,class:^steam_app_\d+$"
"float,class:^steam_app_\d+$"
"monitor 1,fullscreen:1"
"center,class:^steam_app_\d+$"
# TODO: Merge into config
# wayland.windowManager.hyprland.settings.windowrule = [
# # Set up full-screen games on monitor 1 (since window 0 is the vertical one.)
# # "match:class ^steam_app_\d+$, float"
# "match:class ^steam_app_\d+$, fullscreen 1, monitor 1"
# "match:class ^steam_app_\d+$, center on"
#
# # Make sure WoW spawns on the right monitor and that Battlenet floats so it renders correctly
# "match:title ^World of Warcraft$, monitor 1"
# "match:title ^World of Warcraft$, fullscreen on"
# "match:title ^Battle.net$, monitor 1"
# "match:title ^Battle.net$, float on"
#
# # Make Balatro into a regular window.
# "match:title ^Balatro$, monitor 1"
# "match:title ^Balatro$, tile on"
#
# # Load Cyberpunk 2077 on the right monitor.
# "match:class steam_app_1091500, monitor 1"
# "match:class steam_app_1091500, fullscreen on"
# ];
# Make sure WoW spawns on the right monitor and that Battlenet floats so it renders correctly
"monitor 1,title:^World of Warcraft$"
"monitor 1,title:^Battle.net$"
"float,title:^Battle.net$"
# Make Balatro into a regular window.
"monitor 1,title:^Balatro$"
"tile,title:^Balatro$"
];
}

View File

@@ -2,8 +2,7 @@
{
programs.git = {
enable = true;
settings = {
alias = {
aliases = {
"co" = "checkout";
"s" = "status";
"b" = "branch";
@@ -12,9 +11,6 @@
"c" = "commit -am";
"l" = "log --oneline";
};
init = {
defaultBranch = "main";
};
extraConfig = {
pull = {
rebase = true;
@@ -22,9 +18,11 @@
log = {
date = "iso";
};
init = {
defaultBranch = "main";
};
};
# Enable the delta diff pager.
delta.enable = true;
};
};
}

View File

@@ -1,7 +0,0 @@
{
"workspace": {
"library": [
"/nix/store/ndq4688w4wjlip63msnlxld8zwcwr66l-hyprland-0.55.2/share/hypr/stubs"
]
}
}

View File

@@ -1,124 +0,0 @@
hl.curve("easeOutQuint", {
type = "bezier",
points = { { 0.23, 1.0 }, { 0.32, 1.0 } },
})
hl.curve("easeInOutCubic", {
type = "bezier",
points = { { 0.65, 0.05 }, { 0.36, 1.0 } },
})
hl.curve("linear", {
type = "bezier",
points = { { 0.0, 0.0 }, { 1.0, 1.0 } },
})
hl.curve("almostLinear", {
type = "bezier",
points = { { 0.5, 0.5 }, { 0.75, 1.0 } },
})
hl.curve("quick", {
type = "bezier",
points = { { 0.15, 0.0 }, { 0.1, 1.0 } },
})
hl.animation({
leaf = "global",
enabled = true,
speed = 10.0,
bezier = "default",
})
hl.animation({
leaf = "border",
enabled = true,
speed = 5.39,
bezier = "easeOutQuint",
})
hl.animation({
leaf = "windows",
enabled = true,
speed = 4.79,
bezier = "easeOutQuint",
})
hl.animation({
leaf = "windowsIn",
enabled = true,
speed = 4.1,
bezier = "easeOutQuint",
style = "popin 87%",
})
hl.animation({
leaf = "windowsOut",
enabled = true,
speed = 1.49,
bezier = "linear",
style = "popin 87%",
})
hl.animation({
leaf = "fadeIn",
enabled = true,
speed = 1.73,
bezier = "almostLinear",
})
hl.animation({
leaf = "fadeOut",
enabled = true,
speed = 1.46,
bezier = "almostLinear",
})
hl.animation({
leaf = "fade",
enabled = true,
speed = 3.03,
bezier = "quick",
})
hl.animation({
leaf = "layers",
enabled = true,
speed = 3.81,
bezier = "easeOutQuint",
})
hl.animation({
leaf = "layersIn",
enabled = true,
speed = 4.0,
bezier = "easeOutQuint",
style = "fade",
})
hl.animation({
leaf = "layersOut",
enabled = true,
speed = 1.5,
bezier = "linear",
style = "fade",
})
hl.animation({
leaf = "fadeLayersIn",
enabled = true,
speed = 1.79,
bezier = "almostLinear",
})
hl.animation({
leaf = "fadeLayersOut",
enabled = true,
speed = 1.39,
bezier = "almostLinear",
})
hl.animation({
leaf = "workspaces",
enabled = true,
speed = 1.94,
bezier = "almostLinear",
style = "fade",
})
hl.animation({
leaf = "workspacesIn",
enabled = true,
speed = 1.21,
bezier = "almostLinear",
style = "fade",
})
hl.animation({
leaf = "workspacesOut",
enabled = true,
speed = 1.94,
bezier = "almostLinear",
style = "fade",
})

View File

@@ -1,5 +0,0 @@
hl.on("hyprland.start", function()
hl.exec_cmd("nm-applet")
hl.exec_cmd("gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'")
hl.exec_cmd("gsettings set org.gnome.desktop.interface gtk-theme 'Adwaita-dark'")
end)

View File

@@ -1,99 +0,0 @@
local browser = "firefox --new-window"
local mainMod = "SUPER + ALT"
local menu = "rofi -show combi -combi-modes drun,ssh,run -theme ~/.config/rofi/launcher/style.rasi"
local terminal = "foot"
local split = require("hyprsplit")
hl.bind("SUPER + ALT + T", function()
hl.exec_cmd(terminal)
end)
hl.bind("SUPER + ALT + B", function()
hl.exec_cmd(browser)
end)
hl.bind("SUPER + ALT + D", function()
hl.exec_cmd(menu)
end)
hl.bind("SUPER + ALT + SHIFT + S", function()
hl.exec_cmd("hyprshot -m region --clipboard-only")
end)
hl.bind("SUPER + ALT + CTRL + SHIFT + S", function()
hl.exec_cmd("hyprshot -m region -o ~/Pictures")
end)
hl.bind("SUPER + ALT + C", function()
hl.exec_cmd("swaync-client -t")
end)
hl.bind("SUPER + ALT + CTRL + Q", function()
hl.exec_cmd("/home/drew/.config/rofi/powermenu/powermenu.sh")
end)
hl.bind("SUPER + ALT + X", function()
hl.exec_cmd("/home/drew/.config/rofi/powermenu/powermenu.sh")
end)
hl.bind("SUPER + ALT + F", hl.dsp.window.float({ action = "toggle" }))
hl.bind("SUPER + ALT + SHIFT + F", hl.dsp.window.fullscreen({ action = "toggle" }))
hl.bind("SUPER + ALT + CTRL + left", hl.dsp.window.resize({ x = -20, y = 0 }))
hl.bind("SUPER + ALT + CTRL + right", hl.dsp.window.resize({ x = 20, y = 0 }))
hl.bind("SUPER + ALT + CTRL + down", hl.dsp.window.resize({ x = 0, y = 20 }))
hl.bind("SUPER + ALT + CTRL + up", hl.dsp.window.resize({ x = 0, y = -20 }))
hl.bind("SUPER + ALT + R", hl.dsp.focus({ monitor = "next" }))
--
hl.bind("SUPER + ALT + r", hl.dsp.focus({ monitor = "l" }))
hl.bind("SUPER + ALT + s", hl.dsp.focus({ monitor = "r" }))
local hy3 = hl.plugin.hy3
hl.bind("SUPER + ALT + Q", hy3.kill_active())
hl.bind("SUPER + ALT + G", hy3.make_group("opposite"))
hl.bind("SUPER + ALT + W", hy3.make_group("tab", { toggle = true }))
hl.bind("SUPER + ALT + A", hy3.change_focus("raise"))
hl.bind("SUPER + ALT + Z", hy3.change_focus("lower"))
hl.bind("SUPER + ALT + O", hy3.change_group("opposite"))
hl.bind("SUPER + ALT + left", hy3.move_focus("left"))
hl.bind("SUPER + ALT + right", hy3.move_focus("right"))
hl.bind("SUPER + ALT + up", hy3.move_focus("up"))
hl.bind("SUPER + ALT + down", hy3.move_focus("down"))
hl.bind("SUPER + ALT + SHIFT + left", hy3.move_window("left"))
hl.bind("SUPER + ALT + SHIFT + right", hy3.move_window("right"))
hl.bind("SUPER + ALT + SHIFT + up", hy3.move_window("up"))
hl.bind("SUPER + ALT + SHIFT + down", hy3.move_window("down"))
-- Workspace binds
for i = 1, 10 do
hl.bind("SUPER + ALT + " .. (i % 10), split.dsp.focus({ workspace = tostring(i) }))
hl.bind("SUPER + ALT + SHIFT + " .. (i % 10), split.dsp.window.move({ workspace = tostring(i) }))
end
hl.bind("SUPER + ALT + SHIFT + r", hl.dsp.window.move({ monitor = "+1" }))
hl.bind(" + XF86AudioRaiseVolume", function()
hl.exec_cmd("swayosd-client --output-volume raise")
end, { locked = true })
hl.bind(" + XF86AudioLowerVolume", function()
hl.exec_cmd("swayosd-client --output-volume lower")
end, { locked = true })
hl.bind(" + XF86AudioMute", function()
hl.exec_cmd("swayosd-client --output-volume mute-toggle")
end, { locked = true })
hl.bind(" + XF86AudioMicMute", function()
hl.exec_cmd("swayosd-client --input-volume mute-toggle")
end, { locked = true })
hl.bind(" + XF86MonBrightnessUp", function()
hl.exec_cmd("brightnessctl s 10%+")
end, { locked = true })
hl.bind(" + XF86MonBrightnessDown", function()
hl.exec_cmd("brightnessctl s 10%-")
end, { locked = true })
hl.bind(" + XF86AudioNext", function()
hl.exec_cmd("playerctl next")
end, { locked = true })
hl.bind(" + XF86AudioPause", function()
hl.exec_cmd("playerctl play-pause")
end, { locked = true })
hl.bind(" + XF86AudioPlay", function()
hl.exec_cmd("playerctl play-pause")
end, { locked = true })
hl.bind(" + XF86AudioPrev", function()
hl.exec_cmd("playerctl previous")
end, { locked = true })
hl.bind("SUPER + mouse:272", hl.dsp.window.drag(), { mouse = true })
hl.bind("SUPER + mouse:273", hl.dsp.window.resize(), { mouse = true })
-- hl.bind(" + mouse:272", hl.dsp.hy3:focustab({ mouse })) --, { non_consuming = true }

View File

@@ -1,91 +0,0 @@
local split = require("hyprsplit")
split.config({
num_workspaces = 10,
persistent_workspaces = true,
})
hl.config({
general = {
allow_tearing = false,
border_size = 2,
col = {
active_border = {
colors = { "rgba(ff00ffee)", "rgba(ff9900ee)" },
angle = 45,
},
inactive_border = {
colors = { "rgba(00ffff99)", "rgba(0000cc99)" },
angle = 45,
},
},
gaps_in = 5,
gaps_out = 10,
layout = "hy3",
resize_on_border = true,
},
decoration = {
active_opacity = 1.0,
blur = {
enabled = true,
passes = 1,
size = 3,
vibrancy = 0.169600,
},
inactive_opacity = 1.0,
rounding = 5,
shadow = {
color = "rgba(1a1a1aee)",
enabled = true,
range = 5,
render_power = 3,
},
},
input = {
follow_mouse = 1,
kb_layout = "us",
kb_options = "compose:ralt",
sensitivity = 0,
touchpad = {
natural_scroll = false,
},
},
misc = {
disable_hyprland_logo = true,
force_default_wallpaper = 0,
vrr = 1,
},
debug = {
disable_logs = false,
},
cursor = {
no_hardware_cursors = true,
},
animations = {
enabled = true,
},
-- plugin = {
-- hy3 = {
-- group_inset = 10,
-- no_gaps_when_only = 0,
-- node_collapse_policy = 2,
-- tab_first_window = false,
-- tabs = {
-- height = 20,
-- rounding = 2,
-- text_height = 10,
-- },
-- },
-- },
})
hl.device({
name = "logitech-g502-1",
sensitivity = -0.2,
})
-- Additional configs
require("config.autostart")
require("config.binds")
require("config.animation")
require("config.windows")

View File

@@ -1,20 +0,0 @@
hl.window_rule({
match = {
class = "Rofi",
},
stay_focused = true,
})
-- Set border color to red if window is fullscreen
hl.window_rule({
match = { fullscreen = true },
border_color = "rgb(FF0000) rgb(880808)",
})
-- Suppress maximize events
hl.window_rule({
match = {
class = ".*",
},
suppress_event = "maximize",
})

View File

@@ -4,23 +4,23 @@
enable = true;
settings = {
general = {
lock_cmd = "pidof swaylock || ~/.config/swaylock/swaylock.sh"; # avoid starting multiple lock instances.
lock_cmd = "pidof hyprlock || hyprlock"; # avoid starting multiple hyprlock instances.
before_sleep_cmd = "loginctl lock-session"; # lock before suspend.
after_sleep_cmd = "hyprctl dispatch dpms on"; # to avoid having to press a key twice to turn on the display.
};
listener = [
{
timeout = 300; # 5min
timeout = 150; # 2.5min.
on-timeout = "brightnessctl -s set 10"; # set monitor backlight to minimum, avoid 0 on OLED monitor.
on-resume = "brightnessctl -r"; # monitor backlight restore.
}
{
timeout = 330; # 5.5 min
timeout = 300; # 5min
on-timeout = "loginctl lock-session"; # lock screen when timeout has passed
}
{
timeout = 600; # 10 min
timeout = 330; # 5.5min
on-timeout = "hyprctl dispatch dpms off"; # screen off when timeout has passed
on-resume = "hyprctl dispatch dpms on && brightnessctl -r"; # screen on when activity is detected after timeout has fired.
}

View File

@@ -1,126 +0,0 @@
{
pkgs,
inputs,
config,
lib,
...
}:
{
imports = [
../swaylock.nix
./hypridle.nix
../../options/monitors.nix
];
xdg.configFile = {
"hypr/hyprsplit" = {
source = "${inputs.hyprsplit}";
recursive = true;
};
"hypr/config" = {
source = ./config;
recursive = true;
};
};
home.packages = with pkgs; [
swayosd # volume pop-up
swaynotificationcenter # notifications
hyprpolkitagent # Privilege managent
hyprshot # Screenshot utility
# For clipboard management. See below.
wl-clipboard
xclip
];
wayland.windowManager.hyprland = {
enable = true;
# TODO: Switch to the newer LUA config?
configType = "lua";
plugins = [
# https://github.com/outfoxxed/hy3
pkgs.hyprlandPlugins.hy3
];
extraConfig = ''
require("config.hyprland")
'';
# Disable systemd because it conflicts with UWSM
systemd.enable = false;
};
programs.hyprlock = {
enable = false;
settings = {
general = {
disable_loading_bar = true;
hide_cursor = true;
};
background = {
monitor = "";
color = "rgb(000000)";
};
# Time
label = {
monitor = "";
text = ''
cmd[update:1000] echo "$(date '+%F %T')"
'';
font_size = 96;
font_family = "NotoSans Nerd Font";
color = "rgb(990000)";
position = "0, 0";
halign = "center";
valign = "center";
};
input-field = {
monitor = "";
size = "300, 40";
fade_on_empty = true;
outline_thickness = 2;
outer_color = "rgb(cc2222) rgb(661111)";
inner_color = "rgb(221111)";
font_color = "rgb(cc2222)";
dots_size = 0.2;
dots_spacing = 0.1;
placeholder_text = "";
fail_color = "rgb(cc3333)";
capslock_color = "rgb(cccc33)";
halign = "center";
valign = "center";
position = "0, -300px";
};
};
};
# UWSM sessions should not use Hyprland's variables but instead use UWSM's variable management.
# See https://wiki.hyprland.org/Configuring/Environment-variables/
home.file.".config/uwsm/env".text = ''
export XCURSOR_SIZE="32"
export HYPRCURSOR_SIZE="32"
export HYPRCURSOR_THEME="phinger"
# Nvidia config
export LIBVA_DRIVER_NAME="nvidia"
export __GLX_VENDOR_LIBRARY_NAME="nvidia"
# For window theming
export QT_QPA_PLATFORMTHEME="qt6ct # for Qt apps"
export GTK_THEME="Adwaita-dark"
# Tell electron apps they should use OZone for Wayland
export ELECTRON_OZONE_PLATFORM_HINT="auto"
'';
}

View File

@@ -0,0 +1,497 @@
{ pkgs, ... }:
{
imports = [
./hypr/hypridle.nix
];
home.packages = with pkgs; [
swayosd # volume pop-up
swaynotificationcenter # notifications
hyprpolkitagent # Privilege managent
hyprshot # Screenshot utility
# For clipboard management. See below.
wl-clipboard
xclip
];
wayland.windowManager.hyprland = {
enable = true;
plugins = [
# https://github.com/outfoxxed/hy3
pkgs.hyprlandPlugins.hy3
# https://github.com/shezdy/hyprsplit
pkgs.hyprlandPlugins.hyprsplit
];
# Disable systemd because it conflicts with UWSM
systemd.enable = false;
settings = {
monitor = [
"DP-2, 3440x1440, 0x0, 1"
"DP-1, 2560x1440, -1440x-510, 1, transform, 1"
];
"$terminal" = "foot";
"$menu" = "rofi -show combi -combi-modes drun,ssh,run -theme ~/.config/rofi/launcher/style.rasi";
"$browser" = "firefox";
exec-once = [
"nm-applet"
"sleep 2 && hyprpm reload -n"
"systemctl --user start hyprpolkitagent"
"gsettings set org.gnome.desktop.interface color-scheme \"prefer-dark\""
"gsettings set org.gnome.desktop.interface gtk-theme \"Adwaita-dark\""
# Hyprland doesn't paste into Firefox or Wine apps. This program is a workaround.
# See https://github.com/hyprwm/Hyprland/issues/2319
# -t text = Only handle text
# -w xclip -selection clipboard = Watch for events and invoke xclip
"wl-paste -t text -w xclip -selection clipboard"
];
general = {
gaps_in = 5;
gaps_out = 10;
border_size = 2;
# https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors
"col.active_border" = "rgba(eeeeffee) rgba(3366ffee) 45deg";
"col.inactive_border" = "rgba(595959aa)";
# Set to true enable resizing windows by clicking and dragging on borders and gaps
resize_on_border = true;
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
allow_tearing = false;
layout = "hy3";
};
# https://wiki.hyprland.org/Configuring/Variables/#decoration
decoration = {
rounding = 5;
# Change transparency of focused and unfocused windows
active_opacity = 1.0;
inactive_opacity = 1.0;
shadow = {
enabled = true;
range = 5;
render_power = 3;
color = "rgba(1a1a1aee)";
};
# https://wiki.hyprland.org/Configuring/Variables/#blur
blur = {
enabled = true;
size = 3;
passes = 1;
vibrancy = 0.1696;
};
};
# https://wiki.hyprland.org/Configuring/Variables/#animations
animations = {
enabled = "yes";
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
bezier = [
"easeOutQuint,0.23,1,0.32,1"
"easeInOutCubic,0.65,0.05,0.36,1"
"linear,0,0,1,1"
"almostLinear,0.5,0.5,0.75,1.0"
"quick,0.15,0,0.1,1"
];
animation = [
"global, 1, 10, default"
"border, 1, 5.39, easeOutQuint"
"windows, 1, 4.79, easeOutQuint"
"windowsIn, 1, 4.1, easeOutQuint, popin 87%"
"windowsOut, 1, 1.49, linear, popin 87%"
"fadeIn, 1, 1.73, almostLinear"
"fadeOut, 1, 1.46, almostLinear"
"fade, 1, 3.03, quick"
"layers, 1, 3.81, easeOutQuint"
"layersIn, 1, 4, easeOutQuint, fade"
"layersOut, 1, 1.5, linear, fade"
"fadeLayersIn, 1, 1.79, almostLinear"
"fadeLayersOut, 1, 1.39, almostLinear"
"workspaces, 1, 1.94, almostLinear, fade"
"workspacesIn, 1, 1.21, almostLinear, fade"
"workspacesOut, 1, 1.94, almostLinear, fade"
];
};
# https://wiki.hyprland.org/Configuring/Variables/#misc
misc = {
force_default_wallpaper = 0; # Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = true; # If true disables the random hyprland logo / anime girl background. :(
vrr = 1; # Enable variable refresh rate
};
cursor = {
no_hardware_cursors = true;
};
###############
### PLUGINS ###
###############
plugin = {
hy3 = {
# disable gaps when only one window is onscreen
# 0 - always show gaps
# 1 - hide gaps with a single window onscreen
# 2 - 1 but also show the window border
no_gaps_when_only = 0; # default: 0
# policy controlling what happens when a node is removed from a group,
# leaving only a group
# 0 = remove the nested group
# 1 = keep the nested group
# 2 = keep the nested group only if its parent is a tab group
node_collapse_policy = 2; # default: 2
# offset from group split direction when only one window is in a group
group_inset = 10; # default: 10
# if a tab group will automatically be created for the first window spawned in a workspace
tab_first_window = false;
# tab group settings
tabs = {
# height of the tab bar
height = 20; # default: 15
# padding between the tab bar and its focused node
# padding = <int> # default: 5
# the tab bar should animate in/out from the top instead of below the window
# from_top = <bool> # default: false
# rounding of tab bar corners
rounding = 2; # default: 3
# render the window title on the bar
# render_text = <bool> # default: true
# center the window title
# text_center = <bool> # default: false
# font to render the window title with
# text_font = <string> # default: Sans
# height of the window title
text_height = 10; # default: 8
# left padding of the window title
# text_padding = <int> # default: 3
# active tab bar segment color
# col.active = <color> # default: 0xff32b4ff
# urgent tab bar segment color
# col.urgent = <color> # default: 0xffff4f4f
# inactive tab bar segment color
# col.inactive = <color> # default: 0x80808080
# active tab bar text color
# col.text.active = <color> # default: 0xff000000
# urgent tab bar text color
# col.text.urgent = <color> # default: 0xff000000
# inactive tab bar text color
# col.text.inactive = <color> # default: 0xff000000
};
# autotiling settings
autotile = {
# enable autotile
# enable = <bool> # default: false
# make autotile-created groups ephemeral
# ephemeral_groups = <bool> # default: true
# if a window would be squished smaller than this width, a vertical split will be created
# -1 = never automatically split vertically
# 0 = always automatically split vertically
# <number> = pixel width to split at
# trigger_width = <int> # default: 0
# if a window would be squished smaller than this height, a horizontal split will be created
# -1 = never automatically split horizontally
# 0 = always automatically split horizontally
# <number> = pixel height to split at
# trigger_height = <int> # default: 0
# a space or comma separated list of workspace ids where autotile should be enabled
# it's possible to create an exception rule by prefixing the definition with "not:"
# workspaces = 1,2 # autotiling will only be enabled on workspaces 1 and 2
# workspaces = not:1,2 # autotiling will be enabled on all workspaces except 1 and 2
# workspaces = <string> # default: all
};
};
hyprsplit = {
num_workspaces = 10;
persistent_workspaces = true;
};
};
#############
### INPUT ###
#############
# https://wiki.hyprland.org/Configuring/Variables/#input
input = {
kb_layout = "us";
# kb_variant =
# kb_model =
kb_options = "compose:ralt";
# kb_rules =
follow_mouse = 1;
sensitivity = 0; # -1.0 - 1.0, 0 means no modification.
touchpad = {
natural_scroll = false;
};
};
# Example per-device config
# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more
device = {
name = "logitech-g502-1";
sensitivity = -0.2;
};
###################
### KEYBINDINGS ###
###################
# See https://wiki.hyprland.org/Configuring/Keywords/
"$mainMod" = "SUPER + ALT";
bind = [
"$mainMod, T, exec, $terminal"
"$mainMod, B, exec, $browser"
"$mainMod, D, exec, $menu"
"$mainMod + SHIFT, S, exec, hyprshot -m region --clipboard-only"
"$mainMod, C, exec, swaync-client -t"
"$mainMod + L_CONTROL, Q, exec, /home/drew/.config/rofi/powermenu/powermenu.sh"
"$mainMod, X, exec, /home/drew/.config/rofi/powermenu/powermenu.sh"
# "$mainMod, X, exec, hyprlock"
# "$mainMod + SHIFT, X, exec, systemctl suspend"
"$mainMod, F, togglefloating,"
"$mainMod + SHIFT, F, fullscreen, 1"
"$mainMod + CTRL, left, resizeactive, -20 0"
"$mainMod + CTRL, right, resizeactive, 20 0"
"$mainMod + CTRL, down, resizeactive, 0 20"
"$mainMod + CTRL, up, resizeactive, 0 -20"
# Monitors
"$mainMod, R, focusmonitor, next"
"$mainMod + CTRL, R, movecurrentworkspacetomonitor, l"
"$mainMod + CTRL, S, movecurrentworkspacetomonitor, r"
# Hy3 Stuff
"$mainMod, Q, hy3:killactive,"
"$mainMod, G, hy3:makegroup, opposite, ephemeral"
"$mainMod, W, hy3:makegroup, tab, ephemeral"
"$mainMod + SHIFT, W, hy3:changegroup, toggletab"
"$mainMod, A, hy3:changefocus, raise"
"$mainMod, Z, hy3:changefocus, lower"
"$mainMod, O, hy3:changegroup, opposite"
"$mainMod, left, hy3:movefocus, l"
"$mainMod, right, hy3:movefocus, r"
"$mainMod, up, hy3:movefocus, u"
"$mainMod, down, hy3:movefocus, d"
"$mainMod, r, focusmonitor, l"
"$mainMod, s, focusmonitor, r"
"$mainMod + SHIFT, left, hy3:movewindow, l"
"$mainMod + SHIFT, right, hy3:movewindow, r"
"$mainMod + SHIFT, up, hy3:movewindow, u"
"$mainMod + SHIFT, down, hy3:movewindow, d"
# Switch workspaces with mainMod + [0-9]
"$mainMod, 1, split:workspace, 1"
"$mainMod, 2, split:workspace, 2"
"$mainMod, 3, split:workspace, 3"
"$mainMod, 4, split:workspace, 4"
"$mainMod, 5, split:workspace, 5"
"$mainMod, 6, split:workspace, 6"
"$mainMod, 7, split:workspace, 7"
"$mainMod, 8, split:workspace, 8"
"$mainMod, 9, split:workspace, 9"
"$mainMod, 0, split:workspace, 10"
# Move active window to a workspace with mainMod + SHIFT + [0-9]
# Note: using hy3:movetoworkspace instead of split:movetoworkspace.
# The plugins are compatible.
"$mainMod SHIFT, 1, hy3:movetoworkspace, 1, follow"
"$mainMod SHIFT, 2, hy3:movetoworkspace, 2, follow"
"$mainMod SHIFT, 3, hy3:movetoworkspace, 3, follow"
"$mainMod SHIFT, 4, hy3:movetoworkspace, 4, follow"
"$mainMod SHIFT, 5, hy3:movetoworkspace, 5, follow"
"$mainMod SHIFT, 6, hy3:movetoworkspace, 6, follow"
"$mainMod SHIFT, 7, hy3:movetoworkspace, 7, follow"
"$mainMod SHIFT, 8, hy3:movetoworkspace, 8, follow"
"$mainMod SHIFT, 9, hy3:movetoworkspace, 9, follow"
"$mainMod SHIFT, 0, hy3:movetoworkspace, 10, follow"
# These don't take monitor numbers, but rather differences. It's next/prev but
# it'll always just move it to the other monitor
"$mainMod SHIFT, r, movewindow, mon:+1"
];
# m = mouse bindings
# These use just SUPER so I can hit them with my left hand
bindm = [
"SUPER, mouse:272, movewindow"
"SUPER, mouse:273, resizewindow"
];
# n = non-consuming
bindn = [
", mouse:272, hy3:focustab, mouse"
];
# l = available when locked
bindl = [
# Laptop multimedia keys for volume and LCD brightness
",XF86AudioRaiseVolume, exec, swayosd-client --output-volume raise"
",XF86AudioLowerVolume, exec, swayosd-client --output-volume lower"
",XF86AudioMute, exec, swayosd-client --output-volume mute-toggle"
",XF86AudioMicMute, exec, swayosd-client --input-volume mute-toggle"
",XF86MonBrightnessUp, exec, brightnessctl s 10%+"
",XF86MonBrightnessDown, exec, brightnessctl s 10%-"
# Requires playerctl
", XF86AudioNext, exec, playerctl next"
", XF86AudioPause, exec, playerctl play-pause"
", XF86AudioPlay, exec, playerctl play-pause"
", XF86AudioPrev, exec, playerctl previous"
];
##############################
### WINDOWS AND WORKSPACES ###
##############################
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
windowrulev2 = [
# Ignore maximize requests from apps. You'll probably like this.
"suppressevent maximize, class:.*"
# Fix some dragging issues with XWayland
"nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0"
# Make right-click backgrounds not transparent. The menus have no class or title.
"opaque,class:(),title:()"
"noshadow,class:(),title:()"
"noblur,class:(),title:()"
];
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
workspace = [
"1, defaultName:L1"
"2, defaultName:L2"
"3, defaultName:L3"
"4, defaultName:L4"
"5, defaultName:L5"
"6, defaultName:L6"
"7, defaultName:L7"
"8, defaultName:L8"
"9, defaultName:L9"
"10, defaultName:L10"
"11, defaultName:R1"
"12, defaultName:R2"
"13, defaultName:R3"
"14, defaultName:R4"
"15, defaultName:R5"
"16, defaultName:R6"
"17, defaultName:R7"
"18, defaultName:R8"
"19, defaultName:R9"
"20, defaultName:R10"
];
};
};
programs.hyprlock = {
enable = true;
settings = {
general = {
disable_loading_bar = true;
hide_cursor = true;
};
background = {
monitor = "";
color = "rgb(000000)";
};
# Time
label = {
monitor = "";
text = ''
cmd[update:1000] echo "$(date '+%F %T')"
'';
font_size = 96;
font_family = "NotoSans Nerd Font";
color = "rgb(990000)";
position = "0, 0";
halign = "center";
valign = "center";
};
input-field = {
monitor = "";
size = "300, 40";
fade_on_empty = true;
outline_thickness = 2;
outer_color = "rgb(cc2222) rgb(661111)";
inner_color = "rgb(221111)";
font_color = "rgb(cc2222)";
dots_size = 0.2;
dots_spacing = 0.1;
placeholder_text = "";
fail_color = "rgb(cc3333)";
capslock_color = "rgb(cccc33)";
halign = "center";
valign = "center";
position = "0, -300px";
};
};
};
# UWSM sessions should not use Hyprland's variables but instead use UWSM's variable management.
# See https://wiki.hyprland.org/Configuring/Environment-variables/
home.file.".config/uwsm/env".text = ''
export XCURSOR_SIZE="32"
export HYPRCURSOR_SIZE="32"
export HYPRCURSOR_THEME="phinger"
# Nvidia config
export LIBVA_DRIVER_NAME="nvidia"
export __GLX_VENDOR_LIBRARY_NAME="nvidia"
# For window theming
export QT_QPA_PLATFORMTHEME="qt6ct # for Qt apps"
export GTK_THEME="Adwaita-dark"
# Tell electron apps they should use OZone for Wayland
export ELECTRON_OZONE_PLATFORM_HINT="auto"
'';
}

View File

@@ -1,17 +0,0 @@
{ config, ... }:
{
services.hyprpaper = {
enable = true;
settings = {
splash = false; # Disable the little quotes they put on there
wallpaper = map (mon: {
monitor = mon;
# Each monitor gets a folder under wallpaper that containes the current images.
path = "~/Pictures/Wallpaper/${mon}";
timeout = 3600;
recursive = true;
fit_mode = "cover";
}) (builtins.attrNames config.hardware.monitors);
};
};
}

View File

@@ -1,7 +0,0 @@
{ pkgs, ... }:
{
home.packages = with pkgs; [
gimp3
inkscape
];
}

View File

@@ -1,28 +1,19 @@
{
config,
pkgs,
...
}:
let
browser = "firefox";
in
{ pkgs, ... }:
{
imports = [
./audio.nix
./hypr/hyprland.nix
./hyprland.nix
./rofi/rofi.nix
./sway.nix
../apps/element.nix
../apps/discord.nix
./terminal.nix
../options/monitors.nix
];
home = {
packages = with pkgs; [
# Desktop Applications
gimp3
vlc
signal-desktop
# Common utilities
feh
@@ -37,17 +28,14 @@ in
playerctl # for universal play/pause etc
wirelesstools
waypaper # Wallpaper switcher
];
pointerCursor = {
enable = true;
gtk.enable = true;
name = "phinger-cursors";
size = 16;
package = pkgs.bibata-cursors;
name = "Bibata-Modern-Classic";
# package = pkgs.phinger-cursors;
# name = "phinger-cursors";
package = pkgs.phinger-cursors;
};
# Hint for electron apps to use wayland
@@ -61,11 +49,9 @@ in
programs = {
# browsers
firefox = {
enable = true;
configPath = "${config.xdg.configHome}/mozilla/firefox";
};
chromium.enable = true;
firefox.enable = true;
qutebrowser.enable = true;
librewolf.enable = true;
};
# GTK settings
@@ -78,14 +64,10 @@ in
gtk3.extraConfig = {
gtk-application-prefer-dark-theme = true;
};
gtk4 = {
# Don't use GTK4 themes. See https://stopthemingmy.app/
theme = null;
extraConfig = {
gtk4.extraConfig = {
gtk-application-prefer-dark-theme = true;
};
};
};
# This is where the real magic happens to tell the applications to prefer
# dark mode. For example, Element reads from this setting.
@@ -101,18 +83,4 @@ in
platformTheme.name = "adwaita";
style.name = "adwaita-dark";
};
# Default apps
xdg.mimeApps = {
enable = true;
defaultApplications = {
"text/html" = [ "${browser}.desktop" ];
"default-web-browser" = [ "${browser}.desktop" ];
"x-scheme-handler/http" = [ "${browser}.desktop" ];
"x-scheme-handler/https" = [ "${browser}.desktop" ];
"x-scheme-handler/about" = [ "${browser}.desktop" ];
"x-scheme-handler/unknown" = [ "${browser}.desktop" ];
};
};
home.sessionVariables.DEFAULT_BROWSER = "${pkgs."${browser}"}/bin/${browser}";
}

View File

@@ -1,50 +0,0 @@
{ pkgs, ... }:
{
home.packages = with pkgs; [ sshfs ];
systemd.user.services =
let
mountArchive = ''
${pkgs.sshfs}/bin/sshfs \
-f \
-o reconnect \
-o compression=yes \
-o ServerAliveInterval=15 \
-o ServerAliveCountMax=3 \
-o IdentityFile=%h/.ssh/id_ed25519 \
-o StrictHostKeyChecking=yes \
drew@mcp:/tank/archive/drew \
%h/archive
'';
# unmountArchive = "/run/wrappers/bin/fusermount -u %h/archive";
in
{
# Mounts the archive when the system is online.
mcp-archive = {
Unit = {
Description = "SSHFS mount for remote archive";
# Start only after the network is online
After = [ "network-online.target" ];
# Stop this service if the network stops
StopPropagatedFrom = [ "network-online.target" ];
};
Service = {
Type = "simple";
ExecStartPre = "/run/current-system/sw/bin/mkdir -p %h/archive";
ExecStart = mountArchive;
Restart = "on-failure";
RestartSec = "10s";
};
Install = {
# It doesn't like to disconnect on sleep, so not making it automount at all
# WantedBy = [ "default.target" ];
};
};
};
}

View File

@@ -1,19 +0,0 @@
{
"extras": [
"lazyvim.plugins.extras.coding.mini-comment",
"lazyvim.plugins.extras.coding.mini-surround",
"lazyvim.plugins.extras.editor.snacks_picker",
"lazyvim.plugins.extras.lang.astro",
"lazyvim.plugins.extras.lang.haskell",
"lazyvim.plugins.extras.lang.json",
"lazyvim.plugins.extras.lang.markdown",
"lazyvim.plugins.extras.lang.nix",
"lazyvim.plugins.extras.lang.rust",
"lazyvim.plugins.extras.lang.tailwind",
"lazyvim.plugins.extras.lang.toml",
"lazyvim.plugins.extras.lang.typescript",
"lazyvim.plugins.extras.ai.sidekick"
],
"install_version": 8,
"version": 8
}

View File

@@ -2,6 +2,7 @@
-- Default keymaps that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/keymaps.lua
-- Add any additional keymaps here
for _, map in ipairs({ { "<C-up>", "k" }, { "<C-down>", "j" }, { "<C-left>", "h" }, { "<C-right>", "l" } }) do
vim.keymap.set("n", map[1], "<C-w>" .. map[2], { noremap = true, silent = true })
end
vim.keymap.set("n", "<C-up>", "<C-w>k", { noremap = true, silent = true })
vim.keymap.set("n", "<C-down>", "<C-w>j", { noremap = true, silent = true })
vim.keymap.set("n", "<C-left>", "<C-w>h", { noremap = true, silent = true })
vim.keymap.set("n", "<C-right>", "<C-w>l", { noremap = true, silent = true })

View File

@@ -4,6 +4,3 @@
-- Creates a shortcut for adding the directory of the current buffer when specifying a file
vim.cmd("cnoreabbrev %. %:h<Tab>")
-- Set snacks as the preferred picker.
vim.g.lazyvim_picker = "snacks"

View File

@@ -0,0 +1,4 @@
return {
"augmentcode/augment.vim",
enable = true,
}

View File

@@ -31,7 +31,7 @@ return {
cmp.show()
end,
},
["<Tab>"] = { "select_and_accept", "snippet_forward", "fallback" },
["<C-enter>"] = { "select_and_accept" },
},
},
}

View File

@@ -0,0 +1,14 @@
return {
{
"zbirenbaum/copilot.lua",
opts = {
filetypes = {
markdown = false,
help = false,
},
suggestion = {
enabled = false,
},
},
},
}

View File

@@ -1,5 +1,5 @@
return {
-- Maeson installs it's own binaries that are incompatible with NixOS.
{ "mason-org/mason.nvim", enabled = false },
{ "mason-org/mason-lspconfig.nvim", enabled = false },
{ "williamboman/mason.nvim", enabled = false },
{ "williamboman/mason-lspconfig.nvim", enabled = false },
}

View File

@@ -3,23 +3,9 @@ return {
"neovim/nvim-lspconfig",
opts = {
servers = {
-- Lua
lua_ls = {
root_markers = {
".luarc.json",
".git",
},
},
-- Nix
lua_ls = {},
nil_ls = {},
-- Typescript
vtsls = {},
-- Haskell
hls = {},
-- Copilot
copilot = {
enabled = true,
},
},
codelens = {
enable = true,

View File

@@ -1,4 +1,3 @@
-- https://github.com/MeanderingProgrammer/render-markdown.nvim?tab=readme-ov-file#setup
return {
"MeanderingProgrammer/render-markdown.nvim",
opts = {

View File

@@ -1,38 +1,10 @@
return {
{
"nvim-mini/mini.surround",
"echasnovski/mini.surround",
enable = true,
keys = function(_, keys)
-- Populate the keys based on the user's options
local opts = LazyVim.opts("mini.surround")
local mappings = {
{ opts.mappings.add, desc = "Add Surrounding", mode = { "n", "v" } },
{ opts.mappings.delete, desc = "Delete Surrounding" },
{ opts.mappings.find, desc = "Find Right Surrounding" },
{ opts.mappings.find_left, desc = "Find Left Surrounding" },
{ opts.mappings.highlight, desc = "Highlight Surrounding" },
{ opts.mappings.replace, desc = "Replace Surrounding" },
{ opts.mappings.update_n_lines, desc = "Update `MiniSurround.config.n_lines`" },
}
mappings = vim.tbl_filter(function(m)
return m[1] and #m[1] > 0
end, mappings)
return vim.list_extend(mappings, keys)
end,
opts = {
mappings = {
add = "gsa", -- Add surrounding in Normal and Visual modes
delete = "gsd", -- Delete surrounding
find = "gsf", -- Find surrounding (to the right)
find_left = "gsF", -- Find surrounding (to the left)
highlight = "gsh", -- Highlight surrounding
replace = "gsr", -- Replace surrounding
update_n_lines = "gsn", -- Update `n_lines`
},
},
},
{
"nvim-mini/mini.comment",
"echasnovski/mini.comment",
enable = true,
},
}

View File

@@ -1,7 +1,22 @@
return {
"obsidian-nvim/obsidian.nvim",
"epwalsh/obsidian.nvim",
version = "*", -- recommended, use latest release instead of latest commit
lazy = true,
ft = "markdown",
-- Replace the above line with this if you only want to load obsidian.nvim for markdown files in your vault:
-- event = {
-- -- If you want to use the home shortcut '~' here you need to call 'vim.fn.expand'.
-- -- E.g. "BufReadPre " .. vim.fn.expand "~" .. "/my-vault/*.md"
-- -- refer to `:h file-pattern` for more examples
-- "BufReadPre path/to/my-vault/*.md",
-- "BufNewFile path/to/my-vault/*.md",
-- },
dependencies = {
-- Required.
"nvim-lua/plenary.nvim",
-- For the picker
"nvim-telescope/telescope.nvim",
},
opts = {
workspaces = {
{
@@ -32,21 +47,21 @@ return {
time_format = "%H:%M",
},
checkbox = {
order = { " ", "x", "/", ">", "~", "!" },
},
ui = {
-- Disable the UI features and let render-markdown.nvim handle it.
enable = false,
-- Even with UI disabled, this determines the order of checkbox states for the smart action.
checkboxes = {
[" "] = { char = "", hl_group = "ObsidianTodo" },
["x"] = { char = "", hl_group = "ObsidianDone" },
["/"] = { char = "", hl_group = "ObsidianDone" },
[">"] = { char = "", hl_group = "ObsidianDone" },
},
},
link = {
style = "wiki",
},
-- Disable the legacy commands and silence the deprecation warning.
legacy_commands = false,
-- Put the note ID in the wiki links
wiki_link_func = "prepend_note_id",
preferred_link_style = "wiki",
-- Customize how note IDs are generated given an optional title.
---@param title string|?
@@ -58,11 +73,9 @@ return {
return title:gsub("[^A-Za-z0-9-_]", ""):lower()
else
-- If title is nil, just put the date and four random characters
local suffix = ""
for _ = 1, 4 do
suffix = suffix .. string.char(math.random(65, 90))
return os.date("%Y-%m-%d ") .. string.char(math.random(65, 90))
end
return os.date("%Y-%m-%d") .. "-" .. suffix
end
end,
},

View File

@@ -1,13 +0,0 @@
return {
"salkin-mada/openscad.nvim",
config = function()
vim.g.openscad_load_snippets = true
vim.g.openscad_default_mappings = true
vim.g.openscad_fuzzy_finder = "snacks"
require("openscad")
end,
dependencies = {
-- Can fall back to Snacks instead.
-- "ibhagwan/fzf-lua",
},
}

View File

@@ -1,95 +0,0 @@
return {
-- https://github.com/folke/sidekick.nvim
"folke/sidekick.nvim",
opts = {
cli = {
mux = {
enabled = false, -- I haven't bothered to include tmux yet
backend = "tmux",
},
},
},
keys = {
{
"<tab>",
function()
-- if there is a next edit, jump to it, otherwise apply it if any
if not require("sidekick").nes_jump_or_apply() then
return "<Tab>" -- fallback to normal tab
end
end,
expr = true,
desc = "Goto/Apply Next Edit Suggestion",
},
{
"<c-.>",
function()
require("sidekick.cli").focus()
end,
desc = "Sidekick Focus",
mode = { "n", "t", "i", "x" },
},
{
"<leader>aa",
function()
require("sidekick.cli").toggle()
end,
desc = "Sidekick Toggle CLI",
},
{
"<leader>as",
function()
require("sidekick.cli").select()
end,
-- Or to select only installed tools:
-- require("sidekick.cli").select({ filter = { installed = true } })
desc = "Select CLI",
},
{
"<leader>ad",
function()
require("sidekick.cli").close()
end,
desc = "Detach a CLI Session",
},
{
"<leader>at",
function()
require("sidekick.cli").send({ msg = "{this}" })
end,
mode = { "x", "n" },
desc = "Send This",
},
{
"<leader>af",
function()
require("sidekick.cli").send({ msg = "{file}" })
end,
desc = "Send File",
},
{
"<leader>av",
function()
require("sidekick.cli").send({ msg = "{selection}" })
end,
mode = { "x" },
desc = "Send Visual Selection",
},
{
"<leader>ap",
function()
require("sidekick.cli").prompt()
end,
mode = { "n", "x" },
desc = "Sidekick Select Prompt",
},
-- Example of a keybinding to open Claude directly
-- {
-- "<leader>ac",
-- function()
-- require("sidekick.cli").toggle({ name = "claude", focus = true })
-- end,
-- desc = "Sidekick Toggle Claude",
-- },
},
}

View File

@@ -1,12 +1,7 @@
return {
"folke/snacks.nvim",
opts = function(_, opts)
opts = vim.tbl_deep_extend("force", opts, {
styles = {
notification = {
wo = { wrap = true }, -- wrap notifications
},
},
---@type snacks.Config
opts = {
picker = {
smart = {
-- Remove the "recent" picker so we don't get things from other directories.
@@ -20,52 +15,286 @@ return {
cwd_bonus = true,
-- Give more weight to files that are more recent
history_bonus = true,
-- Give more weight to places where the filename is part of the match
filename_bonus = true,
},
},
sources = {
explorer = {
layout = { layout = { position = "right" } },
},
},
-- This only supports the Kitty graphics protocol.
-- See
-- https://github.com/folke/snacks.nvim/blob/main/docs/image.md
-- https://github.com/obsidian-nvim/obsidian.nvim/wiki/Images
-- image = {
-- resolve = function(path, src)
-- if require("obsidian.api").path_is_note(path) then
-- return require("obsidian.api").resolve_image_path(src)
-- end
-- end,
-- },
},
})
Snacks.toggle({
name = "Color Column",
get = function()
return vim.o.colorcolumn == "80"
end,
set = function(state)
if state then
vim.o.colorcolumn = "80"
vim.cmd([[highlight ColorColumn guibg=#202020]])
else
vim.o.colorcolumn = ""
vim.cmd([[highlight ColorColumn guibg=None]])
end
end,
}):map("<leader>ut", { desc = "Toggle Color Column" })
return opts
end,
keys = {
{
"<leader><space>",
function()
Snacks.picker.smart()
Snacks.picker.smart({
-- Remove the "recent" picker so we don't get things from other directories.
multi = { "buffers", "files" },
matcher = {
-- sort even when the search string is empty
sort_empty = false,
-- Enable frecensy for matchers. This puts more common files near
-- the top This includes files that aren't open and can put files I
-- am done with above open files, so it's off.
frecency = false,
-- Make sure files in the current directory are prioritized
cwd_bonus = true,
-- Give more weight to files that are more recent
history_bonus = true,
-- Give more weight to places where the filename is part of the match
filename_bonus = true,
},
})
end,
desc = "Smart Find Files",
},
-- The rest of these are just default bindings. Setting the one binding above seems to override the others.
{
"<leader>,",
function()
Snacks.picker.buffers()
end,
desc = "Buffers",
},
{ "<leader>/", LazyVim.pick("grep"), desc = "Grep (Root Dir)" },
{
"<leader>:",
function()
Snacks.picker.command_history()
end,
desc = "Command History",
},
{
"<leader>n",
function()
Snacks.picker.notifications()
end,
desc = "Notification History",
},
-- find
{
"<leader>fb",
function()
Snacks.picker.buffers()
end,
desc = "Buffers",
},
{
"<leader>fB",
function()
Snacks.picker.buffers({ hidden = true, nofile = true })
end,
desc = "Buffers (all)",
},
{ "<leader>fc", LazyVim.pick.config_files(), desc = "Find Config File" },
{ "<leader>ff", LazyVim.pick("files"), desc = "Find Files (Root Dir)" },
{ "<leader>fF", LazyVim.pick("files", { root = false }), desc = "Find Files (cwd)" },
{
"<leader>fg",
function()
Snacks.picker.git_files()
end,
desc = "Find Files (git-files)",
},
{ "<leader>fr", LazyVim.pick("oldfiles"), desc = "Recent" },
{
"<leader>fR",
function()
Snacks.picker.recent({ filter = { cwd = true } })
end,
desc = "Recent (cwd)",
},
{
"<leader>fp",
function()
Snacks.picker.projects()
end,
desc = "Projects",
},
-- git
{
"<leader>gd",
function()
Snacks.picker.git_diff()
end,
desc = "Git Diff (hunks)",
},
{
"<leader>gs",
function()
Snacks.picker.git_status()
end,
desc = "Git Status",
},
{
"<leader>gS",
function()
Snacks.picker.git_stash()
end,
desc = "Git Stash",
},
-- Grep
{
"<leader>sb",
function()
Snacks.picker.lines()
end,
desc = "Buffer Lines",
},
{
"<leader>sB",
function()
Snacks.picker.grep_buffers()
end,
desc = "Grep Open Buffers",
},
{ "<leader>sg", LazyVim.pick("live_grep"), desc = "Grep (Root Dir)" },
{ "<leader>sG", LazyVim.pick("live_grep", { root = false }), desc = "Grep (cwd)" },
{
"<leader>sp",
function()
Snacks.picker.lazy()
end,
desc = "Search for Plugin Spec",
},
{ "<leader>sw", LazyVim.pick("grep_word"), desc = "Visual selection or word (Root Dir)", mode = { "n", "x" } },
{
"<leader>sW",
LazyVim.pick("grep_word", { root = false }),
desc = "Visual selection or word (cwd)",
mode = { "n", "x" },
},
-- search
{
'<leader>s"',
function()
Snacks.picker.registers()
end,
desc = "Registers",
},
{
"<leader>s/",
function()
Snacks.picker.search_history()
end,
desc = "Search History",
},
{
"<leader>sa",
function()
Snacks.picker.autocmds()
end,
desc = "Autocmds",
},
{
"<leader>sc",
function()
Snacks.picker.command_history()
end,
desc = "Command History",
},
{
"<leader>sC",
function()
Snacks.picker.commands()
end,
desc = "Commands",
},
{
"<leader>sd",
function()
Snacks.picker.diagnostics()
end,
desc = "Diagnostics",
},
{
"<leader>sD",
function()
Snacks.picker.diagnostics_buffer()
end,
desc = "Buffer Diagnostics",
},
{
"<leader>sh",
function()
Snacks.picker.help()
end,
desc = "Help Pages",
},
{
"<leader>sH",
function()
Snacks.picker.highlights()
end,
desc = "Highlights",
},
{
"<leader>si",
function()
Snacks.picker.icons()
end,
desc = "Icons",
},
{
"<leader>sj",
function()
Snacks.picker.jumps()
end,
desc = "Jumps",
},
{
"<leader>sk",
function()
Snacks.picker.keymaps()
end,
desc = "Keymaps",
},
{
"<leader>sl",
function()
Snacks.picker.loclist()
end,
desc = "Location List",
},
{
"<leader>sM",
function()
Snacks.picker.man()
end,
desc = "Man Pages",
},
{
"<leader>sm",
function()
Snacks.picker.marks()
end,
desc = "Marks",
},
{
"<leader>sR",
function()
Snacks.picker.resume()
end,
desc = "Resume",
},
{
"<leader>sq",
function()
Snacks.picker.qflist()
end,
desc = "Quickfix List",
},
{
"<leader>su",
function()
Snacks.picker.undo()
end,
desc = "Undotree",
},
-- ui
{
"<leader>uC",
function()
Snacks.picker.colorschemes()
end,
desc = "Colorschemes",
},
},
}

View File

@@ -0,0 +1,32 @@
-- https://www.lazyvim.org/extras/coding/mini-surround#minisurround
return {
"echasnovski/mini.surround",
keys = function(_, keys)
-- Populate the keys based on the user's options
local opts = LazyVim.opts("mini.surround")
local mappings = {
{ opts.mappings.add, desc = "Add Surrounding", mode = { "n", "v" } },
{ opts.mappings.delete, desc = "Delete Surrounding" },
{ opts.mappings.find, desc = "Find Right Surrounding" },
{ opts.mappings.find_left, desc = "Find Left Surrounding" },
{ opts.mappings.highlight, desc = "Highlight Surrounding" },
{ opts.mappings.replace, desc = "Replace Surrounding" },
{ opts.mappings.update_n_lines, desc = "Update `MiniSurround.config.n_lines`" },
}
mappings = vim.tbl_filter(function(m)
return m[1] and #m[1] > 0
end, mappings)
return vim.list_extend(mappings, keys)
end,
opts = {
mappings = {
add = "gsa", -- Add surrounding in Normal and Visual modes
delete = "gsd", -- Delete surrounding
find = "gsf", -- Find surrounding (to the right)
find_left = "gsF", -- Find surrounding (to the left)
highlight = "gsh", -- Highlight surrounding
replace = "gsr", -- Replace surrounding
update_n_lines = "gsn", -- Update `n_lines`
},
},
}

View File

@@ -1,22 +0,0 @@
return {
"nvim-treesitter/nvim-treesitter",
opts = {
ensure_installed = {
"bash",
"html",
"css",
"javascript",
"json",
"lua",
"markdown",
"markdown_inline",
"python",
"query",
"regex",
"tsx",
"typescript",
"vim",
"yaml",
},
},
}

View File

@@ -6,12 +6,9 @@
vimAlias = true;
defaultEditor = true;
withNodeJs = true;
withRuby = false; # New default as of 26.05, needs explicit change since original config is old.
withPython3 = false; # New default as of 26.05, needs explicit change since original config is old.
extraPackages = with pkgs; [
gcc # For treesitter complation
tree-sitter # For treesitter binaries
ripgrep # Search support
wayclip # Clipboard support
fd # finder for telescope
@@ -21,7 +18,6 @@
sqlite # For Snacks
imagemagick # For image conversion/display
vscode-langservers-extracted # For language servers (it wants this version of eslint for some reason)
copilot-language-server # Enable the copilot language server
];
};

View File

@@ -4,10 +4,6 @@
obsidian
];
home.shellAliases = {
"notes" = "(cd ~/Documents/Notes && nvim)";
};
services.syncthing = {
enable = true;
tray = {
@@ -25,7 +21,6 @@
"altair"
"mcp"
"vega"
"proxima"
];
};
};
@@ -57,14 +52,6 @@
];
compression = "always";
};
proxima = {
id = "NWZL6LY-ULJQMZE-EWY3MQU-XPDAFQB-LTIBZV7-GPKIABJ-WBJE36F-SK6LVAY";
name = "Proxima";
addresses = [
"relay://syncthing.blazestar.net:22067"
];
compression = "always";
};
};
options = {
localAnnounceEnabled = false;

View File

@@ -11,7 +11,7 @@ dir="${HOME}/.config/rofi/powermenu"
# CMDs
uptime="$(uptime | awk -F ' ' '{print $2}' | sed -e 's/,//g')"
host=$(hostname)
host=`hostname`
# Options
shutdown='󰐥'
@@ -66,25 +66,19 @@ run_cmd() {
amixer set Master mute
systemctl suspend
elif [[ $1 == '--logout' ]]; then
hyprctl dispatch 'hl.dsp.exec_cmd("uwsm stop")'
# DESKTOP_SESSION appears to be empty right now. I'm not going
# to bother setting it until I'm using more than one manager.
#
# if [[ "$DESKTOP_SESSION" == 'openbox' ]]; then
# openbox --exit
# elif [[ "$DESKTOP_SESSION" == 'bspwm' ]]; then
# bspc quit
# elif [[ "$DESKTOP_SESSION" == 'i3' ]]; then
# i3-msg exit
# elif [[ "$DESKTOP_SESSION" == 'plasma' ]]; then
# qdbus org.kde.ksmserver /KSMServer logout 0 0 0
# elif [[ "$DESKTOP_SESSION" == 'plasma' ]]; then
# qdbus org.kde.ksmserver /KSMServer logout 0 0 0
# elif [[ "$DESKTOP_SESSION" == 'hyprland-uwsm' ]]; then
# hyprctl dispatch 'exec, uwsm stop'
# elif [[ "$DESKTOP_SESSION" == 'hyprland' ]]; then
# hyprctl dispatch exit
# fi
if [[ "$DESKTOP_SESSION" == 'openbox' ]]; then
openbox --exit
elif [[ "$DESKTOP_SESSION" == 'bspwm' ]]; then
bspc quit
elif [[ "$DESKTOP_SESSION" == 'i3' ]]; then
i3-msg exit
elif [[ "$DESKTOP_SESSION" == 'plasma' ]]; then
qdbus org.kde.ksmserver /KSMServer logout 0 0 0
elif [[ "$DESKTOP_SESSION" == 'plasma' ]]; then
qdbus org.kde.ksmserver /KSMServer logout 0 0 0
elif command -v hyprctl &>/dev/null; then
hyprctl dispatch exit
fi
fi
else
exit 0
@@ -101,16 +95,13 @@ $reboot)
run_cmd --reboot
;;
$lock)
loginctl lock-session
# if [[ -x '/usr/bin/betterlockscreen' ]]; then
# betterlockscreen -l
# elif [[ -x '/usr/bin/i3lock' ]]; then
# i3lock
# elif command -v hyprlock &>/dev/null; then
# hyprlock
# elif command -v swaylock &>/dev/null; then
# swaylock
# fi
if [[ -x '/usr/bin/betterlockscreen' ]]; then
betterlockscreen -l
elif [[ -x '/usr/bin/i3lock' ]]; then
i3lock
elif command -v hyprlock &>/dev/null; then
hyprlock
fi
;;
$suspend)
run_cmd --suspend

View File

@@ -9,10 +9,9 @@
recursive = true;
};
# TODO: Merge into lua config
# wayland.windowManager.hyprland.settings.windowrule = [
# # Forces the Rofi window to hold focus so that it gets input even if
# # something else opens or the mouse is outside the window.
# "stay_focused on, match:class Rofi"
# ];
wayland.windowManager.hyprland.settings.windowrulev2 = [
# Forces the Rofi window to hold focus so that it gets input even if
# something else opens or the mouse is outside the window.
"stayfocused, class:Rofi"
];
}

View File

@@ -11,29 +11,21 @@
# a system or working with other shell commands. Packages more specific to
# development can be in development configs.
home.packages = with pkgs; [
# Package management
comma # Runs commands without having to install them
# System
htop
btop
psmisc # fuser, killall, pstree
lsof
neofetch
killall
# Session management
tmux
# Archives
# Files
zip
xz
unzip
p7zip
unrar-wrapper
# File manipulation
file
tree
yazi # File manager
ueberzugpp # for image previews
w3m # terminal browser for image previews
dysk # better disk info
ripgrep # better grep
@@ -46,7 +38,6 @@
# Networking
dnsutils
inetutils
net-tools
socat
xh
@@ -55,7 +46,7 @@
# Other
jq
mprocs # Manage multiple long-running processes
];
home.shellAliases = {
@@ -69,26 +60,9 @@
"http" = "echo 'Do you mean: xh'";
"du" = "echo 'Do you mean: dust or dua?'";
"ranger" = "echo 'Do you mean: yazi'";
"stay-awake" = ''
tmux new-session -d -A -s keep-awake 'systemd-inhibit --who="Drew" --why="Manual keep awake" --what=sleep sleep infinity'
'';
"allow-sleep" = "tmux kill-session -t keep-awake";
};
programs = {
zoxide = {
# TODO: Learn all the capabilities of zoxide and use them.
enable = true;
enableZshIntegration = true;
};
starship = {
enable = true;
settings = {
add_newline = true;
};
};
zsh = {
programs.zsh = {
enable = true;
envExtra = ''
PATH=$PATH:$HOME/.local/bin
@@ -139,5 +113,17 @@
}
];
};
programs.zoxide = {
# TODO: Learn all the capabilities of zoxide and use them.
enable = true;
enableZshIntegration = true;
};
programs.starship = {
enable = true;
settings = {
add_newline = true;
};
};
}

View File

@@ -3,13 +3,13 @@
programs.ssh = {
enable = true;
matchBlocks = {
"mcp.haven" = {
user = "drew";
# Automatically add keys to agent when used
# Other options are "no" and "confirm"
addKeysToAgent = "yes";
matchBlocks = {
"mcp.haven" = {
user = "drew";
};
};
};

View File

@@ -1,12 +1,21 @@
{
config,
lib,
pkgs,
...
}:
{
imports = [
./swaync.nix
./swaylock.nix
];
home.packages = with pkgs; [
hyprlock # lock screen
# swayidle # lock on idle
# swayosd # volume pop-up
# swaynotificationcenter # notifications
# hyprpolkitagent # Privilege managent
# hyprshot # Screenshot utility
];
services.swayosd = {
@@ -14,6 +23,16 @@
topMargin = 0.7;
};
services.swayidle = {
enable = true;
events = [
{
event = "before-sleep";
command = "hyprlock";
}
];
};
# Enable sway config generation for home.pointerCursor
home.pointerCursor.sway.enable = true;

View File

@@ -1,37 +0,0 @@
{ pkgs, ... }:
{
programs.swaylock = {
enable = true;
package = pkgs.swaylock-effects;
settings = {
clock = true;
indicator = true;
effect-pixelate = 20;
};
};
home.file.".config/swaylock/swaylock.sh" = {
executable = true;
# A script that will take the wallpaper folder for each monitor (see
# wallpaper.nix) and use a random image as the background on each monitor.
text = ''
#!/usr/bin/env sh
wallpaper_dir="$HOME/Pictures/Wallpaper"
cmd="swaylock"
for monitor in $(hyprctl monitors | awk '/^Monitor/ { print $2 }'); do
dir="$wallpaper_dir/$monitor"
if [ -d "$dir" ]; then
image=$(find "$dir" -type f | shuf -n 1)
if [ -n "$image" ]; then
cmd="$cmd --image \"$monitor:$image\""
fi
fi
done
# Eval to handle the quoted image paths correctly
eval $cmd
'';
};
}

View File

@@ -13,7 +13,6 @@
nerd-fonts.jetbrains-mono
libsixel # For working with images in terminals
ueberzugpp # for image previews
];
# Allow Home Manager to set fonts.
@@ -60,7 +59,7 @@
lines = 5000;
};
colors-dark = {
colors = {
alpha = 0.9;
# Tokionight Night theme
# From https://codeberg.org/dnkl/foot/src/branch/master/themes/tokyonight-night

View File

@@ -1,7 +1,10 @@
{ config }:
monitors:
{ ... }:
{
# NOTE: Wpaperd displays wallpaper upside-down on 90´ ccw rotated
# monitors as of 26.05
wayland.windowManager.hyprland.settings.exec-once = [
"wpaperd"
];
services.wpaperd = {
enable = true;
settings =
@@ -9,11 +12,9 @@
map (mon: {
name = mon;
value = {
# Each monitor gets a folder under wallpaper that
# containes the current images.
path = "~/Pictures/Wallpaper/${mon}";
};
}) (builtins.attrNames config.hardware.monitors)
}) monitors
)
// {
default = {

View File

@@ -1,100 +0,0 @@
{ lib, config, ... }:
{
options =
with lib;
with types;
{
hardware.monitors = mkOption {
type = lazyAttrsOf (submodule {
options = {
mode = mkOption {
type = str;
default = "auto";
};
position = mkOption {
type = str;
default = "auto";
};
rotation = mkOption {
type = enum [
"none"
"90"
"180"
"270"
];
default = "none";
};
flipped = mkOption {
type = bool;
default = false;
};
};
});
default = { };
description = "Monitor configuration";
};
};
# config = {
# xdg.configFile."hypr/monitors.lua" = lib.mkIf config.wayland.windowManager.hyprland.enable {
# text =
# let
# workspaceSettings = (
# lib.imap0 (
# idx:
# { name, value }:
# let
# offset = idx * 10;
# in
# lib.concatMapStrings (
# i:
# let
# ws = offset + i;
# in
# ''
# hl.workspace_rule({ workspace = ${toString ws}, monitor = "${name}", default_name = "${toString i}" })
# ''
# ) (lib.range 1 10)
# ) (lib.attrsToList config.hardware.monitors)
# );
# transformMap = {
# "none" = {
# normal = 0;
# flipped = 4;
# };
# "90" = {
# normal = 1;
# flipped = 5;
# };
# "180" = {
# normal = 2;
# flipped = 6;
# };
# "270" = {
# normal = 3;
# flipped = 7;
# };
# };
#
# monitorTransform =
# m:
# let
# t = transformMap.${m.rotation};
# in
# if m.flipped then t.flipped else t.normal;
#
# monitorString = name: m: ''
# hl.monitor({
# output = "${name}",
# mode = "${m.mode}",
# position = "${m.position}",
# transform = ${toString (monitorTransform m)},
# })
# '';
#
# monitorSettings = lib.mapAttrsToList monitorString config.hardware.monitors;
# in
# lib.concatStringsSep "\n" (workspaceSettings ++ monitorSettings);
# };
# };
}

View File

@@ -11,54 +11,32 @@ focalboard:
offen:
secret: ENC[AES256_GCM,data:sH2siPc/QH1O2M7ZlJwqhqlHRIeLIG9r,iv:eD29ALx2ji0rm1t9j6RulTZT3f6VLK7dxpPOze3qDKA=,tag:zqJTgT2UeA/ecBS4VremUw==,type:str]
smtp-token: ENC[AES256_GCM,data:ZTfe65g3JykPvG2l0AN8UQ==,iv:GTruGo/vcP+imfJyqB3NX9ic8dz5jvTEh6SF+OeqMDM=,tag:kgwd59pG/WUt8OAaVzi39Q==,type:str]
traefik:
oauth2-client-secret: ENC[AES256_GCM,data:p7/6OsN2ytBj8mQiK0YL7J6NYLtMHOXIIs/6+bIDpsU=,iv:k6jLZifJEFLYKSFMkyn/kA7iBE+EFB8O/3/3fyTh1SY=,tag:6s49O2+tdlZoXyAGEamuMQ==,type:str]
oauth2-plugin-secret: ENC[AES256_GCM,data:sArqwKHAdW35o5kD7DGfXSYCXFUXqvKQdoVnXutsNLw=,iv:qWf597QS3BqkVQkeAb99HbpDB0kUhdD+qKdpUPZEB0o=,tag:vXnb93npaklItWkMZ+/M9Q==,type:str]
protonvpn:
private_key: ENC[AES256_GCM,data:41pfbR1klj1F24v3HlCCA4ofW2sCEnyE5TH8iX4Ug8D+kmwstTaj5RG2Zz8=,iv:P6XyQnDVoOmdkP8ilBR9DyfqPZA6GsQ6VUwY/tSGhx4=,tag:Bzgdv29lbk/gYlADPZMGVA==,type:str]
deploy-key:
mcp: ENC[AES256_GCM,data:eQcX8xdz5qZ6nU8ISOvZo+ZtP4Z/ePd+/ZZReX1BKvTUqGQPPFxConbLMwFzvzpD6xAUbA1MLkcR/bT98QbNx6LJYlhbofuDUg2DI79RB0fcrAcj/wUV0YPhmgofUdYYDaimH5A2PSvtmKfB3CtKKuA5HNeLymoXeLEpFzbckkGhzPee/CHiUmxayogp6za6btsDJsiT8hdHbrzyD2S6fhMJrzX+PlRzT32M/6eaFuFWE8EUO1gbkRlNfKPXw/EM2GXWJfR4qXfN2YKIKigqrtlAAoxnrUbp5EBrn/hGHS2ZYZXeRUFr3avFjcI0bLX423PWRHAylfQCPxgYVEtbcRv11CAFmq4rfFl0ZdvnAKbTLNmWcrQNijBATZPaAdQzgKPDHs8pwPUFR9Tcg8pZNbzw0mK9kPolniAOL2PBKUHv6LP/uEkB1E6Pxc7yms0kGpeJyo7hrFVOiVAckCey+SI9dpbJMSB3md070I1xk6Ik7PywrWh2QDeUtOU1U28UkYgnJ/9MJelWsNlUX9SR,iv:oCNeanaV/7UZ3dhmq4ZmJUZ5hb61AnHpHCfskM2Jsm8=,tag:F2uJKN5beM/rfiBMSyUP7w==,type:str]
matrix:
syncv3:
db-password: ENC[AES256_GCM,data:N/IO0k/2BZpmaDTbKZmSgZNzmdk=,iv:p0jGjJ9mTCh5FPM/Oe1vxusYvlyg14UeggE5ynpDVL8=,tag:tZbddwxJf6wSH6L1QRUQVg==,type:str]
secret: ENC[AES256_GCM,data:KZjYxjUxGgkY1I5jGF7XMEhkHK+khDaQzxugoKxpLsROmVs722tFfbUAxhp71llam55gy9+eUWGxIPlmvOySlw==,iv:OoThGcT08Z11kpnAMQ7w59wj5JheNFGEk1jfFENsmy0=,tag:8EeKT7dh2/a52Amf6LsL1w==,type:str]
blazestar-registration-token: ENC[AES256_GCM,data:TB3bR+E4H4c2l9pRcEOAZr35+vBVaJUcuCs9K0Pjd0aW+M35x5LgZ8+F99Y=,iv:e28sie6LSI5UX41BPb+yN+3n+Yw9Ssfsqe4zppwbPkU=,tag:cQPgZcRFbYSiZnmPVtZxHg==,type:str]
tandoor:
secret_key: ENC[AES256_GCM,data:nl7S2fS1wENrT5k2iZfLEAGc99lCUktgwR5L5KklF69BNVKQkW1rUgb3aIv50VpXZa+3OxV/vdPmG9NhKMy96I5+Dno=,iv:FFyGQBARz0B5zrONZELzUMsOIn8TWrDNTKGsAHPlS7w=,tag:/c4MnDfLXQpBZDqVxZ0DTg==,type:str]
immich:
database: ENC[AES256_GCM,data:1fjOQsLZcq/T+r+AkzomWwCQWw==,iv:c4pn2rC+3xkxLJ7uAdhnTE6zVTRQkfuKK3tjUyDhfAw=,tag:kvk7DOv6X/+RDxfPxVak7w==,type:str]
searxng:
secret: ENC[AES256_GCM,data:Dg/6TAKY7fyhw8LJ6E4ZYN85zs/ItcetCYHETFbOMvHkGsflE3ngQNyIPVY=,iv:z3y2vXjJWCGhXYjfj963Nhiw6K0i/M8bVvwO/L3zgzk=,tag:yO9NtYMLjSazHQmToSR3fA==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- enc: |
- recipient: age1yvdzvuvu5wqztcx6ll2xk6x547uuyqy735tjjdd7zftkz53jsf9qf5ahue
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVZnYrVnR1MlBjdzFpQnVi
RXFUcEN2N0Zoc2ZUUnprdXc0aElFaXU5c1VBCkxxcGd4QUhOaTl4K0paOTZ3d2Ez
Vks4blZLUVZPaklhQlE3WWhVelRFUEkKLS0tIGs2dU44NFdOVm41QXVJbHNkQTVo
b2NVRnVFNmF6a2hjRE9OOUxVMGpjSWcKA6Do+oddwMjc2XNJUCeGXY985gJZ4f2v
Dyo0/Oyuwz1jFMrjLmnmnmtyn079mT9wsm28KlaEEvzDIxOBnLjx6A==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiL3FoUWN1NWlDQTAvRkFM
TkdCMWVXYThIYVZFSlVEVmxUb1JuQmpESWtrClFlTVlHdGhndFJjUWxUMVFybGtn
bEpJNkFSVnpueXlvR0NkbU90RkZoTzAKLS0tIHdTTnRteWRNbW9MVmdKSVJLeWxB
Zkpxc1lLNVFSUFA2NTJUS0UrOXJCWXMKOuZmbFBdjPIcIUUWHHfGl8TD+HIRa9Hj
Eykvc7Iv5zl61kuJRorE7DWWS1I2C586c+MZtNo6GPQxUasxWRRgJQ==
-----END AGE ENCRYPTED FILE-----
recipient: age1dstsz5g4qthwt2sssu5wp787ku7rnfqm02mqaefsaueswhvqzpmsyft4p4
- enc: |
- recipient: age1jrk4h7x4qzhr6z5m4d099mlfyjc4n5n9s52r4gfsdz0slnqlqa9sss735v
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKeGhvQWp6WWx6N1JDdm1J
UWlWZTV5ellMd2ZIRWpkRnJDT2s4UVZJMEM4Cll2MnVNTnFWYit2bHpCVzcwbysy
NkxtMUZFcDJnb3Z1TmlTWDJLNVJ4cnMKLS0tIHdqVXB1U2xVdEtlY3BsemFIcXlC
OHlxaytJTWlNSFRCd2dDaFVsUU1CbUkKp7J+mDnGFS9upV6QWi40ptG85+UZ5y1G
9ic/sQEMHQe9VeAutVz1GPYWbF3Vt5iawXmtVxN+zx/bLAYGglv5yg==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZck5tK3p6blhxZWp5YkNF
b3lnMWI1dHBnQzlDOG05c0YyZlZDTzA5V2pvCis3N1BhNXlmamRFci9wWTBuRWFv
cC9kdWV3RnpFWTFVRVEwaTVWQVdnZE0KLS0tIEhOTzExRzRLaGdKOTRwRmNXcW5P
by9aNFY4dXNxaWxnTXFTQS9reHhuQWMKh5rZ93nFtBV9EpFVRp+E+GXZ6xzVy2Jw
vFh4deGcAb60q4odSaeWfk1Dr7L9Ua69oK9omjbCNUt+P7Kwlfca7Q==
-----END AGE ENCRYPTED FILE-----
recipient: age1yvdzvuvu5wqztcx6ll2xk6x547uuyqy735tjjdd7zftkz53jsf9qf5ahue
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkM0RRaFhGKytkSjNMU3cy
d1dlcms4TVMzU2xsSHBqcDJWQTdCU1dZcERJCktHRVZiWkZrbzk4QXRNSmpyY25X
ZUdOS2pSOVpITExpVVFyc1A3QTBDTGsKLS0tIDM4NFd1eWtZSnR4d0tLUnRFYmJW
QlE3cFYxUXNVam1aMlJEVTRGV2xsN1kKXMeSXiEdXyb6c+dcxX3G/Q+s78lfi90g
dvKJPDYvGb/8JXSkZNkLlWvHEVnsAJ1mJFb1idenYzeQvJlsX07rYA==
-----END AGE ENCRYPTED FILE-----
recipient: age1jrk4h7x4qzhr6z5m4d099mlfyjc4n5n9s52r4gfsdz0slnqlqa9sss735v
lastmodified: "2026-07-21T02:59:55Z"
mac: ENC[AES256_GCM,data:0v26ceptPmbVdee70AOMdZHOarPDRuxKi+QP3Ci+wyJQJdQVriKKUnN3Zv/MKjZ3Sg2M/C5XtsdhGwXJpPsfwdDhWIRip6UYWXIKkVyZcxOs6E2zlK3xCAk/eTKhAsmL7Y6yFV+hpYwlT/hFnzOHXt1xbxGVJiKnO98OH0+6hnk=,iv:f94wd227bM0SniUuY+CFg2deEfITzUGTGwDA/WQJhms=,tag:M0r4Vg480wWWYPH3PBB7qg==,type:str]
lastmodified: "2025-04-28T23:33:42Z"
mac: ENC[AES256_GCM,data:cZkRcGV5/CPPVUdTDekwC8UjO6K348sBsS7NvR8wnoXS0AmSZsqN594nkvoc0VccM55Hwnm4jZxY56OV+UFMya1IRIkTo6LJRb88/CgZ8bjz30ACe33FKgJfCugimUDKsekbgNX1UFg1DVbqYK9/N4fcEBSxV3Xmzy5QGnQ/8KU=,iv:EprUHNtU5w7569ADMOxw+izDAL22A5OrB12T9iyHxKU=,tag:kRvyUEZwd/RttKdFOY2bJQ==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.13.1
version: 3.9.4

View File

@@ -1,26 +0,0 @@
webhook:
deploy-key: ENC[AES256_GCM,data:Gd+cCNmWI/SCab8dnXnWxXFTT7EBAi9LTJVLVF/+ouCn7dI5LE0Ndwq8KVadNEbDPE3LFXAgylpzRIJawWd5saAXPAw5HtrcBgGZDN9ehebAPWNzrGpy4pVgwak6ByQ04Y13PaBK9vG8OR3wG8yQqcZLrdk5TWdYPZ8Q2mdcl3hgItEJMBuOeYMBFiMyBdvbYPQixi9dF21owBjHQr5oZjWzDAS3V+X46Qgq5LYLr74BSYpBQzs/yEOi9CV4H39hdYmFyyPzgSiJljnH2E56iahScQfuQuwqHHdtHGmLYz9CxnHnUGvdzqC8PCu8T8zUuq8qwtbF6+UVISYwhjWmNLq7zUTabLhFjafqQcqdd9PcMMoNhSNQsTVfM0vYrpdfiot3u5AD7HGrORY1WWE71i8CQ7ETO28gsfJB8fWX7s+/FNFNau6fcMw1TCkt+qhnon5zS/OwmFG4596vCTyQ0CVCnHeQDiaqdexJVrVS7w1gROrI266fBPLH0yaSri/ps72A,iv:itchCa14rgALz5Gurm9dh+nWvz0pGi0EFtUlF+Jh3pU=,tag:ucrpJbZmPf9z5x5bHUXnRQ==,type:str]
sops:
age:
- recipient: age1dstsz5g4qthwt2sssu5wp787ku7rnfqm02mqaefsaueswhvqzpmsyft4p4
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2ellCLzVCdmFtN0grU3FO
Y3BEOW53ejVZZjlYWFNmOG4yUHB5MUV2UUhzCnZ6UTQ2VXVXZFp4MzgzRE1xdGJk
Z0VrTytJeXdQYzFCbkFHUWFVSXA5ZjAKLS0tIEo4UjJvMGlxcjdEcFBhYXNlMEli
MysyWkdHZ09lV29mMnJoUnRTNVU4bXMKSkL3VBnBTqrLpG3fuiDTCPNZTFWZbJEW
VLwB6g/K3b573MgfXGCCPs9nwdDurqpz0zxB4pEBl0N6fU2CwqAxqQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1jrk4h7x4qzhr6z5m4d099mlfyjc4n5n9s52r4gfsdz0slnqlqa9sss735v
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwWDJZQ2ZESkltQjFHRHpI
cXU0WFo1aFJrT2VTNlFTY2s5T0gxSWRoa0F3Ck9tVnFtZ3c4T0tWQTNhYXRrU3Ri
dFRrcWp3VnRPRDc5Y3JZZ1U1SDNSbXMKLS0tIFFqMUdNU3VFODhMdmJZN0ZuZGgz
Z2JFS25iM3YvaVh0L3JkRk5DODZwZzgKg8C2kboc7EojVva+8KWW+pVhSbovwHp9
noZ68vZ8NxsBP3CNFBBN1xKT7gQduShUcmSQUQizZ6fxtxO+GLy4Pg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-05-18T22:16:11Z"
mac: ENC[AES256_GCM,data:+/cn2b06HsQ23UL4YyTa51eepBelypcfBCVrG9a6HWhuX0KMpi9bn55IuZ6VbyN2uGc5+yCJcQBtAPm4+Mdi9UdGAWoePYIvQwu6ct12xIOLBF4P7eDRwjlWykANF9/jWFoPfd/hu+LiM2Ow09RTZTnExgAjYiCrq7YJRTrjIfU=,iv:ODluo/RXmSu7PzzXS/ZnFNMzUOjcrCpkzHoAviCNDUU=,tag:2LW9hIP6dq23/coQbTLOww==,type:str]
unencrypted_suffix: _unencrypted
version: 3.12.1

View File

@@ -1,4 +0,0 @@
{ ... }:
{
users.users.drew.extraGroups = [ "adbusers" ];
}

View File

@@ -1,12 +0,0 @@
{ pkgs, ... }:
{
programs.appimage = {
enable = true;
binfmt = true;
package = pkgs.appimage-run.override {
extraPkgs = pkgs: [
pkgs.icu
];
};
};
}

View File

@@ -1,10 +0,0 @@
{ pkgs, ... }:
{
# Enable bluetooth.
hardware.bluetooth.enable = true;
# Install the management service and packages.
environment.systemPackages = with pkgs; [
bluez
];
}

View File

@@ -3,17 +3,12 @@
virtualisation = {
docker = {
enable = true;
# rootless = {
# enable = true;
# setSocketVariable = true;
# };
rootless = {
enable = true;
setSocketVariable = true;
};
};
# Do not use Podman in Wayland/Hyperland. It will crash the session. Very annoying.
# podman.enable = true;
};
hardware.nvidia-container-toolkit.enable = true;
home-manager.users.drew =
{ ... }:
@@ -23,7 +18,4 @@
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
'';
};
# add me to the docker group so I can access it.
users.users.drew.extraGroups = [ "docker" ];
}

View File

@@ -1,12 +0,0 @@
{ pkgs, ... }:
{
services.flatpak.enable = true;
systemd.services.flatpak-repo = {
wantedBy = [ "multi-user.target" ];
path = [ pkgs.flatpak ];
script = ''
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
'';
};
}

View File

@@ -1,9 +1,8 @@
_: {
{ ... }: {
# Automatic
nix.gc = {
automatic = true;
dates = "weekly";
persistent = true;
options = "--delete-older-than 30d";
};
}

View File

@@ -1,50 +0,0 @@
{ config, lib, ... }:
{
options =
with lib;
with types;
{
graphics = {
enable = mkEnableOption "graphics support";
driverChannel = mkOption {
type = str;
# Default to production because I often want new features, but I don't want bleeding edge.
default = "production";
description = "Driver channel to use (in order of oldest to newest): stable, beta, latest";
};
};
};
config = lib.mkIf config.graphics.enable {
# Graphics settings
hardware.graphics = {
enable = true;
enable32Bit = true;
};
services.xserver.videoDrivers = [ "nvidia" ];
hardware.nvidia = {
package = config.boot.kernelPackages.nvidiaPackages."${config.graphics.driverChannel}";
modesetting.enable = true;
# Nvidia power management. Experimental, and can cause sleep/suspend to fail.
# Enable this if you have graphical corruption issues or application crashes after waking
# up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead
# of just the bare essentials.
powerManagement.enable = true;
# Fine-grained power management for PRIME. Turns off GPU when not in use.
# Experimental and only works on modern Nvidia GPUs (Turing or newer).
# Requires offload to be enabled.
# powerManagement.finegrained = false;
# Use the open-source driver?
open = false;
# Enable the nvidia-settings menu?
nvidiaSettings = true;
};
};
}

View File

@@ -1,8 +1,11 @@
{ ... }:
{
{ ... }: {
# Enable the X11 windowing system and a display manager
services.xserver = {
enable = true;
displayManager.gdm = {
enable = true;
wayland = true;
};
# displayManager.sddm = {
# enable = true;
# wayland.enable = true;
@@ -14,19 +17,7 @@
variant = "";
};
};
# services.displayManager.gdm = {
# enable = true;
# };
services.displayManager.ly = {
enable = true;
# See https://github.com/S41G0N/ly/blob/master/res/config.ini
settings = {
animation = "matrix"; # matrix|doom|none
bigclock = "en";
clear_password = true; # erase the password on failure
clock = "%c"; # Show the clock
};
};
# services.displayManager.ly.enable = true;
# Enable Hyprland so it shows up in the menu
programs.hyprland = {

View File

@@ -1,21 +0,0 @@
{ pkgs, ... }:
{
services.printing = {
enable = true;
drivers = [ pkgs.brlaser ];
};
hardware.printers = {
ensurePrinters = [
{
name = "Brother_HL-L2370DW_series";
location = "Home";
deviceUri = "dnssd://Brother%20HL-L2370DW%20series._ipp._tcp.local/?uuid=e3248000-80ce-11db-8000-3c2af4f28c38";
model = "drv:///brlaser.drv/brl2370d.ppd";
ppdOptions = {
PageSize = "Letter";
};
}
];
ensureDefaultPrinter = "Brother_HL-L2370DW_series";
};
}

View File

@@ -1,4 +0,0 @@
_: {
virtualisation.virtualbox.host.enable = true;
users.extraGroups.vboxusers.members = [ "drew" ];
}

View File

@@ -1,127 +0,0 @@
{ config, lib, ... }:
{
options =
with lib;
with types;
{
virtualisation.web-containers = {
enable = mkEnableOption "web containers";
containers = mkOption {
type = lazyAttrsOf (submodule {
options =
let
strOpt = mkOption { type = str; };
intOpt = mkOption { type = int; };
boolOpt = mkOption {
type = bool;
default = false;
};
strList = mkOption {
type = listOf str;
default = [ ];
};
attrOpt = mkOption {
type = attrsOf str;
default = { };
};
in
{
image = strOpt;
hostname = strOpt;
port = intOpt;
homepageOpts = attrOpt;
dependsOn = strList;
domain = strOpt;
volumes = strList;
environment = attrOpt;
environmentFiles = strList;
public = boolOpt;
user = mkOption {
type = nullOr str;
default = null;
};
extraOptions = strList;
oauthProxy = boolOpt;
extraLabels = attrOpt;
};
});
default = { };
description = "";
};
};
};
config = {
virtualisation.oci-containers.containers = lib.mkIf config.virtualisation.web-containers.enable (
let
hostRule = host: domain: "Host(`${host}.${domain}`)";
localNet = "192.168.0.0/16";
dockerNet = "10.88.0.0/16";
localNetRule = "(ClientIP(`${localNet}`) || ClientIP(`${dockerNet}`))";
localHostRule = host: domain: "${localNetRule} && ${hostRule host domain}";
mkContainer =
key:
{
image,
hostname,
port,
homepageOpts,
dependsOn,
domain,
volumes,
environment,
environmentFiles,
public,
user,
extraOptions,
oauthProxy,
extraLabels,
}:
let
fqn = "${hostname}.${domain}";
serviceName = builtins.replaceStrings [ "." ] [ "-" ] fqn;
routerRule = if public then hostRule hostname domain else localHostRule hostname domain;
homepageLabels =
if homepageOpts == { } then
{ }
else
{
"homepage.group" = "${homepageOpts.group}";
"homepage.name" = "${homepageOpts.name}";
"homepage.icon" = "${homepageOpts.icon}";
"homepage.href" = "https://${fqn}";
"homepage.description" = "${homepageOpts.description}";
};
oauthLabels =
if oauthProxy then
{ "traefik.http.routers.${serviceName}.middlewares" = "oidc-auth@file"; }
else
{ };
in
{
inherit
image
dependsOn
volumes
environment
environmentFiles
user
extraOptions
;
autoStart = true;
labels = {
"traefik.enable" = "true";
"traefik.http.routers.${serviceName}.rule" = "${routerRule}";
"traefik.http.routers.${serviceName}.service" = "${serviceName}";
"traefik.http.routers.${serviceName}.entrypoints" = "web,websecure";
"traefik.http.services.${serviceName}.loadbalancer.server.port" = "${toString port}";
}
// oauthLabels
// homepageLabels
// extraLabels;
};
in
builtins.mapAttrs mkContainer config.virtualisation.web-containers.containers
);
};
}

View File

@@ -1,26 +0,0 @@
_: {
hardware = {
# Wireless xbox controllers
xpadneo = {
enable = true;
settings = {
disable_mouse = 1;
};
};
# Hardwired xbox controllers.
xone.enable = true;
};
# disable_ertm is required for xpadneo to connect properly and establish a
# data connection. enable_autosuspend is to prevent Linux from putting the
# BT radio to sleep and causing a disconnect.
boot.extraModprobeConfig = ''
options bluetooth disable_ertm=1
options btusb enable_autosuspend=0
'';
# Steam also needs HID API to be off so SDL will read directly from evdev,
# otherwise it won't get any events from xpadneo.
environment.sessionVariables.SDL_JOYSTICK_HIDAPI = "0";
}

View File

@@ -6,6 +6,7 @@
pkgs,
...
}:
{
networking.hostName = "altair"; # Define your hostname.
@@ -16,8 +17,7 @@
# Enable networking
networking.networkmanager.enable = true;
# Disable wpa-supplicant to avoid conflicts with network manager.
# Appears this is not needed as of 26.05
networking.wireless.enable = true;
networking.wireless.enable = false;
# Set your time zone.
time.timeZone = "America/Los_Angeles";
@@ -70,7 +70,7 @@
services.gnome.gnome-keyring.enable = true;
# Enable sound with pipewire.
services.pulseaudio.enable = false;
hardware.pulseaudio.enable = false;
security.rtkit.enable = true;
services.pipewire = {
enable = true;

View File

@@ -1,20 +1,14 @@
{ ... }:
{
imports = [
../../authorized-keys.nix
../../features/android-dev.nix
../../features/appimage.nix
../../features/audio.nix
../../features/bluetooth.nix
../../features/container-dev.nix
../../features/flatpak.nix
../../features/gc.nix
../../features/gui.nix
../../features/virtualbox.nix
../../features/xpadneo.nix
./configuration.nix
./hardware-configuration.nix
./qmk.nix
../../authorized-keys.nix
../../features/audio.nix
../../features/gc.nix
../../features/gui.nix
../../features/container-dev.nix
];
nixpkgs.config.allowUnfree = true;

View File

@@ -1,81 +1,52 @@
{ pkgs, ... }:
{ ... }:
let
monitors = [
"DP-1"
"DP-2"
];
in
{
imports = map (x: ../../../home-manager + x) [
"/features/astronomy.nix"
"/features/chat.nix"
imports =
map (x: ../../../home-manager + x) [
"/features/development/development.nix"
"/features/development/docker.nix"
"/features/development/haskell.nix"
"/features/development/markdown.nix"
"/features/development/typescript.nix"
"/features/development/vscode.nix"
"/features/eww"
"/features/email.nix"
"/features/gaming.nix"
"/features/image-editing.nix"
"/features/linux-desktop.nix"
"/features/mounts/mcp-archive.nix"
"/features/notes.nix"
"/features/3d-printing.nix"
"/features/hyprpaper.nix"
]
++ [
(import ../../../home-manager/features/wallpaper.nix monitors)
];
home = {
stateVersion = "24.11";
username = "drew";
homeDirectory = "/home/drew";
# This config file is needed for nix shell to allow unfree programs. I'm not
# sure why this isn't a home-manager option.
file.".config/nixpkgs/config.nix".text = ''
home.file.".config/nixpkgs/config.nix".text = ''
{ allowUnfree = true; }
'';
home.stateVersion = "24.11";
home.username = "drew";
home.homeDirectory = "/home/drew";
programs.git = {
userName = "Drew Haven";
userEmail = "drew.haven@gmail.com";
};
programs.git.settings = {
user = {
name = "Drew Haven";
email = "periodic@blazestar.net";
};
};
hardware.monitors = {
"DP-2" = {
position = "0x0";
mode = "3440x1440@164";
};
"DP-1" = {
mode = "2560x1440";
position = "-1440x-510";
rotation = "90";
};
};
programs.eww.widgets = [
"primary-statusbar"
"secondary-statusbar"
# Set up eww here because it's based on the monitor configuration
wayland.windowManager.hyprland.settings = {
exec-once = [
"sleep 2 && eww open-many primary-statusbar secondary-statusbar launcher"
];
windowrulev2 = [
# Rofi doesn't center properly when I have the two asymetric monitors
"center, class:Rofi"
];
xdg.configFile = {
"hypr/altair.lua" = {
source = ./hyprland.lua;
};
};
wayland.windowManager.hyprland.extraConfig = ''
require("altair")
'';
# TODO: Still merging these into the LUA config
# wayland.windowManager.hyprland.settings = {
# exec_once = [
# # Set up eww here because it's based on the monitor configuration
# "sleep 2 && eww open-many primary-statusbar secondary-statusbar"
# # Set DP-2 as the primary monitor, otherwise it defaults to DP-1 because it's first in the list.
# "xrandr --output DP-2 --primary"
# ];
# # windowrule = [
# # # Rofi doesn't center properly when I have the two asymetric monitors, so we need hyprland to manage it.
# # "match:class Rofi, center on"
# # ];
# };
}

View File

@@ -13,14 +13,10 @@
(modulesPath + "/installer/scan/not-detected.nix")
];
# Bootloader.
boot = {
loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
initrd = {
availableKernelModules = [
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"nvme"
@@ -28,19 +24,16 @@
"usb_storage"
"sd_mod"
];
kernelModules = [ ];
};
kernelModules = [ "kvm-intel" ];
extraModulePackages = [ ];
};
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems = {
"/" = {
fileSystems."/" = {
device = "/dev/disk/by-uuid/343c0ac5-3973-49b3-964a-6ad90c36b89c";
fsType = "ext4";
};
"/boot" = {
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/5F99-043D";
fsType = "vfat";
options = [
@@ -49,11 +42,10 @@
];
};
"/home" = {
fileSystems."/home" = {
device = "/dev/disk/by-uuid/28f4fb41-9414-4504-a767-c2e8bf5eb2c8";
fsType = "ext4";
};
};
swapDevices = [ ];
@@ -66,27 +58,18 @@
# networking.interfaces.wlo1.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
services.xserver.videoDrivers = [ "nvidia" ];
hardware = {
cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
graphics = {
# Graphics settings
hardware.graphics = {
enable = true;
enable32Bit = true;
};
nvidia = {
# Other options include:
# stable - Current stable
# production - Same as stable
# latest - Bleeding edge
# beta - latest beta
#
# See https://nixos.wiki/wiki/Nvidia
#
# Current versions can be found in https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/os-specific/linux/nvidia-x11/default.nix
#
package = config.boot.kernelPackages.nvidiaPackages.stable;
services.xserver.videoDrivers = [ "nvidia" ];
hardware.nvidia = {
package = config.boot.kernelPackages.nvidiaPackages.beta;
modesetting.enable = true;
@@ -107,7 +90,6 @@
# Enable the nvidia-settings menu?
nvidiaSettings = true;
};
};
# Add a udev rule to prevent the mouse from waking the system. Note that it
# has two entries depending on whether it's plugged in or not.

View File

@@ -1,64 +0,0 @@
-- These configs are specifically for Altair
-----------------------------------------------------------
for i = 1, 10 do
hl.workspace_rule({ workspace = i, monitor = "DP-1", default_name = tostring(i) })
hl.workspace_rule({ workspace = i + 10, monitor = "DP-2", default_name = tostring(i) })
end
hl.monitor({
output = "DP-1",
mode = "2560x1440",
position = "-1440x-510",
transform = 1,
})
hl.monitor({
output = "DP-2",
mode = "3440x1440@164",
position = "0x0",
transform = 0,
})
local fullscreen_windows = { "^steam_app_\\d+$" }
for _, class in ipairs(fullscreen_windows) do
hl.window_rule({
name = "Fullscreen " .. class,
match = {
class = class,
},
fullscreen = true,
monitor = "DP-2",
center = true,
})
end
local maximized_windows = { "Slay the Spire" }
for _, class in ipairs(maximized_windows) do
hl.window_rule({
name = "Maximize " .. class,
match = {
class = class,
},
maximize = true,
monitor = "DP-2",
center = true,
})
end
-- MIGRATION_WARNING: Window rule conflict for 'monitor 1': 'match:title ^World of Warcraft$' (line 200) vs 'match:title ^Battle.net$' (line 202)
-- MIGRATION_WARNING: Window rule conflict for 'monitor 1': 'match:title ^World of Warcraft$' (line 200) vs 'match:title ^Balatro$' (line 204)
-- MIGRATION_WARNING: Window rule conflict for 'monitor 1': 'match:title ^World of Warcraft$' (line 200) vs 'match:class steam_app_1091500' (line 206)
-- hl.window_rule({
-- match = { class = "monitor 1" },
-- match:title = "^World of Warcraft$",
-- match:title = "^Battle.net$",
-- match:title = "^Balatro$",
-- match:class = "steam_app_1091500",
-- })
-- PATTERN: MERGED with line 207
-- MIGRATION_WARNING: Window rule conflict for 'fullscreen on': 'match:title ^World of Warcraft$' (line 201) vs 'match:class steam_app_1091500' (line 207)
-- hl.window_rule({ match = { class = "fullscreen on" }, match:title = "^World of Warcraft$", match:class = "steam_app_1091500" })
-- hl.window_rule({ match = { class = "float on" }, match:title = "^Battle.net$" })
-- hl.window_rule({ match = { class = "tile on" }, match:title = "^Balatro$" })

View File

@@ -1,10 +1,3 @@
# mcp
Configuration for the MCP server.
## Sops
```bash
nix-shell -p sops --run "sops secrets/mcp.yaml"
```
Configuration for the MCP server

View File

@@ -1,4 +1,4 @@
{ pkgs, ... }:
{ config, pkgs, ... }:
{
imports = [
./vars.nix
@@ -16,6 +16,9 @@
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# Set the kernel to be compatible with ZFS
boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
networking.hostName = "mcp"; # Define your hostname.
networking.hostId = "5e292f2d"; # Define a host ID for ZFS with `head -c 8 /etc/machine-id`
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
@@ -45,6 +48,12 @@
LC_TIME = "en_US.UTF-8";
};
# Configure keymap in X11
services.xserver.xkb = {
layout = "us";
variant = "";
};
# Define a user account. Don't forget to set a password with passwd.
users.users.drew = {
isNormalUser = true;
@@ -53,7 +62,6 @@
"networkmanager"
"wheel"
"docker-registry"
"docker"
];
shell = pkgs.zsh;
# Enable linger so that systemd services run for this user are started and
@@ -61,9 +69,11 @@
linger = true;
};
# List packages installed in system profile.
# List packages installed in system profile. To search, run:
# $ nix search wget
environment.systemPackages = with pkgs; [
vim
vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
# wget
];
# Some programs need SUID wrappers, can be configured further or are
@@ -86,8 +96,7 @@
port = 5000;
openFirewall = true;
# Bind to the podman network so Traefik can route to it.
# Note that it may fail to start if this network has not been created yet,
# so this has to be manually restarted when the system boots.
# Note that it may fail to start if this network has not been created yet.
listenAddress = "10.88.0.1";
};
@@ -98,10 +107,8 @@
enabledCollectors = [ "systemd" ];
port = 9002;
# Open the firewall, but only listen on the internal address
# TODO: Add some form authentication
openFirewall = true;
# Bind to the podman network so Traefik can route to it.
# Note that it may fail to start if this network has not been created yet,
# so this has to be manually restarted when the system boots.
listenAddress = "10.88.0.1";
};
};

View File

@@ -1,100 +1,29 @@
{
config,
pkgs,
lib,
...
}:
# Started from https://nixos.wiki/wiki/Podman
{ config, pkgs, ... }:
{
# Additional configuration
imports = [
./containers/havenisms.com
./containers/blazestar.net
# Docker containers
./containers/dm-companion.nix
./containers/freshrss.nix
./containers/gitea.nix
./containers/goatcounter.nix
./containers/grafana.nix
# ./containers/jobhunt.nix
./containers/jobhunt.nix
./containers/mariadb.nix
./containers/media-system.nix
./containers/nextcloud.nix
# ./containers/offen.nix
./containers/offen.nix
./containers/pocket-id.nix
./containers/prometheus.nix
./containers/public-homepage.nix
./containers/searxng.nix
./containers/shared-postgres.nix
# ./containers/timetagger.nix
./containers/synapse.nix
./containers/traefik.nix
./containers/users.nix
# NixOS Containers
./static-site-hooks.nix
];
options.local = with lib; {
container-backend = mkOption {
type = with types; uniq str;
default = "docker";
example = "docker";
description = "Which backend to use for containers: docker or podman";
};
container-socket = mkOption {
type = with types; uniq str;
default = "/var/run/docker.sock";
example = "/var/run/docker.sock";
description = "Path to the container management deamon's socket.";
};
};
config = {
# local = {
# container-backend = "docker";
# container-socket = "/var/run/docker.sock";
# };
local = {
container-backend = "podman";
container-socket = "/var/run/podman/podman.sock";
};
# Enable common container config files in /etc/containers
virtualisation.containers.enable = true;
virtualisation = {
containers.enable = true;
oci-containers.backend = config.local.container-backend;
docker = lib.mkIf (config.local.container-backend == "docker") {
enable = true;
# Enable rootless so that I can run containers as other users for security.
rootless = {
enable = true;
# Set this to make the default DOCKER_HOST be the rootless version for normal users.
setSocketVariable = true;
daemon = {
settings = {
default-address-pools = [
{
base = "10.88.0.0/16";
size = 24;
}
];
};
};
};
daemon = {
settings = {
default-address-pools = [
{
base = "10.88.0.0/16";
size = 24;
}
];
};
};
};
podman = lib.mkIf (config.local.container-backend == "podman") {
podman = {
enable = true;
# Create a `docker` alias for podman, to use it as a drop-in replacement
@@ -110,17 +39,238 @@
# Useful other development tools
environment.systemPackages = with pkgs; [
dive # look into docker image layers
podman-tui # status of containers in the terminal
docker-compose # start group of containers for dev
#podman-compose # start group of containers for dev
];
virtualisation.oci-containers.backend = "podman";
virtualisation.oci-containers.containers =
let
inherit (import ./containers/lib.nix config)
hostRuleHavenisms
localHostRuleHavenisms
havenisms
;
havenisms;
in
{
jellyfin = {
image = "lscr.io/linuxserver/jellyfin";
autoStart = true;
extraOptions = [
"--device=/dev/dri:/dev/dri"
"-l=traefik.enable=true"
"-l=traefik.http.routers.jellyfin.rule=${hostRuleHavenisms "jellyfin"}"
"-l=traefik.http.services.jellyfin.loadbalancer.server.port=8096"
"-l=homepage.group=Apps"
"-l=homepage.name=Jellyfin"
"-l=homepage.icon=jellyfin.svg"
"-l=homepage.href=https://jellyfin.${havenisms}"
"-l=homepage.description=Media player"
"-l=homepage.widget.type=jellyfin"
"-l=homepage.widget.key={{HOMEPAGE_FILE_JELLYFIN_KEY}}"
"-l=homepage.widget.url=http://jellyfin:8096"
"-l=homepage.widget.enableBlocks=true"
];
volumes = [
"/tank/media/collection:/data"
"/tank/config/jellyfin:/config"
];
# environment = {
# TZ = vars.timeZone;
# PUID = "994";
# UMASK = "002";
# GUID = "993";
# };
};
deluge = {
image = "linuxserver/deluge:latest";
autoStart = true;
dependsOn = [
"gluetun"
];
extraOptions = [
"--network=container:gluetun"
"-l=homepage.group=Arr"
"-l=homepage.name=Deluge"
"-l=homepage.icon=deluge.svg"
"-l=homepage.href=https://deluge.${havenisms}"
"-l=homepage.description=Torrent client"
"-l=homepage.widget.type=deluge"
"-l=homepage.widget.password={{HOMEPAGE_FILE_DELUGE_PASSWORD}}"
"-l=homepage.widget.url=http://gluetun:8112"
];
volumes = [
"/tank/media:/data"
"/tank/config/deluge:/config"
];
};
qbittorrent = {
image = "linuxserver/qbittorrent:latest";
autoStart = true;
dependsOn = [
"gluetun"
];
extraOptions = [
"--network=container:gluetun"
"-l=homepage.group=Arr"
"-l=homepage.name=qBitTorrent"
"-l=homepage.icon=qbittorrent.svg"
"-l=homepage.href=https://torrents.${havenisms}"
"-l=homepage.description=Torrent client"
"-l=homepage.widget.type=qbittorrent"
"-l=homepage.widget.url=http://torrents.${havenisms}"
];
volumes = [
"/tank/media/Downloads:/downloads"
"/tank/config/qbittorrent:/config"
];
environment = {
PUID = "911";
PGID = "911";
UMASK = "002";
};
};
gluetun = {
image = "qmcgaw/gluetun:latest";
autoStart = true;
extraOptions = [
# add network admin capability.
"--cap-add=NET_ADMIN"
"--device=/dev/net/tun:/dev/net/tun"
"-l=traefik.enable=true"
"-l=traefik.http.routers.torrents.rule=${localHostRuleHavenisms "torrents"}"
"-l=traefik.http.routers.torrents.service=torrents"
"-l=traefik.http.services.torrents.loadbalancer.server.port=8080"
"-l=homepage.group=Infra"
"-l=homepage.name=GlueTun"
"-l=homepage.icon=gluetun.svg"
"-l=homepage.href=https://torrents.${havenisms}"
"-l=homepage.description=VPN killswitch"
"-l=homepage.widget.type=gluetun"
"-l=homepage.widget.url=http://gluetun:8000"
];
ports = [
"127.0.0.1:8083:8000"
];
environmentFiles = [
"/tank/config/gluetun/vpn.env"
];
environment = {
VPN_SERVICE_PROVIDER = "protonvpn";
UMASK = "002";
};
};
prowlarr = {
image = "lscr.io/linuxserver/prowlarr";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.prowlarr.rule=${localHostRuleHavenisms "prowlarr"}"
"-l=traefik.http.services.prowlarr.loadbalancer.server.port=9696"
"-l=homepage.group=Arr"
"-l=homepage.name=Prowlarr"
"-l=homepage.icon=prowlarr.svg"
"-l=homepage.href=https://prowlarr.${havenisms}"
"-l=homepage.description=Torrent indexer"
];
volumes = [
"/tank/config/prowlarr:/config"
];
environment = {
UMASK = "002";
};
};
# Currently broken and doesn't work. :(
# flaresolverr = {
# image = "ghcr.io/flaresolverr/flaresolverr:latest";
# autoStart = true;
# extraOptions = [
# "-l=homepage.group=Infra"
# "-l=homepage.name=FlareSolverr"
# "-l=homepage.icon=flaresolverr.svg"
# "-l=homepage.href=https://flaresolverr.${domain}"
# "-l=homepage.description=Cloudflare bypass"
# ];
# volumes = [
# "/tank/config/flaresolverr:/config"
# ];
# environment = {
# UMASK = "002";
# };
# };
radarr = {
image = "lscr.io/linuxserver/radarr";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.radarr.rule=${localHostRuleHavenisms "radarr"}"
"-l=traefik.http.services.radarr.loadbalancer.server.port=7878"
"-l=homepage.group=Arr"
"-l=homepage.name=Radarr"
"-l=homepage.icon=radarr.svg"
"-l=homepage.href=https://radarr.${havenisms}"
"-l=homepage.description=Movie acquisition"
"-l=homepage.widget.type=radarr"
"-l=homepage.widget.url=http://radarr:7878"
"-l=homepage.widget.key={{HOMEPAGE_FILE_RADARR_KEY}}"
];
volumes = [
"/tank/media:/data"
"/tank/config/radarr:/config"
];
environment = {
UMASK = "002";
};
};
sonarr = {
image = "lscr.io/linuxserver/sonarr";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.sonarr.rule=${localHostRuleHavenisms "sonarr"}"
"-l=traefik.http.services.sonarr.loadbalancer.server.port=8989"
"-l=homepage.group=Arr"
"-l=homepage.name=Sonarr"
"-l=homepage.icon=sonarr.svg"
"-l=homepage.href=https://sonarr.${havenisms}"
"-l=homepage.description=Show acquisition"
"-l=homepage.widget.type=sonarr"
"-l=homepage.widget.url=http://sonarr:8989"
"-l=homepage.widget.key={{HOMEPAGE_FILE_SONARR_KEY}}"
];
volumes = [
"/tank/media:/data"
"/tank/config/sonarr:/config"
];
environment = {
UMASK = "002";
};
};
readarr = {
# The Linuxserver version of this image doesn't have a latest tag. Odd.
image = "lscr.io/linuxserver/readarr:develop";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.readarr.rule=${localHostRuleHavenisms "readarr"}"
"-l=traefik.http.services.readarr.loadbalancer.server.port=8787"
"-l=homepage.group=Arr"
"-l=homepage.name=Readarr"
"-l=homepage.icon=readarr.svg"
"-l=homepage.href=https://readarr.${havenisms}"
"-l=homepage.description=E-book acquisition"
"-l=homepage.widget.type=readarr"
"-l=homepage.widget.url=http://readarr.havenisms.com:8787"
"-l=homepage.widget.key={{HOMEPAGE_FILE_READARR_KEY}}"
];
volumes = [
"/tank/media:/data"
"/tank/config/readarr:/config"
];
environment = {
UMASK = "002";
};
};
homepage = {
image = "ghcr.io/gethomepage/homepage:latest";
autoStart = true;
@@ -135,7 +285,7 @@
"/tank/secrets/jellyfin.key:/app/config/secrets/jellyfin.key"
"/tank/secrets/radarr.key:/app/config/secrets/radarr.key"
"/tank/secrets/sonarr.key:/app/config/secrets/sonarr.key"
"${config.local.container-socket}:/var/run/docker.sock:ro"
"/var/run/podman/podman.sock:/var/run/docker.sock:ro"
];
environment = {
HOMEPAGE_FILE_JELLYFIN_KEY = "/app/config/secrets/jellyfin.key";
@@ -160,10 +310,10 @@
"-l=homepage.widget.type=scrutiny"
"-l=homepage.widget.url=http://scrutiny:8080"
"--cap-add=SYS_RAWIO"
"--device=/dev/disk/by-id/wwn-0x5000cca26fca1aed:/dev/disk/by-id/wwn-0x5000cca26fca1aed"
"--device=/dev/disk/by-id/wwn-0x5000cca26fef696c:/dev/disk/by-id/wwm-0x5000cca26fef696c"
"--device=/dev/disk/by-id/wwn-0x5000cca270db1d0e:/dev/disk/by-id/wwn-0x5000cca270db1d0e"
# "--device=/dev/sdd:/dev/sdd" Removing this one while the disk is down
"--device=/dev/sda:/dev/sda"
"--device=/dev/sdb:/dev/sdb"
"--device=/dev/sdc:/dev/sdc"
"--device=/dev/sdd:/dev/sdd"
];
volumes = [
"/run/udev:/run/udev:ro"
@@ -179,5 +329,4 @@
];
};
};
};
}

View File

@@ -1,104 +0,0 @@
{ config, ... }:
let
inherit (import ../lib.nix config) mkContainer blazestar;
matrixHost = "matrix";
serviceName = "matrix-blazestar-net";
dbPath = "/var/lib/matrix";
port = 8448;
in
{
sops.secrets = {
"matrix/blazestar-registration-token" = {
restartUnits = [ "${config.local.container-backend}-matrix-blazestar-net.service" ];
};
};
sops.templates."matrix-blazestar-net.env".content = ''
TUWUNEL_REGISTRATION_TOKEN=${config.sops.placeholder."matrix/blazestar-registration-token"}
'';
# This isn't using any of my usual helpers because I wanted to set a custom
# serviceName in Traefik that is different from the hostname to avoid
# conflicts with the havenisms.com server.
virtualisation.oci-containers.containers."${serviceName}" = {
# The 1.1.0 version has an issue with the compression being incorrectly tagged.
# See: https://github.com/matrix-construct/tuwunel/issues/79
image = "ghcr.io/matrix-construct/tuwunel:v1.0.0-release-all-x86_64-linux-gnu";
autoStart = true;
volumes = [
"matrix-blazestar-net-db:${dbPath}"
];
environment = {
TUWUNEL_PORT = toString port;
TUWUNEL_ADDRESS = "0.0.0.0"; # It'll bind to localhost by default with Podman
TUWUNEL_SERVER_NAME = "blazestar.net";
TUWUNEL_ALLOW_REGISTRATION = "true";
TUWUNEL_ALLOW_CHECK_FOR_UPDATES = "true";
TUWUNEL_ALLOW_FEDERATION = "true";
TUWUNEL_DATABASE_BACKEND = "rocksdb";
TUWUNEL_DATABASE_PATH = dbPath;
TUWUNEL_WELL_KNOWN = ''
{
client=https://${matrixHost}.blazestar.net,
server=${matrixHost}.blazestar.net:443
}
'';
TUWUNEL_TRUSTED_SERVERS = ''["matrix.org", "chat.havenisms.com"]'';
};
environmentFiles = [
config.sops.templates."matrix-blazestar-net.env".path
];
labels = {
"traefik.enable" = "true";
"traefik.http.routers.${serviceName}.rule" = "Host(`${matrixHost}.${blazestar}`)";
"traefik.http.services.${serviceName}.loadbalancer.server.port" = "${toString port}";
# Redirect well-known requests to this host.
"traefik.http.routers.${matrixHost}-blazestar-net-well-known.rule" =
"Host(`blazestar.net`) && PathPrefix(`/.well-known/matrix`)";
"traefik.http.routers.${matrixHost}-blazestar-net-well-known.service" = serviceName;
};
};
# virtualisation.oci-containers.containers.matrix-blazestar-net =
# mkContainer {
# image = "registry.gitlab.com/famedly/conduit/matrix-conduit:latest";
# hostName = hostname;
# domain = blazestar;
# port = port;
# ports = [
# "8449:6167"
# ];
# volumes = [
# "chat-blazestar-net-db:${dbPath}"
# ];
# environment = {
# CONDUIT_PORT = "6167";
# CONDUIT_SERVER_NAME = "blazestar.net";
# CONDUIT_ALLOW_REGISTRATION = "true";
# CONDUIT_DATABASE_BACKEND = "rocksdb";
# CONDUIT_DATABASE_PATH = dbPath;
# CONDUIT_ALLOW_CHECK_FOR_UPDATES = "true";
# CONDUIT_ALLOW_FEDERATION = "true";
# CONDUIT_MAX_REQUEST_SIZE = "20000000";
# CONDUIT_TRUSTED_SERVERS = "[\"matrix.org\"]";
# CONDUIT_MAX_CONCURRENT_REQUESTS = "100";
# CONDUIT_WELL_KNOWN_CLIENT = "https://${hostname}.blazestar.net";
# CONDUIT_WELL_KNOWN_SERVER = "${hostname}.blazestar.net:443";
# CONDUIT_CONFIG = ""; # Ignore the config file
# };
# extraLabels = {
# "traefik.http.routers.${hostname}-blazestar-net-well-known.rule" =
# "Host(`blazestar.net`) && PathPrefix(`/.well-known`)";
# "traefik.http.routers.${hostname}-blazestar-net-well-known.service" = "${hostname}-blazestar-net";
# };
# };
virtualisation.oci-containers.containers.chat = mkContainer {
image = "vectorim/element-web:latest";
hostName = "chat";
port = 8080;
domain = blazestar;
environment = {
ELEMENT_WEB_PORT = "8080";
};
};
}

View File

@@ -1,7 +0,0 @@
{ ... }:
{
imports = [
./chat.nix
./uptime.nix
];
}

View File

@@ -1,15 +0,0 @@
{ config, ... }:
let
inherit (import ../lib.nix config) blazestar;
in
{
virtualisation.web-containers.containers.uptime = {
image = "louislam/uptime-kuma:1";
hostname = "uptime";
domain = blazestar;
port = 3001;
volumes = [
"uptime-kuma:/app/data"
];
};
}

View File

@@ -1,8 +1,7 @@
{ config, ... }:
let
inherit (import ./lib.nix config) mkContainer mkMariaDbContainer havenisms;
in
{
in {
imports = [
(mkMariaDbContainer {
name = "bookstack";
@@ -15,12 +14,12 @@ in
sops.secrets = {
bookstack_app_key = {
restartUnits = [ "${config.local.container-backend}-bookstack.service" ];
restartUnits = [ "podman-bookstack.service" ];
mode = "0400";
owner = config.users.users.bookstack.name;
};
bookstack_db = {
restartUnits = [ "${config.local.container-backend}-bookstack-mariadb.service" ];
restartUnits = [ "podman-bookstack-mariadb.service" ];
mode = "0400";
owner = config.users.users.bookstack.name;
};

View File

@@ -1,108 +0,0 @@
{ config, pkgs, ... }:
let
inherit (import ./lib.nix config) mkContainer localHostRule terakoda;
nginxConf = pkgs.writeText "dm-companion-nginx.conf" ''
user nginx;
worker_processes auto;
# error.log is symlinked to /dev/stderr
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# access.log is symlinked to /dev/stdout
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
gzip on;
server {
listen 80;
server_name dm.blazestar.net;
root /usr/share/nginx/html;
# X-Frame-Options is to prevent from clickJacking attack
add_header X-Frame-Options SAMEORIGIN;
# disable content-type sniffing on some browsers.
add_header X-Content-Type-Options nosniff;
# This header enables the Cross-site scripting (XSS) filter
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "no-referrer-when-downgrade";
# Enables response header of "Vary: Accept-Encoding"
# This lets the cache have different entries depending on the encoding, e.g. compression
gzip_vary on;
# Serve static files separately.
location ~ ^/(robots.txt|manifest.json) {
expires modified 1y;
add_header Cache-Control "public";
access_log off;
}
location / {
try_files $uri $uri/ /index.html;
index index.html;
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
}
}
'';
in
{
virtualisation.oci-containers.containers = {
dm-companion-pocketbase =
let
hostName = "dm-pocketbase";
in
mkContainer {
inherit hostName;
image = "docker.havenisms.com/lazy-dm/pocketbase";
domain = terakoda;
port = 8080;
volumes = [
"/tank/web/dm.terakoda.com/pb_data:/pb/pb_data"
"/tank/web/dm.terakoda.com/pb_migrations:/pb/pb_migrations:ro"
];
environment = { };
extraLabels = {
"traefik.http.routers.${hostName}-api.rule" =
"PathPrefix(`/api`) && ${localHostRule "dm" terakoda}";
"traefik.http.routers.${hostName}-api.service" = "${hostName}";
};
};
dm-companion = mkContainer {
image = "nginx:alpine";
hostName = "dm";
domain = terakoda;
port = 80;
dependsOn = [
"dm-companion-pocketbase"
];
volumes = [
"/tank/web/dm.terakoda.com/deployed:/usr/share/nginx/html:ro"
"${nginxConf}:/etc/nginx/nginx.conf:ro"
];
};
};
}

View File

@@ -1,8 +1,7 @@
{ config, ... }:
let
inherit (import ./lib.nix config) mkContainer mkPostgresContainer terakoda;
in
{
in {
imports = [
(mkPostgresContainer {
name = "focalboard";
@@ -15,26 +14,21 @@ in
sops.secrets = {
"focalboard/database" = {
restartUnits = [
"${config.local.container-backend}-focalboard.service"
"${config.local.container-backend}-focalboard-postgres.service"
];
restartUnits = [ "podman-focalboard.service" "podman-focalboard-postgres.service" ];
mode = "0400";
owner = config.users.users.focalboard.name;
};
};
sops.templates."focalboard-config.json" = {
restartUnits = [ "${config.local.container-backend}-focalboard.service" ];
restartUnits = [ "podman-focalboard.service" ];
owner = config.users.users.focalboard.name;
content = builtins.toJSON {
# Defaults from https://github.com/mattermost-community/focalboard/blob/main/config.json
"serverRoot" = "https://focalboard.terakoda.com";
"port" = 8000;
"dbtype" = "postgres";
"dbconfig" = "postgres://focalboard:${
config.sops.placeholder."focalboard/database"
}@focalboard-postgres/focalboard?sslmode=disable&connect_timeout=10";
"dbconfig" = "postgres://focalboard:${config.sops.placeholder."focalboard/database"}@focalboard-postgres/focalboard?sslmode=disable&connect_timeout=10";
"useSSL" = true;
"prometheus_address" = ":9092";
"session_expire_time" = 2592000;

View File

@@ -5,10 +5,10 @@ in
{
sops.secrets = {
"gitea/db_password" = {
restartUnits = [ "${config.local.container-backend}-gitea.service" ];
restartUnits = [ "podman-gitea.service" ];
};
"gitea/registration_token" = {
restartUnits = [ "${config.local.container-backend}-gitea-runner.service" ];
restartUnits = [ "podman-gitea-runner.service" ];
};
};
@@ -66,7 +66,7 @@ in
];
volumes = [
# The runner will spawn new containers to run the actions
"${config.local.container-socket}:/var/run/docker.sock:ro"
"/var/run/podman/podman.sock:/var/run/docker.sock:ro"
];
};
}

View File

@@ -1,58 +0,0 @@
{ config, ... }:
let
inherit (import ./lib.nix config)
terakoda
blazestar
hostRule
;
in
{
imports = [
../../../features/web-containers.nix
];
virtualisation.web-containers = {
enable = true;
containers = {
goatcounter-terakoda = {
image = "arp242/goatcounter";
hostname = "goatcounter";
domain = terakoda;
public = true;
port = 8080;
volumes = [
"goatcounter-data:/home/goatcounter/goatcounter-data"
];
extraLabels = {
# "traefik.http.middlewares.strip-analytics.stripprefix.prefixes" = "/analytics";
# "traefik.http.routers.www-terakoda-com-goatcounter.middlewares" = "strip-analytics";
# Host the script on www.terakoda.com so that it is easy to fetch
"traefik.http.routers.www-terakoda-com-goatcounter.rule" =
"PathPrefix(`/count`) && ${hostRule "www" terakoda}";
"traefik.http.routers.www-terakoda-com-goatcounter.entrypoints" = "websecure";
"traefik.http.routers.www-terakoda-com-goatcounter.service" = "goatcounter-terakoda-com";
};
};
goatcounter-blazestar = {
image = "arp242/goatcounter";
hostname = "goatcounter";
domain = blazestar;
public = true;
port = 8080;
volumes = [
"goatcounter-data-blazestar:/home/goatcounter/goatcounter-data"
];
extraLabels = {
# "traefik.http.middlewares.strip-analytics.stripprefix.prefixes" = "/analytics";
# "traefik.http.routers.www-blazestar-net-goatcounter.middlewares" = "strip-analytics";
# Host the script on www.blazestar.net so that it is easy to fetch
"traefik.http.routers.www-blazestar-net-goatcounter.rule" =
"PathPrefix(`/count`) && ${hostRule "www" blazestar}";
"traefik.http.routers.www-blazestar-net-goatcounter.entrypoints" = "websecure";
"traefik.http.routers.www-blazestar-net-goatcounter.service" = "goatcounter-blazestar-net@docker";
};
};
};
};
}

View File

@@ -1,8 +1,7 @@
{ config, ... }:
let
lib = import ./lib.nix config;
in
{
in {
imports = [
./shared-postgres.nix
];
@@ -44,4 +43,25 @@ in
};
environment = {};
};
services.promtail = {
enable = true;
configuration = {
server.http_listen_port = 9080;
clients = [
{ url = "https://loki.blazestar.net/loki/api/v1/push"; }
];
scrape_configs = [{
job_name = "journal";
journal = {
max_age = "12h";
labels = { job = "systemd-journal"; };
};
relabel_configs = [{
source_labels = ["__journal__systemd_unit"];
target_label = "unit";
}];
}];
};
};
}

View File

@@ -1,84 +0,0 @@
{ config, ... }:
let
inherit (import ../lib.nix config) hostRule havenisms;
syncRule = "(PathPrefix(`/client/`) || PathPrefix(`/_matrix/client/unstable/org.matrix.msc3575/sync`))";
wellKnownRule = "(Host(`havenisms.com`) || Host(`chat.havenisms.com`)) && PathPrefix(`/.well-known`)";
in
{
sops.secrets = {
"matrix/syncv3/db-password" = {
restartUnits = [ "${config.local.container-backend}-matrix-sliding-sync.service" ];
};
"matrix/syncv3/secret" = {
restartUnits = [ "${config.local.container-backend}-matrix-sliding-sync.service" ];
};
};
sops.templates."matrix-sliding-sync.env".content = ''
SYNCV3_SERVER=http://synapse:8008
SYNCV3_DB=postgres://syncv3:${
config.sops.placeholder."matrix/syncv3/db-password"
}@db:5432/syncv3?sslmode=disable
SYNCV3_SECRET=${config.sops.placeholder."matrix/syncv3/secret"}
SYNCV3_BINDADDR=:8009
'';
virtualisation.oci-containers.containers = {
synapse = {
image = "docker.io/matrixdotorg/synapse:latest";
autoStart = true;
dependsOn = [
"db"
];
volumes = [
"/tank/config/synapse/data:/data"
];
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.synapse.rule=${hostRule "chat" havenisms} && !(${syncRule} || ${wellKnownRule})"
"-l=traefik.http.routers.synapse.service=synapse"
"-l=traefik.http.services.synapse.loadbalancer.server.port=8008"
# Federation forwarding
"-l=traefik.http.routers.synapse-federation.rule=${hostRule "chat" havenisms}"
"-l=traefik.http.routers.synapse-federation.service=synapse-federation"
"-l=traefik.http.routers.synapse-federation.entrypoints=matrix-federation"
"-l=traefik.http.services.synapse-federation.loadbalancer.server.port=8448"
];
};
matrix-sliding-sync = {
image = "ghcr.io/matrix-org/sliding-sync:latest";
dependsOn = [
"db"
"synapse"
];
environmentFiles = [
config.sops.templates."matrix-sliding-sync.env".path
];
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.syncv3.rule=${hostRule "chat" havenisms} && ${syncRule}"
"-l=traefik.http.services.syncv3.loadbalancer.server.port=8009"
];
};
# This server helps to serve the .well-known files that are required by clients to find the sync server.
matrix-well-known = {
image = "nginx";
dependsOn = [ "synapse" ];
volumes = [
"/tank/config/synapse/static-files:/usr/share/nginx/html:ro"
];
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.middlewares.strip-well-known.stripprefix.prefixes=/.well-known"
"-l=traefik.http.routers.matrix-well-known.rule=${wellKnownRule}"
"-l=traefik.http.routers.matrix-well-known.middlewares=strip-well-known"
"-l=traefik.http.services.matrix-well-known.loadbalancer.server.port=80"
];
};
};
}

View File

@@ -1,11 +0,0 @@
{ ... }:
{
imports = [
./chat.nix
# Currently disabled because it doesn't start up properly
# ./immich.nix
./storyden.nix
./tandoor.nix
./wallabag.nix
];
}

View File

@@ -1,73 +0,0 @@
{ config, ... }:
let
inherit (import ../lib.nix config) havenisms mkPostgresContainer;
in
{
imports = [
(mkPostgresContainer {
# Immich wants a custom build of postgres with the vectors extensions.
image = "ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:c44be5f2871c59362966d71eab4268170eb6f5653c0e6170184e72b38ffdf107";
name = "immich";
directory = "/tank/immich/db";
uid = config.users.users.immich.uid;
gid = config.users.groups.immich.gid;
passwordSecret = "immich/database";
})
];
sops.secrets = {
"immich/database" = {
restartUnits = [
"${config.local.container-backend}-immich-db.service"
];
mode = "0400";
owner = config.users.users.immich.name;
};
};
sops.templates."immich.env" = {
restartUnits = [ "${config.local.container-backend}-immich.service" ];
owner = config.users.users.immich.name;
content = ''
DB_HOSTNAME=immich-postgres
DB_PASSWORD=${config.sops.placeholder."immich/database"}
DB_USERNAME=immich
DB_DATABASE_NAME=immich
REDIS_HOSTNAME=immich-redis
IMMICH_LOG_LEVEL=verbose
'';
};
virtualisation.web-containers.containers.immich = {
image = "ghcr.io/immich-app/immich-server:release";
hostname = "immich";
domain = havenisms;
port = 2283;
volumes = [
"/tank/photos/immich:/data"
"/etc/localtime:/etc/localtime:ro"
];
dependsOn = [
"immich-redis"
"immich-postgres"
];
environmentFiles = [
"${config.sops.templates."immich.env".path}"
];
};
virtualisation.oci-containers.containers = {
"immich-redis" = {
image = "docker.io/valkey/valkey";
};
"immich-machine-learning" = {
image = "ghcr.io/immich-app/immich-machine-learning:release";
volumes = [
"model-cache:/cache"
];
environmentFiles = [
"${config.sops.templates."immich.env".path}"
];
};
};
}

View File

@@ -1,17 +0,0 @@
{ config, ... }:
let
inherit (import ../lib.nix config) havenisms;
in
{
virtualisation.web-containers.containers.storyden = {
image = "ghcr.io/southclaws/storyden";
port = 8000;
hostname = "storyden";
domain = havenisms;
environment = {
PUBLIC_WEB_ADDRESS = "https://storyden.${havenisms}";
PUBLIC_API_ADDRESS = "https://storyden.${havenisms}";
};
};
}

View File

@@ -1,30 +0,0 @@
{ config, ... }:
let
inherit (import ../lib.nix config) havenisms;
in
{
sops.secrets = {
"tandoor/secret_key" = {
restartUnits = [ "${config.local.container-backend}-tandoor.service" ];
};
};
sops.templates."tandoor.env".content = ''
SECRET_KEY="${config.sops.placeholder."tandoor/secret_key"}"
DB_ENGINE=django.db.backends.sqlite3
'';
virtualisation.web-containers.containers.tandoor = {
image = "vabene1111/recipes";
hostname = "recipes";
domain = havenisms;
port = 80;
volumes = [
"/tank/tandoor-recipes/mediafiles:/opt/recipes/mediafiles"
"/tank/tandoor-recipes/staticfiles:/opt/recipes/staticfiles"
];
environmentFiles = [
config.sops.templates."tandoor.env".path
];
};
}

Some files were not shown because too many files have changed in this diff Show More