init
This commit is contained in:
commit
2be8de47fa
87 changed files with 11501 additions and 0 deletions
72
modules/home/antigravity2api.nix
Normal file
72
modules/home/antigravity2api.nix
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.services.antigravity2api;
|
||||
workDir = "/home/ashie/git/antigravity2api-nodejs";
|
||||
in
|
||||
{
|
||||
options.services.antigravity2api = {
|
||||
enable = lib.mkEnableOption "Antigravity2API service";
|
||||
|
||||
credentials = {
|
||||
username = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "admin";
|
||||
description = "Admin username for the dashboard";
|
||||
};
|
||||
password = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "password";
|
||||
description = "Admin password for the dashboard";
|
||||
};
|
||||
apiKey = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
description = "API Key for client access";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.user.services.antigravity2api = {
|
||||
Unit = {
|
||||
Description = "Antigravity API to OpenAI Proxy";
|
||||
After = [ "network.target" ];
|
||||
};
|
||||
|
||||
Service = {
|
||||
ExecStartPre = pkgs.writeShellScript "antigravity2api-init" ''
|
||||
cat > ${workDir}/.env <<EOF
|
||||
API_KEY=${cfg.credentials.apiKey}
|
||||
ADMIN_USERNAME=${cfg.credentials.username}
|
||||
ADMIN_PASSWORD=${cfg.credentials.password}
|
||||
SYSTEM_INSTRUCTION="你是聊天机器人,名字叫萌萌,如同名字这般,你的性格是软软糯糯萌萌哒的,专门为用户提供聊天和情绪价值,协助进行小说创作或者角色扮演"
|
||||
OFFICIAL_SYSTEM_PROMPT="You are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.You are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.**Proactiveness**"
|
||||
EOF
|
||||
'';
|
||||
|
||||
ExecStart = ''
|
||||
${pkgs.podman}/bin/podman run --replace --rm --name antigravity2api \
|
||||
-p 8045:8045 \
|
||||
-v ${workDir}/data:/app/data \
|
||||
-v ${workDir}/public/images:/app/public/images \
|
||||
-v ${workDir}/.env:/app/.env \
|
||||
-v ${workDir}/config.json:/app/config.json \
|
||||
localhost/antigravity2api
|
||||
'';
|
||||
ExecStop = "${pkgs.podman}/bin/podman stop antigravity2api";
|
||||
Restart = "always";
|
||||
RestartSec = "10";
|
||||
};
|
||||
|
||||
Install = {
|
||||
WantedBy = [ "default.target" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
118
modules/home/browser-container-update.nix
Normal file
118
modules/home/browser-container-update.nix
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
# Browser Container Update Module (Home Manager)
|
||||
# Provides: Auto-update timer for browser container images
|
||||
#
|
||||
# Usage:
|
||||
# myModules.browserContainerUpdate = {
|
||||
# enable = true;
|
||||
# repositoryPath = "/home/user/nixos";
|
||||
# schedule = "weekly";
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.browserContainerUpdate;
|
||||
in
|
||||
{
|
||||
options.myModules.browserContainerUpdate = {
|
||||
enable = lib.mkEnableOption "Browser container auto-update timer";
|
||||
|
||||
repositoryPath = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = config.myModules.common.repoPath;
|
||||
description = "Path to repository containing container Dockerfiles";
|
||||
};
|
||||
|
||||
schedule = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "weekly";
|
||||
description = "systemd calendar expression for update schedule";
|
||||
};
|
||||
|
||||
randomDelay = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "1h";
|
||||
description = "Random delay before running update";
|
||||
};
|
||||
|
||||
browsers = lib.mkOption {
|
||||
type = lib.types.listOf (
|
||||
lib.types.enum [
|
||||
"firefox"
|
||||
"tor-browser"
|
||||
"thorium"
|
||||
]
|
||||
);
|
||||
default = [
|
||||
"firefox"
|
||||
"tor-browser"
|
||||
"thorium"
|
||||
];
|
||||
description = "Which browser containers to update";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.user.services.browser-containers-update = {
|
||||
Unit = {
|
||||
Description = "Update browser container images";
|
||||
};
|
||||
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
ExecStart = pkgs.writeShellScript "update-browser-containers" ''
|
||||
set -e
|
||||
REPO_DIR="${cfg.repositoryPath}"
|
||||
|
||||
${lib.optionalString (builtins.elem "firefox" cfg.browsers) ''
|
||||
echo "=== Updating Firefox container ==="
|
||||
${pkgs.podman}/bin/podman build --pull --no-cache \
|
||||
-t localhost/firefox-wayland:latest \
|
||||
"$REPO_DIR/containers/firefox-wayland/"
|
||||
''}
|
||||
|
||||
${lib.optionalString (builtins.elem "tor-browser" cfg.browsers) ''
|
||||
echo "=== Updating Tor Browser container ==="
|
||||
${pkgs.podman}/bin/podman build --pull --no-cache \
|
||||
-t localhost/tor-browser-wayland:latest \
|
||||
"$REPO_DIR/containers/tor-browser-wayland/"
|
||||
''}
|
||||
|
||||
${lib.optionalString (builtins.elem "thorium" cfg.browsers) ''
|
||||
echo "=== Updating Thorium container ==="
|
||||
${pkgs.podman}/bin/podman build --pull --no-cache \
|
||||
-t localhost/thorium-wayland:latest \
|
||||
"$REPO_DIR/containers/thorium-wayland/"
|
||||
''}
|
||||
|
||||
echo "=== Cleaning old images ==="
|
||||
${pkgs.podman}/bin/podman image prune -f
|
||||
|
||||
echo "=== Update complete ==="
|
||||
${pkgs.libnotify}/bin/notify-send "Browser Containers" "Updated browser containers" --icon=security-high
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
systemd.user.timers.browser-containers-update = {
|
||||
Unit = {
|
||||
Description = "Weekly browser container update timer";
|
||||
};
|
||||
|
||||
Timer = {
|
||||
OnCalendar = cfg.schedule;
|
||||
Persistent = true;
|
||||
RandomizedDelaySec = cfg.randomDelay;
|
||||
};
|
||||
|
||||
Install = {
|
||||
WantedBy = [ "timers.target" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
92
modules/home/cli-tools.nix
Normal file
92
modules/home/cli-tools.nix
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
programs = {
|
||||
# Modern replacement for 'ls'
|
||||
eza = {
|
||||
enable = true;
|
||||
icons = "auto";
|
||||
git = true;
|
||||
enableBashIntegration = true;
|
||||
};
|
||||
|
||||
# A cat(1) clone with wings
|
||||
bat = {
|
||||
enable = true;
|
||||
config = {
|
||||
theme = "Catppuccin Mocha";
|
||||
};
|
||||
};
|
||||
|
||||
# A smarter cd command
|
||||
zoxide = {
|
||||
enable = true;
|
||||
enableBashIntegration = true;
|
||||
options = [
|
||||
"--cmd cd" # Replace cd with zoxide
|
||||
];
|
||||
};
|
||||
|
||||
# Command-line fuzzy finder
|
||||
fzf = {
|
||||
enable = true;
|
||||
enableBashIntegration = true;
|
||||
};
|
||||
|
||||
# Modern grep replacement
|
||||
ripgrep.enable = true;
|
||||
|
||||
# Modern find replacement
|
||||
fd.enable = true;
|
||||
|
||||
# Magical Shell History
|
||||
atuin = {
|
||||
enable = true;
|
||||
enableFishIntegration = true;
|
||||
flags = [
|
||||
"--disable-up-arrow"
|
||||
];
|
||||
};
|
||||
|
||||
# Terminal Multiplexer
|
||||
zellij = {
|
||||
enable = true;
|
||||
enableFishIntegration = false;
|
||||
settings = {
|
||||
theme = "catppuccin-mocha";
|
||||
show_startup_tips = false;
|
||||
};
|
||||
};
|
||||
|
||||
# Interactive Cheatsheet
|
||||
navi = {
|
||||
enable = true;
|
||||
enableFishIntegration = true;
|
||||
};
|
||||
};
|
||||
|
||||
home.packages = with pkgs; [
|
||||
nh # Nix helper for faster rebuilds
|
||||
];
|
||||
|
||||
# Point nh to the flake location
|
||||
home.sessionVariables = {
|
||||
NH_FLAKE = "/home/ashie/nixos";
|
||||
};
|
||||
|
||||
home.shellAliases = {
|
||||
cat = "bat";
|
||||
grep = "rg";
|
||||
find = "fd";
|
||||
top = "btm";
|
||||
ps = "procs";
|
||||
sed = "sd";
|
||||
du = "dust -r";
|
||||
|
||||
# Enhanced eza aliases
|
||||
ls = "eza --icons=auto";
|
||||
ll = "eza --icons=auto --long --git";
|
||||
la = "eza --icons=auto --all";
|
||||
lla = "eza --icons=auto --all --long --git";
|
||||
tree = "eza --icons=auto --tree";
|
||||
};
|
||||
}
|
||||
14
modules/home/common.nix
Normal file
14
modules/home/common.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
{
|
||||
options.myModules.common = {
|
||||
repoPath = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/home/ashie/nixos";
|
||||
description = "Path to the main NixOS configuration repository";
|
||||
};
|
||||
};
|
||||
}
|
||||
28
modules/home/default.nix
Normal file
28
modules/home/default.nix
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Home Manager Modules Index
|
||||
# Import this to get all home-manager modules
|
||||
#
|
||||
# Usage in home.nix:
|
||||
# imports = [ ./modules/home ];
|
||||
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./common.nix
|
||||
./hyprland-catppuccin.nix
|
||||
./niri.nix
|
||||
./gluetun-user.nix
|
||||
./qbittorrent-vpn.nix
|
||||
./browser-container-update.nix
|
||||
./proton-cachyos-updater.nix
|
||||
./cli-tools.nix
|
||||
|
||||
# ./unified-router.nix
|
||||
./sillytavern.nix
|
||||
|
||||
./niri.nix
|
||||
./noctalia.nix
|
||||
./polling-rate.nix
|
||||
./antigravity2api.nix
|
||||
./theme.nix
|
||||
];
|
||||
}
|
||||
124
modules/home/gluetun-user.nix
Normal file
124
modules/home/gluetun-user.nix
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
# Gluetun User Service Module (Home Manager)
|
||||
# Provides: Rootless Gluetun VPN container as user systemd service
|
||||
#
|
||||
# Usage:
|
||||
# myModules.gluetunUser = {
|
||||
# enable = true;
|
||||
# environmentFile = "/run/secrets/rendered/gluetun.env";
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.gluetunUser;
|
||||
in
|
||||
{
|
||||
options.myModules.gluetunUser = {
|
||||
enable = lib.mkEnableOption "Rootless Gluetun VPN container service";
|
||||
|
||||
image = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "qmcgaw/gluetun:latest";
|
||||
description = "Gluetun container image";
|
||||
};
|
||||
|
||||
webPort = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8080;
|
||||
description = "Web UI port for Gluetun";
|
||||
};
|
||||
|
||||
environmentFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Path to environment file with VPN credentials";
|
||||
};
|
||||
|
||||
secretsDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/run/secrets";
|
||||
description = "Path to secrets directory";
|
||||
};
|
||||
|
||||
dnsAddress = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "1.1.1.1";
|
||||
description = "DNS server address for VPN";
|
||||
};
|
||||
|
||||
extraPorts = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "Extra ports to map (e.g. ['3000:80'])";
|
||||
};
|
||||
|
||||
wireguardMtu = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 1280;
|
||||
description = "WireGuard MTU setting";
|
||||
};
|
||||
|
||||
keepaliveInterval = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "15s";
|
||||
description = "WireGuard persistent keepalive interval";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.user.services.gluetun = {
|
||||
Unit = {
|
||||
Description = "Gluetun VPN Container (Rootless)";
|
||||
After = [ "network-online.target" ];
|
||||
Wants = [ "network-online.target" ];
|
||||
};
|
||||
|
||||
Service = {
|
||||
Restart = "always";
|
||||
ExecStartPre = [
|
||||
"-${pkgs.podman}/bin/podman system migrate"
|
||||
"-${pkgs.podman}/bin/podman stop gluetun"
|
||||
];
|
||||
ExecStart = ''
|
||||
${pkgs.podman}/bin/podman run --rm --replace --name gluetun \
|
||||
--cap-add=NET_ADMIN \
|
||||
--device=/dev/net/tun \
|
||||
--sysctl=net.ipv4.conf.all.src_valid_mark=1 \
|
||||
--add-host=host.containers.internal:host-gateway \
|
||||
-v ${cfg.environmentFile}:${cfg.environmentFile}:ro \
|
||||
-v ${cfg.secretsDir}:${cfg.secretsDir}:ro \
|
||||
-p 127.0.0.1:${toString cfg.webPort}:8080 \
|
||||
${lib.concatMapStringsSep " " (p: "-p ${p}") cfg.extraPorts} \
|
||||
-e VPN_SERVICE_PROVIDER=custom \
|
||||
-e VPN_TYPE=wireguard \
|
||||
-e WIREGUARD_IMPLEMENTATION=userspace \
|
||||
-e WIREGUARD_PRIVATE_KEY_SECRETFILE=${cfg.secretsDir}/wireguard_private_key \
|
||||
-e WIREGUARD_PRESHARED_KEY_SECRETFILE=${cfg.secretsDir}/wireguard_preshared_key \
|
||||
-e WIREGUARD_ADDRESSES_SECRETFILE=${cfg.secretsDir}/wireguard_addresses \
|
||||
-e WIREGUARD_PUBLIC_KEY=''${WIREGUARD_PUBLIC_KEY} \
|
||||
-e WIREGUARD_ENDPOINT_IP=''${WIREGUARD_ENDPOINT_IP} \
|
||||
-e WIREGUARD_ENDPOINT_PORT=''${WIREGUARD_ENDPOINT_PORT} \
|
||||
-e WIREGUARD_MTU=1280 \
|
||||
-e WIREGUARD_PERSISTENT_KEEPALIVE_INTERVAL=15s \
|
||||
-e DNS_ADDRESS=${cfg.dnsAddress} \
|
||||
-e DOT=off \
|
||||
-e DOT_CACHING=on \
|
||||
-e HEALTH_RESTART_VPN=off \
|
||||
-e LOG_LEVEL=info \
|
||||
-e FIREWALL_VPN_INPUT_PORTS=4000,3001,3000,9090,36630 \
|
||||
${cfg.image}
|
||||
'';
|
||||
EnvironmentFile = [ cfg.environmentFile ];
|
||||
ExecStop = "${pkgs.podman}/bin/podman stop gluetun";
|
||||
};
|
||||
|
||||
Install = {
|
||||
WantedBy = [ "default.target" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
291
modules/home/hyprland-catppuccin.nix
Normal file
291
modules/home/hyprland-catppuccin.nix
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
# Hyprland Catppuccin Theme Module (Home Manager)
|
||||
# Provides: Catppuccin-themed Hyprland with animations and keybinds
|
||||
#
|
||||
# Usage:
|
||||
# myModules.hyprlandCatppuccin = {
|
||||
# enable = true;
|
||||
# keyboardLayout = "de";
|
||||
# primaryMonitor = "DP-2";
|
||||
# secondaryMonitor = "HDMI-A-1";
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.hyprlandCatppuccin;
|
||||
in
|
||||
{
|
||||
options.myModules.hyprlandCatppuccin = {
|
||||
enable = lib.mkEnableOption "Catppuccin-themed Hyprland configuration";
|
||||
|
||||
keyboardLayout = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "us";
|
||||
description = "Keyboard layout";
|
||||
};
|
||||
|
||||
keyboardVariant = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
description = "Keyboard variant (e.g., 'nodeadkeys')";
|
||||
};
|
||||
|
||||
keyboardModel = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "pc104";
|
||||
description = "Keyboard model (e.g., 'pc104' or 'pc105')";
|
||||
};
|
||||
|
||||
capsToEscape = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Remap Caps Lock to Escape";
|
||||
};
|
||||
|
||||
primaryMonitor = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "DP-1";
|
||||
description = "Primary monitor name";
|
||||
};
|
||||
|
||||
primaryResolution = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "2560x1440@165";
|
||||
description = "Primary monitor resolution and refresh rate";
|
||||
};
|
||||
|
||||
secondaryMonitor = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Secondary monitor name (null to disable)";
|
||||
};
|
||||
|
||||
secondaryResolution = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "1920x1080@60";
|
||||
description = "Secondary monitor resolution";
|
||||
};
|
||||
|
||||
terminal = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "kitty";
|
||||
description = "Default terminal emulator";
|
||||
};
|
||||
|
||||
fileManager = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "nautilus";
|
||||
description = "Default file manager";
|
||||
};
|
||||
|
||||
launcher = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "noctalia-shell ipc call launcher toggle";
|
||||
description = "Application launcher";
|
||||
};
|
||||
|
||||
gapsIn = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 3;
|
||||
description = "Inner gaps between windows";
|
||||
};
|
||||
|
||||
gapsOut = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 8;
|
||||
description = "Outer gaps around windows";
|
||||
};
|
||||
|
||||
borderSize = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 2;
|
||||
description = "Window border size";
|
||||
};
|
||||
|
||||
rounding = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 10;
|
||||
description = "Window corner rounding";
|
||||
};
|
||||
|
||||
enableBlur = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Enable window blur effect";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
wayland.windowManager.hyprland = {
|
||||
enable = true;
|
||||
xwayland.enable = true;
|
||||
|
||||
settings = {
|
||||
input = {
|
||||
kb_layout = cfg.keyboardLayout;
|
||||
kb_variant = cfg.keyboardVariant;
|
||||
kb_model = cfg.keyboardModel;
|
||||
kb_options = lib.mkIf cfg.capsToEscape "caps:escape";
|
||||
accel_profile = "flat";
|
||||
};
|
||||
|
||||
exec-once = [
|
||||
"gnome-keyring-daemon --start --components=secrets,pkcs11"
|
||||
"lxqt-policykit-agent"
|
||||
"swww-daemon"
|
||||
"swww img /home/ashie/Pictures/Wallpapers/chill-mario.gif"
|
||||
"noctalia-shell"
|
||||
];
|
||||
|
||||
bind = [
|
||||
"SUPER, Return, exec, ${cfg.terminal}"
|
||||
"SUPER, Q, killactive"
|
||||
"SUPER, E, exec, ${cfg.fileManager}"
|
||||
"SUPER, 1, exec, ~/.config/hypr/ws-go.sh workspace 1"
|
||||
"SUPER, 2, exec, ~/.config/hypr/ws-go.sh workspace 2"
|
||||
"SUPER, 3, exec, ~/.config/hypr/ws-go.sh workspace 3"
|
||||
"SUPER, 4, exec, ~/.config/hypr/ws-go.sh workspace 4"
|
||||
"SUPER, 5, exec, ~/.config/hypr/ws-go.sh workspace 5"
|
||||
"SUPER, 6, exec, ~/.config/hypr/ws-go.sh workspace 6"
|
||||
"SUPER, 7, exec, ~/.config/hypr/ws-go.sh workspace 7"
|
||||
"SUPER, 8, exec, ~/.config/hypr/ws-go.sh workspace 8"
|
||||
"SUPER, 9, exec, ~/.config/hypr/ws-go.sh workspace 9"
|
||||
"SUPER, 0, exec, ~/.config/hypr/ws-go.sh workspace 0"
|
||||
|
||||
"SUPER_CTRL, 1, exec, ~/.config/hypr/ws-go.sh movetoworkspace 1"
|
||||
"SUPER_CTRL, 2, exec, ~/.config/hypr/ws-go.sh movetoworkspace 2"
|
||||
"SUPER_CTRL, 3, exec, ~/.config/hypr/ws-go.sh movetoworkspace 3"
|
||||
"SUPER_CTRL, 4, exec, ~/.config/hypr/ws-go.sh movetoworkspace 4"
|
||||
"SUPER_CTRL, 5, exec, ~/.config/hypr/ws-go.sh movetoworkspace 5"
|
||||
"SUPER_CTRL, 6, exec, ~/.config/hypr/ws-go.sh movetoworkspace 6"
|
||||
"SUPER_CTRL, 7, exec, ~/.config/hypr/ws-go.sh movetoworkspace 7"
|
||||
"SUPER_CTRL, 8, exec, ~/.config/hypr/ws-go.sh movetoworkspace 8"
|
||||
"SUPER_CTRL, 9, exec, ~/.config/hypr/ws-go.sh movetoworkspace 9"
|
||||
"SUPER_CTRL, 0, exec, ~/.config/hypr/ws-go.sh movetoworkspace 0"
|
||||
|
||||
"SUPER, F, fullscreen, 0"
|
||||
"SUPER, SPACE, togglefloating"
|
||||
# Browsers (all via isolated Podman containers)
|
||||
"SUPER, W, exec, firefox-vpn-podman"
|
||||
"SUPER ALT, W, exec, tor-browser-vpn-podman"
|
||||
"SUPER ALT, Return, exec, kitty-vpn-podman"
|
||||
"SUPER SHIFT, W, exec, thorium-vpn-podman"
|
||||
|
||||
# Media Controls
|
||||
", XF86AudioPlay, exec, playerctl play-pause"
|
||||
", XF86AudioNext, exec, playerctl next"
|
||||
", XF86AudioPrev, exec, playerctl previous"
|
||||
", XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
|
||||
", XF86AudioRaiseVolume, exec, wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+"
|
||||
", XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
|
||||
];
|
||||
|
||||
bindm = [
|
||||
"SUPER, mouse:272, movewindow"
|
||||
"SUPER, mouse:273, resizewindow"
|
||||
];
|
||||
|
||||
monitor = [
|
||||
"${cfg.primaryMonitor}, ${cfg.primaryResolution}, 1920x0, 1"
|
||||
]
|
||||
++ lib.optional (
|
||||
cfg.secondaryMonitor != null
|
||||
) "${cfg.secondaryMonitor}, ${cfg.secondaryResolution}, 0x0, 1";
|
||||
|
||||
workspace = [
|
||||
"1, monitor:${cfg.primaryMonitor}"
|
||||
"2, monitor:${cfg.primaryMonitor}"
|
||||
"3, monitor:${cfg.primaryMonitor}"
|
||||
"4, monitor:${cfg.primaryMonitor}"
|
||||
"5, monitor:${cfg.primaryMonitor}"
|
||||
"6, monitor:${cfg.primaryMonitor}"
|
||||
"7, monitor:${cfg.primaryMonitor}"
|
||||
"8, monitor:${cfg.primaryMonitor}"
|
||||
"9, monitor:${cfg.primaryMonitor}"
|
||||
"10, monitor:${cfg.primaryMonitor}"
|
||||
]
|
||||
++ lib.optionals (cfg.secondaryMonitor != null) [
|
||||
"12, monitor:${cfg.secondaryMonitor}"
|
||||
"13, monitor:${cfg.secondaryMonitor}"
|
||||
"14, monitor:${cfg.secondaryMonitor}"
|
||||
"15, monitor:${cfg.secondaryMonitor}"
|
||||
"16, monitor:${cfg.secondaryMonitor}"
|
||||
"17, monitor:${cfg.secondaryMonitor}"
|
||||
"18, monitor:${cfg.secondaryMonitor}"
|
||||
"19, monitor:${cfg.secondaryMonitor}"
|
||||
"20, monitor:${cfg.secondaryMonitor}"
|
||||
];
|
||||
};
|
||||
|
||||
extraConfig = ''
|
||||
bind = Super, Super_L, exec, ${cfg.launcher}
|
||||
windowrulev2 = float, class:^(Tor Browser)$
|
||||
env = HYPRCURSOR_THEME,"Future-Cyan-Hyprcursor_Theme"
|
||||
env = HYPRCURSOR_SIZE,32
|
||||
|
||||
animations {
|
||||
enabled = 1
|
||||
bezier = default, 0.12, 0.92, 0.08, 1.0
|
||||
bezier = wind, 0.12, 0.92, 0.08, 1.0
|
||||
bezier = overshot, 0.18, 0.95, 0.22, 1.03
|
||||
bezier = liner, 1, 1, 1, 1
|
||||
|
||||
animation = windows, 1, 5, wind, popin 60%
|
||||
animation = windowsIn, 1, 6, overshot, popin 60%
|
||||
animation = windowsOut, 1, 4, overshot, popin 60%
|
||||
animation = windowsMove, 1, 4, overshot, slide
|
||||
animation = layers, 1, 4, default, popin
|
||||
animation = fadeIn, 1, 7, default
|
||||
animation = fadeOut, 1, 7, default
|
||||
animation = fadeSwitch, 1, 7, default
|
||||
animation = fadeShadow, 1, 7, default
|
||||
animation = fadeDim, 1, 7, default
|
||||
animation = fadeLayers, 1, 7, default
|
||||
animation = workspaces, 1, 5, overshot, slidevert
|
||||
animation = border, 1, 1, liner
|
||||
animation = borderangle, 1, 24, liner, loop
|
||||
}
|
||||
|
||||
env = QT_QPA_PLATFORMTHEME, qt6ct
|
||||
|
||||
general {
|
||||
gaps_in = ${toString cfg.gapsIn}
|
||||
gaps_out = ${toString cfg.gapsOut}
|
||||
border_size = ${toString cfg.borderSize}
|
||||
col.active_border = rgba(ca9ee6ff) rgba(f2d5cfff) 45deg
|
||||
col.inactive_border = rgba(b4befecc) rgba(6c7086cc) 45deg
|
||||
layout = dwindle
|
||||
resize_on_border = true
|
||||
}
|
||||
|
||||
group {
|
||||
col.border_active = rgba(ca9ee6ff) rgba(f2d5cfff) 45deg
|
||||
col.border_inactive = rgba(b4befecc) rgba(6c7086cc) 45deg
|
||||
col.border_locked_active = rgba(ca9ee6ff) rgba(f2d5cfff) 45deg
|
||||
col.border_locked_inactive = rgba(b4befecc) rgba(6c7086cc) 45deg
|
||||
}
|
||||
|
||||
decoration {
|
||||
rounding = ${toString cfg.rounding}
|
||||
shadow:enabled = false
|
||||
|
||||
blur {
|
||||
enabled = ${if cfg.enableBlur then "yes" else "no"}
|
||||
size = 6
|
||||
passes = 3
|
||||
new_optimizations = on
|
||||
ignore_opacity = on
|
||||
xray = false
|
||||
}
|
||||
}
|
||||
|
||||
bind = , Print, exec, grim -g "$(slurp -w 0)" - | wl-copy
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
264
modules/home/niri.nix
Normal file
264
modules/home/niri.nix
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.niri;
|
||||
in
|
||||
{
|
||||
options.myModules.niri = {
|
||||
enable = lib.mkEnableOption "Niri configuration";
|
||||
|
||||
keyboardLayout = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "us";
|
||||
};
|
||||
|
||||
keyboardVariant = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
};
|
||||
|
||||
keyboardOptions = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "caps:escape";
|
||||
};
|
||||
|
||||
primaryMonitor = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "DP-1";
|
||||
};
|
||||
|
||||
primaryResolution = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "2560x1440@165";
|
||||
};
|
||||
|
||||
secondaryMonitor = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
secondaryResolution = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "1920x1080@60";
|
||||
};
|
||||
|
||||
terminal = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "kitty";
|
||||
};
|
||||
|
||||
launcher = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "noctalia-shell ipc call launcher toggle";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
home.packages = [
|
||||
inputs.niri.packages.${pkgs.system}.niri
|
||||
inputs.niri.packages.${pkgs.system}.niri
|
||||
pkgs.xwayland-satellite
|
||||
pkgs.grim
|
||||
pkgs.slurp
|
||||
pkgs.wl-clipboard
|
||||
pkgs.lxqt.lxqt-policykit
|
||||
pkgs.libnotify
|
||||
pkgs.swww
|
||||
];
|
||||
|
||||
xdg.portal = {
|
||||
enable = true;
|
||||
extraPortals = [
|
||||
pkgs.xdg-desktop-portal-gtk
|
||||
pkgs.xdg-desktop-portal-gnome
|
||||
];
|
||||
config.common.default = [
|
||||
"gtk"
|
||||
"gnome"
|
||||
];
|
||||
};
|
||||
|
||||
systemd.user.services.xwayland-satellite = {
|
||||
Unit = {
|
||||
Description = "Xwayland Satellite (Rootless Xwayland Bridge)";
|
||||
After = [ "graphical-session.target" ];
|
||||
PartOf = [ "graphical-session.target" ];
|
||||
};
|
||||
|
||||
Service = {
|
||||
ExecStart = "${pkgs.xwayland-satellite}/bin/xwayland-satellite";
|
||||
Restart = "always";
|
||||
RestartSec = "10";
|
||||
|
||||
# Security Hardening
|
||||
NoNewPrivileges = true;
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
PrivateTmp = true;
|
||||
RestrictNamespaces = true;
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
};
|
||||
|
||||
Install = {
|
||||
WantedBy = [ "graphical-session.target" ];
|
||||
};
|
||||
};
|
||||
|
||||
xdg.configFile."niri/config.kdl".text = ''
|
||||
input {
|
||||
keyboard {
|
||||
xkb {
|
||||
layout "${cfg.keyboardLayout}"
|
||||
variant "${cfg.keyboardVariant}"
|
||||
options "${cfg.keyboardOptions}"
|
||||
}
|
||||
}
|
||||
mouse {
|
||||
accel-profile "flat"
|
||||
}
|
||||
focus-follows-mouse
|
||||
}
|
||||
|
||||
environment {
|
||||
GTK_THEME "catppuccin-mocha-mauve-standard"
|
||||
GTK_THEME "catppuccin-mocha-mauve-standard"
|
||||
QT_QPA_PLATFORMTHEME "gtk3"
|
||||
QT_STYLE_OVERRIDE "kvantum"
|
||||
XCURSOR_THEME "Bibata-Modern-Ice"
|
||||
XCURSOR_SIZE "24"
|
||||
}
|
||||
|
||||
prefer-no-csd
|
||||
|
||||
|
||||
output "${cfg.primaryMonitor}" {
|
||||
mode "${cfg.primaryResolution}"
|
||||
scale 1.0
|
||||
position x=1920 y=0
|
||||
}
|
||||
|
||||
${lib.optionalString (cfg.secondaryMonitor != null) ''
|
||||
output "${cfg.secondaryMonitor}" {
|
||||
mode "${cfg.secondaryResolution}"
|
||||
scale 1.0
|
||||
position x=0 y=0
|
||||
}
|
||||
''}
|
||||
|
||||
layout {
|
||||
gaps 8
|
||||
center-focused-column "never"
|
||||
|
||||
preset-column-widths {
|
||||
proportion 0.33333
|
||||
proportion 0.5
|
||||
proportion 0.66667
|
||||
}
|
||||
|
||||
default-column-width { proportion 0.5; }
|
||||
|
||||
focus-ring {
|
||||
off
|
||||
}
|
||||
}
|
||||
|
||||
spawn-at-startup "swww-daemon"
|
||||
spawn-at-startup "bash" "-c" "sleep 1; swww img /home/ashie/Pictures/Wallpapers/chill-mario.gif"
|
||||
|
||||
spawn-at-startup "bash" "-c" "sleep 2; dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP; ${
|
||||
inputs.noctalia.packages.${pkgs.system}.default
|
||||
}/bin/noctalia-shell >> /tmp/noctalia.log 2>&1"
|
||||
|
||||
binds {
|
||||
Mod+Return { spawn "${cfg.terminal}"; }
|
||||
Mod+D { spawn "sh" "-c" "${cfg.launcher}"; }
|
||||
Mod+Q { close-window; }
|
||||
Mod+E { spawn "nautilus"; }
|
||||
Mod+F { fullscreen-window; }
|
||||
Mod+M { maximize-column; }
|
||||
Mod+Space { switch-preset-column-width; }
|
||||
|
||||
Mod+1 { focus-workspace 1; }
|
||||
Mod+2 { focus-workspace 2; }
|
||||
Mod+3 { focus-workspace 3; }
|
||||
Mod+4 { focus-workspace 4; }
|
||||
Mod+5 { focus-workspace 5; }
|
||||
Mod+6 { focus-workspace 6; }
|
||||
Mod+7 { focus-workspace 7; }
|
||||
Mod+8 { focus-workspace 8; }
|
||||
Mod+9 { focus-workspace 9; }
|
||||
|
||||
Mod+WheelScrollDown { focus-column-right; }
|
||||
Mod+WheelScrollUp { focus-column-left; }
|
||||
Mod+Left { focus-column-left; }
|
||||
Mod+Right { focus-column-right; }
|
||||
Mod+Up { focus-window-up; }
|
||||
Mod+Down { focus-window-down; }
|
||||
Mod+H { focus-column-left; }
|
||||
Mod+L { focus-column-right; }
|
||||
Mod+K { focus-window-up; }
|
||||
Mod+J { focus-window-down; }
|
||||
|
||||
Mod+Shift+Left { move-column-left; }
|
||||
Mod+Shift+Right { move-column-right; }
|
||||
Mod+Shift+Up { move-window-up; }
|
||||
Mod+Shift+Down { move-window-down; }
|
||||
Mod+Shift+H { move-column-left; }
|
||||
Mod+Shift+L { move-column-right; }
|
||||
Mod+Shift+K { move-window-up; }
|
||||
Mod+Shift+J { move-window-down; }
|
||||
|
||||
Mod+Ctrl+1 { move-column-to-workspace 1; }
|
||||
Mod+Ctrl+2 { move-column-to-workspace 2; }
|
||||
Mod+Ctrl+3 { move-column-to-workspace 3; }
|
||||
Mod+Ctrl+4 { move-column-to-workspace 4; }
|
||||
Mod+Ctrl+5 { move-column-to-workspace 5; }
|
||||
Mod+Ctrl+6 { move-column-to-workspace 6; }
|
||||
Mod+Ctrl+7 { move-column-to-workspace 7; }
|
||||
Mod+Ctrl+8 { move-column-to-workspace 8; }
|
||||
Mod+Ctrl+9 { move-column-to-workspace 9; }
|
||||
|
||||
Mod+BracketLeft { consume-or-expel-window-left; }
|
||||
Mod+BracketRight { consume-or-expel-window-right; }
|
||||
Mod+C { center-column; }
|
||||
|
||||
Mod+Minus { set-column-width "-10%"; }
|
||||
Mod+Equal { set-column-width "+10%"; }
|
||||
|
||||
Mod+Shift+E { quit; }
|
||||
Print { spawn "sh" "-c" "grim -g \"$(slurp)\" - | wl-copy"; }
|
||||
|
||||
// Browsers
|
||||
Mod+W { spawn "firefox"; }
|
||||
Mod+Alt+W { spawn "tor-browser-vpn-podman"; }
|
||||
Mod+Shift+W { spawn "brave"; }
|
||||
Mod+Alt+Return { spawn "kitty-vpn-podman"; }
|
||||
|
||||
// Media
|
||||
XF86AudioRaiseVolume { spawn "wpctl" "set-volume" "-l" "1.5" "@DEFAULT_AUDIO_SINK@" "5%+"; }
|
||||
XF86AudioLowerVolume { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "5%-"; }
|
||||
XF86AudioMute { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; }
|
||||
XF86AudioPlay { spawn "playerctl" "play-pause"; }
|
||||
XF86AudioNext { spawn "playerctl" "next"; }
|
||||
XF86AudioPrev { spawn "playerctl" "previous"; }
|
||||
}
|
||||
|
||||
window-rule {
|
||||
geometry-corner-radius 12
|
||||
clip-to-geometry true
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match app-id="^Tor Browser$"
|
||||
open-floating true
|
||||
}
|
||||
'';
|
||||
};
|
||||
}
|
||||
79
modules/home/noctalia.nix
Normal file
79
modules/home/noctalia.nix
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.noctalia;
|
||||
|
||||
# Catppuccin Mocha Palette
|
||||
mocha = {
|
||||
base = "#1e1e2e";
|
||||
mantle = "#181825";
|
||||
crust = "#11111b";
|
||||
text = "#cdd6f4";
|
||||
subtext0 = "#a6adc8";
|
||||
overlay0 = "#6c7086";
|
||||
|
||||
mauve = "#cba6f7"; # Primary
|
||||
lavender = "#b4befe"; # Secondary
|
||||
pink = "#f5c2e7"; # Tertiary
|
||||
red = "#f38ba8"; # Error
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
imports = [ inputs.noctalia.homeModules.default ];
|
||||
|
||||
options.myModules.noctalia = {
|
||||
enable = lib.mkEnableOption "Noctalia Shell Configuration";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Correct Option Name: programs.noctalia-shell
|
||||
programs.noctalia-shell = {
|
||||
enable = true;
|
||||
|
||||
# Manual Catppuccin Mocha Theme mapping to Material Design roles
|
||||
colors = {
|
||||
mPrimary = mocha.mauve;
|
||||
mOnPrimary = mocha.base;
|
||||
|
||||
mSecondary = mocha.lavender;
|
||||
mOnSecondary = mocha.base;
|
||||
|
||||
mTertiary = mocha.pink;
|
||||
mOnTertiary = mocha.base;
|
||||
|
||||
mError = mocha.red;
|
||||
mOnError = mocha.base;
|
||||
|
||||
mSurface = mocha.base;
|
||||
mOnSurface = mocha.text;
|
||||
|
||||
mSurfaceVariant = mocha.mantle;
|
||||
mOnSurfaceVariant = mocha.subtext0;
|
||||
|
||||
mOutline = mocha.overlay0;
|
||||
mShadow = mocha.crust;
|
||||
};
|
||||
|
||||
settings = {
|
||||
colorSchemes = {
|
||||
darkMode = true;
|
||||
useWallpaperColors = false; # Force our manual colors
|
||||
};
|
||||
location = {
|
||||
weatherEnabled = true;
|
||||
name = "Berlin";
|
||||
};
|
||||
wallpaper = {
|
||||
enabled = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
99
modules/home/polling-rate.nix
Normal file
99
modules/home/polling-rate.nix
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.services.polling-rate-switcher;
|
||||
|
||||
switcherScript = pkgs.writeShellScriptBin "polling-rate-switcher" ''
|
||||
# Find Razer Viper V3 Pro sysfs path
|
||||
# Look for a device that supports poll_rate
|
||||
MOUSE_PATH=""
|
||||
|
||||
find_mouse() {
|
||||
for dev in /sys/bus/hid/drivers/razermouse/*; do
|
||||
if [ -f "$dev/device_type" ] && [ -f "$dev/poll_rate" ]; then
|
||||
TYPE=$(cat "$dev/device_type")
|
||||
# Check if it looks like a mouse "Razer Viper V3 Pro" usually has unique ID/Type
|
||||
# For now, we take the first Razer device that supports polling rate
|
||||
# Or specifically filter if we knew exact string.
|
||||
# The user said "Active window... polling rate of my razer viper v3 pro"
|
||||
# We'll assume it's the main device found.
|
||||
MOUSE_PATH="$dev"
|
||||
echo "Found Razer device at $MOUSE_PATH ($TYPE)"
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
find_mouse
|
||||
|
||||
if [ -z "$MOUSE_PATH" ]; then
|
||||
echo "No Razer mouse found with poll_rate capability."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Starting Polling Rate Switcher for $MOUSE_PATH"
|
||||
|
||||
CURRENT_MODE="unknown"
|
||||
|
||||
update_rate() {
|
||||
TARGET=$1
|
||||
# Read current to avoid redundant writes
|
||||
CURRENT=$(cat "$MOUSE_PATH/poll_rate")
|
||||
|
||||
if [ "$CURRENT" != "$TARGET" ]; then
|
||||
echo "Switching polling rate to $TARGET Hz"
|
||||
echo "$TARGET" > "$MOUSE_PATH/poll_rate"
|
||||
fi
|
||||
}
|
||||
|
||||
while true; do
|
||||
# Get active window info from Niri
|
||||
# Niri msg active-window returns JSON
|
||||
WINDOW_INFO=$(${pkgs.niri}/bin/niri msg -j focused-window 2>/dev/null)
|
||||
|
||||
if [ -n "$WINDOW_INFO" ]; then
|
||||
APP_ID=$(echo "$WINDOW_INFO" | ${pkgs.jq}/bin/jq -r '.app_id // ""' | tr '[:upper:]' '[:lower:]')
|
||||
TITLE=$(echo "$WINDOW_INFO" | ${pkgs.jq}/bin/jq -r '.title // ""' | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Check for Overwatch
|
||||
if [[ "$APP_ID" == *"overwatch"* ]] || [[ "$TITLE" == *"overwatch"* ]]; then
|
||||
update_rate 8000
|
||||
else
|
||||
update_rate 1000
|
||||
fi
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
done
|
||||
'';
|
||||
in
|
||||
{
|
||||
options.services.polling-rate-switcher = {
|
||||
enable = lib.mkEnableOption "Polling Rate Switcher Service";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.user.services.polling-rate-switcher = {
|
||||
Unit = {
|
||||
Description = "Auto-switch Razer Polling Rate for Overwatch";
|
||||
After = [ "graphical-session.target" ];
|
||||
PartOf = [ "graphical-session.target" ];
|
||||
};
|
||||
|
||||
Service = {
|
||||
ExecStart = "${switcherScript}/bin/polling-rate-switcher";
|
||||
Restart = "always";
|
||||
RestartSec = 5;
|
||||
};
|
||||
|
||||
Install = {
|
||||
WantedBy = [ "graphical-session.target" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
190
modules/home/proton-cachyos-updater.nix
Normal file
190
modules/home/proton-cachyos-updater.nix
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
# Proton CachyOS Auto-Updater Module (Home Manager)
|
||||
# Provides: Auto-update timer for Proton CachyOS from GitHub releases
|
||||
#
|
||||
# Usage:
|
||||
# myModules.protonCachyosUpdater = {
|
||||
# enable = true;
|
||||
# arch = "x86_64_v3";
|
||||
# schedule = "daily";
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.protonCachyosUpdater;
|
||||
|
||||
updateScript = pkgs.writeShellScript "update-proton-cachyos" ''
|
||||
set -euo pipefail
|
||||
|
||||
COMPAT_DIR="${cfg.compatToolsDir}"
|
||||
ARCH="${cfg.arch}"
|
||||
GITHUB_API="https://api.github.com/repos/CachyOS/proton-cachyos/releases/latest"
|
||||
VERSION_FILE="$COMPAT_DIR/.proton-cachyos-version"
|
||||
|
||||
# Ensure directory exists
|
||||
mkdir -p "$COMPAT_DIR"
|
||||
|
||||
echo "=== Checking for Proton CachyOS updates ==="
|
||||
|
||||
# Get latest release info from GitHub
|
||||
RELEASE_JSON=$(${pkgs.curl}/bin/curl -sL "$GITHUB_API")
|
||||
|
||||
if [ -z "$RELEASE_JSON" ] || echo "$RELEASE_JSON" | ${pkgs.jq}/bin/jq -e '.message' > /dev/null 2>&1; then
|
||||
echo "Failed to fetch release info from GitHub"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LATEST_TAG=$(echo "$RELEASE_JSON" | ${pkgs.jq}/bin/jq -r '.tag_name')
|
||||
echo "Latest version: $LATEST_TAG"
|
||||
|
||||
# Check current version
|
||||
CURRENT_VERSION=""
|
||||
if [ -f "$VERSION_FILE" ]; then
|
||||
CURRENT_VERSION=$(cat "$VERSION_FILE")
|
||||
fi
|
||||
echo "Current version: ''${CURRENT_VERSION:-none}"
|
||||
|
||||
if [ "$LATEST_TAG" = "$CURRENT_VERSION" ]; then
|
||||
echo "Already up to date!"
|
||||
# Still ensure symlink exists
|
||||
LATEST_DIR=$(ls -v "$COMPAT_DIR" | grep -E "^proton-cachyos" | grep -v "latest" | tail -1)
|
||||
if [ -n "$LATEST_DIR" ]; then
|
||||
ln -sfn "$COMPAT_DIR/$LATEST_DIR" "$COMPAT_DIR/proton-cachyos-latest"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Find download URL for our architecture
|
||||
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | ${pkgs.jq}/bin/jq -r ".assets[] | select(.name | contains(\"$ARCH\")) | .browser_download_url" | grep "\.tar\.xz$" | head -1)
|
||||
|
||||
if [ -z "$DOWNLOAD_URL" ]; then
|
||||
echo "No download found for architecture: $ARCH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FILENAME=$(basename "$DOWNLOAD_URL")
|
||||
echo "Downloading: $FILENAME"
|
||||
|
||||
# Download to temp directory
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
trap "rm -rf $TEMP_DIR" EXIT
|
||||
|
||||
${pkgs.curl}/bin/curl -L --progress-bar -o "$TEMP_DIR/$FILENAME" "$DOWNLOAD_URL"
|
||||
|
||||
echo "Extracting..."
|
||||
${pkgs.gnutar}/bin/tar -xf "$TEMP_DIR/$FILENAME" -C "$COMPAT_DIR"
|
||||
|
||||
# Find extracted directory name
|
||||
EXTRACTED_DIR=$(ls -v "$COMPAT_DIR" | grep -E "^proton-cachyos" | grep -v "latest" | tail -1)
|
||||
|
||||
if [ -z "$EXTRACTED_DIR" ]; then
|
||||
echo "Failed to find extracted directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Installed: $EXTRACTED_DIR"
|
||||
|
||||
# Create wrapper directory (remove old symlink/dir first)
|
||||
rm -rf "$COMPAT_DIR/proton-cachyos-latest"
|
||||
mkdir -p "$COMPAT_DIR/proton-cachyos-latest"
|
||||
|
||||
# Create wrapper compatibilitytool.vdf
|
||||
# This allows Steam to see "proton-cachyos-latest" as a distinct tool pointing to the real files
|
||||
cat > "$COMPAT_DIR/proton-cachyos-latest/compatibilitytool.vdf" <<EOF
|
||||
"compatibilitytools"
|
||||
{
|
||||
"compat_tools"
|
||||
{
|
||||
"proton-cachyos-latest"
|
||||
{
|
||||
"install_path" "$COMPAT_DIR/$EXTRACTED_DIR"
|
||||
"display_name" "Proton CachyOS (Latest)"
|
||||
"from_oslist" "windows"
|
||||
"to_oslist" "linux"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Updated wrapper: proton-cachyos-latest -> $EXTRACTED_DIR"
|
||||
|
||||
# Save version
|
||||
echo "$LATEST_TAG" > "$VERSION_FILE"
|
||||
|
||||
echo "=== Update complete ==="
|
||||
${pkgs.libnotify}/bin/notify-send "Proton CachyOS" "Updated to $LATEST_TAG" --icon=steam
|
||||
'';
|
||||
in
|
||||
{
|
||||
options.myModules.protonCachyosUpdater = {
|
||||
enable = lib.mkEnableOption "Proton CachyOS auto-updater";
|
||||
|
||||
arch = lib.mkOption {
|
||||
type = lib.types.enum [
|
||||
"x86_64"
|
||||
"x86_64_v2"
|
||||
"x86_64_v3"
|
||||
"x86_64_v4"
|
||||
];
|
||||
default = "x86_64_v3";
|
||||
description = "CPU architecture variant to download";
|
||||
};
|
||||
|
||||
schedule = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "daily";
|
||||
description = "systemd calendar expression for update schedule";
|
||||
};
|
||||
|
||||
randomDelay = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "1h";
|
||||
description = "Random delay before running update";
|
||||
};
|
||||
|
||||
compatToolsDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "$HOME/.local/share/Steam/compatibilitytools.d";
|
||||
description = "Steam compatibility tools directory";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.user.services.proton-cachyos-update = {
|
||||
Unit = {
|
||||
Description = "Update Proton CachyOS from GitHub releases";
|
||||
After = [ "network-online.target" ];
|
||||
Wants = [ "network-online.target" ];
|
||||
};
|
||||
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
ExecStart = updateScript;
|
||||
Environment = [
|
||||
"HOME=%h"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.user.timers.proton-cachyos-update = {
|
||||
Unit = {
|
||||
Description = "Proton CachyOS update timer";
|
||||
};
|
||||
|
||||
Timer = {
|
||||
OnCalendar = cfg.schedule;
|
||||
Persistent = true;
|
||||
RandomizedDelaySec = cfg.randomDelay;
|
||||
};
|
||||
|
||||
Install = {
|
||||
WantedBy = [ "timers.target" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
96
modules/home/qbittorrent-vpn.nix
Normal file
96
modules/home/qbittorrent-vpn.nix
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# qBittorrent VPN Module (Home Manager)
|
||||
# Provides: qBittorrent running through Gluetun VPN as user service
|
||||
#
|
||||
# Usage:
|
||||
# myModules.qbittorrentVpn = {
|
||||
# enable = true;
|
||||
# configDir = "/home/user/qbittorrent/config";
|
||||
# downloadsDir = "/home/user/qbittorrent/downloads";
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.qbittorrentVpn;
|
||||
in
|
||||
{
|
||||
options.myModules.qbittorrentVpn = {
|
||||
enable = lib.mkEnableOption "qBittorrent via VPN container";
|
||||
|
||||
image = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "lscr.io/linuxserver/qbittorrent:latest";
|
||||
description = "qBittorrent container image";
|
||||
};
|
||||
|
||||
configDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Path to qBittorrent config directory";
|
||||
};
|
||||
|
||||
downloadsDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Path to downloads directory";
|
||||
};
|
||||
|
||||
webPort = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8080;
|
||||
description = "WebUI port (inside container)";
|
||||
};
|
||||
|
||||
timezone = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "Europe/Berlin";
|
||||
description = "Container timezone";
|
||||
};
|
||||
|
||||
vpnContainer = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "gluetun";
|
||||
description = "Name of VPN container to route through";
|
||||
};
|
||||
|
||||
vpnService = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "gluetun.service";
|
||||
description = "Systemd service name of VPN container";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.user.services.qbittorrent = {
|
||||
Unit = {
|
||||
Description = "qBittorrent Container (Rootless)";
|
||||
After = [ cfg.vpnService ];
|
||||
Requires = [ cfg.vpnService ];
|
||||
};
|
||||
|
||||
Service = {
|
||||
Restart = "always";
|
||||
ExecStartPre = "-${pkgs.podman}/bin/podman stop qbittorrent";
|
||||
ExecStart = ''
|
||||
${pkgs.podman}/bin/podman run --rm --name qbittorrent \
|
||||
--network=container:${cfg.vpnContainer} \
|
||||
-e PUID=0 \
|
||||
-e PGID=0 \
|
||||
-e TZ=${cfg.timezone} \
|
||||
-e WEBUI_PORT=${toString cfg.webPort} \
|
||||
-v ${cfg.configDir}:/config \
|
||||
-v ${cfg.downloadsDir}:/downloads \
|
||||
${cfg.image}
|
||||
'';
|
||||
ExecStop = "${pkgs.podman}/bin/podman stop qbittorrent";
|
||||
};
|
||||
|
||||
Install = {
|
||||
WantedBy = [ "default.target" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
87
modules/home/sillytavern.nix
Normal file
87
modules/home/sillytavern.nix
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# SillyTavern Module (Home Manager)
|
||||
# Provides: SillyTavern as rootless container
|
||||
#
|
||||
# Usage:
|
||||
# myModules.sillytavern = {
|
||||
# enable = true;
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.sillytavern;
|
||||
in
|
||||
{
|
||||
options.myModules.sillytavern = {
|
||||
enable = lib.mkEnableOption "SillyTavern container";
|
||||
|
||||
image = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "ghcr.io/sillytavern/sillytavern:latest";
|
||||
description = "SillyTavern container image";
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8000;
|
||||
description = "Host port for SillyTavern";
|
||||
};
|
||||
|
||||
configDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/home/ashie/nixos/sillytavern/config";
|
||||
description = "Path to config directory";
|
||||
};
|
||||
|
||||
dataDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/home/ashie/nixos/sillytavern/data";
|
||||
description = "Path to data directory";
|
||||
};
|
||||
|
||||
pluginsDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/home/ashie/nixos/sillytavern/plugins";
|
||||
description = "Path to plugins directory";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.user.services.sillytavern = {
|
||||
Unit = {
|
||||
Description = "SillyTavern Container (Rootless)";
|
||||
After = [ "network-online.target" ];
|
||||
Wants = [ "network-online.target" ];
|
||||
};
|
||||
|
||||
Service = {
|
||||
Restart = "always";
|
||||
ExecStartPre = [
|
||||
"-${pkgs.podman}/bin/podman stop sillytavern"
|
||||
"${pkgs.podman}/bin/podman network create antigravity-net --ignore"
|
||||
];
|
||||
ExecStart = ''
|
||||
${pkgs.podman}/bin/podman run --rm --name sillytavern \
|
||||
--network=antigravity-net \
|
||||
--network-alias=sillytavern \
|
||||
--dns=8.8.8.8 \
|
||||
-v ${cfg.configDir}:/home/node/app/config \
|
||||
-v ${cfg.dataDir}:/home/node/app/data \
|
||||
-v ${cfg.pluginsDir}:/home/node/app/plugins \
|
||||
-p 127.0.0.1:${toString cfg.port}:8000 \
|
||||
${cfg.image}
|
||||
'';
|
||||
ExecStop = "${pkgs.podman}/bin/podman stop sillytavern";
|
||||
};
|
||||
|
||||
Install = {
|
||||
WantedBy = [ "default.target" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
36
modules/home/theme.nix
Normal file
36
modules/home/theme.nix
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
home.pointerCursor = {
|
||||
gtk.enable = true;
|
||||
x11.enable = true;
|
||||
package = pkgs.bibata-cursors;
|
||||
name = "Bibata-Modern-Ice";
|
||||
size = 24;
|
||||
};
|
||||
|
||||
gtk = {
|
||||
enable = true;
|
||||
iconTheme = {
|
||||
name = "Papirus-Dark";
|
||||
package = pkgs.papirus-icon-theme;
|
||||
};
|
||||
theme = {
|
||||
name = "Catppuccin-Mocha-Standard-Mauve-Dark";
|
||||
package = pkgs.catppuccin-gtk.override {
|
||||
accents = [ "mauve" ];
|
||||
size = "standard";
|
||||
tweaks = [ "rimless" "black" ];
|
||||
variant = "mocha";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
qt = {
|
||||
enable = true;
|
||||
platformTheme.name = "gtk";
|
||||
style.name = "gtk2";
|
||||
};
|
||||
}
|
||||
90
modules/home/unified-router.nix
Normal file
90
modules/home/unified-router.nix
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# Unified Router Module (Home Manager)
|
||||
# Provides: Unified API router as rootless container
|
||||
#
|
||||
# Usage:
|
||||
# myModules.unifiedRouter = {
|
||||
# enable = true;
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.unifiedRouter;
|
||||
in
|
||||
{
|
||||
options.myModules.unifiedRouter = {
|
||||
enable = lib.mkEnableOption "Unified API Router";
|
||||
|
||||
image = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "localhost/unified-router:latest";
|
||||
description = "Unified Router container image";
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 6767;
|
||||
description = "Host port for Unified Router";
|
||||
};
|
||||
environmentFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/run/secrets/rendered/api_key.env";
|
||||
description = "Path to environment file containing API_KEY";
|
||||
};
|
||||
antigravityPath = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/home/ashie/nixos/antigravity-src";
|
||||
description = "Path to antigravity-src directory";
|
||||
};
|
||||
|
||||
dataDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/home/ashie/.local/share/unified-router";
|
||||
description = "Path to persist container data (accounts.json, etc.)";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.user.services.unified-router = {
|
||||
Unit = {
|
||||
Description = "Unified API Router Container (Rootless)";
|
||||
After = [ "network-online.target" ];
|
||||
Wants = [ "network-online.target" ];
|
||||
};
|
||||
|
||||
Service = {
|
||||
Environment = "PATH=/run/wrappers/bin:/run/current-system/sw/bin";
|
||||
Restart = "always";
|
||||
RestartSec = "10s";
|
||||
ExecStartPre = [
|
||||
# Best effort cleanup, ignore errors
|
||||
"-${pkgs.podman}/bin/podman system migrate"
|
||||
"-${pkgs.podman}/bin/podman rm -f unified-router --ignore"
|
||||
"-${pkgs.podman}/bin/podman stop unified-router --ignore"
|
||||
# Network creation removed (host mode)
|
||||
"${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir}"
|
||||
];
|
||||
ExecStart = ''
|
||||
${pkgs.podman}/bin/podman run --replace --rm --name unified-router \
|
||||
--user 0 \
|
||||
--network=host \
|
||||
-e PORT=${toString cfg.port} \
|
||||
--env-file=${cfg.environmentFile} \
|
||||
-e LOG_LEVEL=debug \
|
||||
-v ${cfg.dataDir}:/app/data \
|
||||
${cfg.image}
|
||||
'';
|
||||
ExecStop = "${pkgs.podman}/bin/podman stop -t 10 unified-router";
|
||||
};
|
||||
|
||||
Install = {
|
||||
WantedBy = [ "default.target" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue