initial commit

This commit is contained in:
GammaKinematics 2026-03-30 13:10:42 +07:00
commit 90cff4f16a
59 changed files with 6855 additions and 0 deletions

34
.gitignore vendored Normal file
View file

@ -0,0 +1,34 @@
# Nix build artifacts
result
result-*
# Secrets (never commit these)
secrets/
*.secret
*.key
*.pem
# Editor backups
*.backup
*~
*.swp
# Local overrides
local.nix
# Claude stuff
.claude/
# Quickshell generated theme files (generated by Nix)
Hyprland/Quickshell/bar/Theme.qml
Hyprland/Quickshell/control-center/Theme.qml
# nixos-hardware fork (now on GitHub)
nixos-hardware-fork/
# Hyprland wiki
hyprland-wiki/
/st/
/dwm/

409
Hetzner/axiomania.nix Normal file
View file

@ -0,0 +1,409 @@
{ pkgs, ... }:
{
imports = [ ./common.nix ];
networking.hostName = "axiomania";
# ============================================================================
# Firewall
# ============================================================================
networking.firewall = {
allowedTCPPorts = [ 22 80 443 2222 ];
allowedUDPPorts = [ 51820 53 ]; # Wireguard, Adguard DNS
};
# ============================================================================
# Forgejo
# ============================================================================
services.forgejo = {
enable = true;
database.type = "sqlite3";
settings = {
DEFAULT.APP_NAME = "Axiomania Git";
server = {
DOMAIN = "git.axiomania.org";
ROOT_URL = "https://git.axiomania.org/";
HTTP_ADDR = "127.0.0.1";
HTTP_PORT = 3000;
SSH_DOMAIN = "git.axiomania.org";
SSH_PORT = 2222;
START_SSH_SERVER = true;
};
service = {
DISABLE_REGISTRATION = true;
DEFAULT_KEEP_EMAIL_PRIVATE = true;
};
repository = {
DEFAULT_PRIVATE = "private";
};
api = {
ENABLE_SWAGGER = false;
};
session = {
COOKIE_SECURE = true;
};
actions = {
ENABLED = true;
};
};
};
# ============================================================================
# Forgejo Actions Runner
# ============================================================================
# Requires a registration token from Forgejo.
# Generate one: ssh root@178.104.15.221, then:
# forgejo-runner register (follow prompts)
# Or via Forgejo web UI: Site Administration > Runners > Create Runner
# Then put the token in /var/lib/forgejo-runner/token
services.gitea-actions-runner.instances.default = {
enable = true;
name = "axiomania";
url = "https://git.axiomania.org";
tokenFile = "/var/lib/forgejo-runner/token";
labels = [ "native:host" ];
hostPackages = with pkgs; [
bash
coreutils
curl
gawk
git
gnused
nodejs
nix
];
};
# ============================================================================
# Vaultwarden (Bitwarden-compatible password manager)
# ============================================================================
services.vaultwarden = {
enable = true;
config = {
DOMAIN = "https://vault.axiomania.org";
SIGNUPS_ALLOWED = false;
ROCKET_ADDRESS = "127.0.0.1";
ROCKET_PORT = 8222;
};
};
# ============================================================================
# Navidrome (music streaming)
# ============================================================================
services.navidrome = {
enable = true;
settings = {
Address = "127.0.0.1";
Port = 4533;
MusicFolder = "/var/lib/navidrome/music";
};
};
# ============================================================================
# Syncthing (file sync)
# ============================================================================
services.syncthing = {
enable = true;
openDefaultPorts = true;
dataDir = "/var/lib/syncthing";
settings.gui.insecureSkipHostcheck = true;
};
# ============================================================================
# Attic (Nix binary cache)
# ============================================================================
# Requires a secret generated on the server:
# openssl genrsa -traditional 4096 | base64 -w0 > /var/lib/atticd/token-secret
# echo "ATTIC_SERVER_TOKEN_RS256_SECRET_BASE64=$(cat /var/lib/atticd/token-secret)" > /etc/atticd.env
services.atticd = {
enable = true;
environmentFile = "/etc/atticd.env";
settings = {
listen = "[::]:8080";
storage = {
type = "local";
path = "/var/lib/atticd/storage";
};
garbage-collection = {
default-retention-period = "3 months";
};
chunking = {
nar-size-threshold = 65536;
min-size = 16384;
avg-size = 65536;
max-size = 262144;
};
};
};
# ============================================================================
# Kavita (ebook library)
# ============================================================================
# Generate token on the server:
# openssl rand -hex 128 > /var/lib/kavita/token-key
services.kavita = {
enable = true;
settings.Port = 5000;
tokenKeyFile = "/var/lib/kavita/token-key";
};
# ============================================================================
# Audiobookshelf
# ============================================================================
services.audiobookshelf = {
enable = true;
port = 8234;
host = "127.0.0.1";
};
# ============================================================================
# Paperless-ngx (document management)
# ============================================================================
services.paperless = {
enable = true;
port = 28981;
address = "127.0.0.1";
domain = "docs.axiomania.org";
};
# ============================================================================
# Adguard Home (DNS ad blocker)
# ============================================================================
# Use as DNS server via Wireguard: set DNS = 10.100.0.1 in client config
services.adguardhome = {
enable = true;
port = 3300;
host = "127.0.0.1";
};
# ============================================================================
# SearXNG (private search engine)
# ============================================================================
# Generate secret on the server:
# echo "SEARX_SECRET=$(openssl rand -hex 32)" > /etc/searx/secrets.env
services.searx = {
enable = true;
redisCreateLocally = true;
settings = {
server = {
bind_address = "127.0.0.1";
port = 8888;
secret_key = "$SEARX_SECRET";
};
};
environmentFile = "/etc/searx/secrets.env";
};
# ============================================================================
# Homepage (services dashboard)
# ============================================================================
services.homepage-dashboard = {
enable = true;
listenPort = 8082;
allowedHosts = "home.axiomania.org";
};
# ============================================================================
# Matrix Conduit (encrypted messaging)
# ============================================================================
services.matrix-conduit = {
enable = true;
settings.global = {
server_name = "axiomania.org";
port = 6167;
address = "127.0.0.1";
allow_registration = false;
};
};
# ============================================================================
# Gokapi (file sharing) — disabled due to CVEs in stable
# ============================================================================
# services.gokapi = {
# enable = true;
# environment = {
# GOKAPI_PORT = 53842;
# };
# };
# ============================================================================
# Wireguard VPN
# ============================================================================
# Generate keys on the server:
# wg genkey | tee /etc/wireguard/private.key | wg pubkey > /etc/wireguard/public.key
networking.wireguard.interfaces.wg0 = {
ips = [ "10.100.0.1/24" ];
listenPort = 51820;
privateKeyFile = "/etc/wireguard/private.key";
peers = [
{
# Minisforum V3 SE (local NixOS machine)
publicKey = "rEsHqzJVnv9AoOIEVTrdEF3x4G6rWxXqHLXTzAx88gc=";
allowedIPs = [ "10.100.0.2/32" ];
}
{
# Pixel 5
publicKey = "jP+rYdc8qKdNY4l3PDFmGiOA31SnJRgcmQIVu6AJ9EM=";
allowedIPs = [ "10.100.0.3/32" ];
}
];
};
# NAT for Wireguard clients to access the internet
networking.nat = {
enable = true;
externalInterface = "ens3";
internalInterfaces = [ "wg0" ];
};
# ============================================================================
# Nginx reverse proxy + ACME
# ============================================================================
services.nginx = {
enable = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
# ── Public services ──
virtualHosts."git.axiomania.org" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:3000";
proxyWebsockets = true;
};
};
virtualHosts."matrix.axiomania.org" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:6167";
proxyWebsockets = true;
};
};
virtualHosts."cache.axiomania.org" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://[::1]:8080";
proxyWebsockets = true;
};
};
# ── VPN-only services (WireGuard clients only) ──
virtualHosts."vault.axiomania.org" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8222";
proxyWebsockets = true;
extraConfig = "allow 10.100.0.0/24; allow 127.0.0.1; deny all;";
};
};
virtualHosts."music.axiomania.org" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:4533";
proxyWebsockets = true;
extraConfig = "allow 10.100.0.0/24; allow 127.0.0.1; deny all;";
};
};
virtualHosts."sync.axiomania.org" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8384";
proxyWebsockets = true;
extraConfig = "allow 10.100.0.0/24; allow 127.0.0.1; deny all;";
};
};
virtualHosts."books.axiomania.org" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:5000";
proxyWebsockets = true;
extraConfig = "allow 10.100.0.0/24; allow 127.0.0.1; deny all;";
};
};
virtualHosts."audio.axiomania.org" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8234";
proxyWebsockets = true;
extraConfig = "allow 10.100.0.0/24; allow 127.0.0.1; deny all;";
};
};
virtualHosts."docs.axiomania.org" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:28981";
proxyWebsockets = true;
extraConfig = "allow 10.100.0.0/24; allow 127.0.0.1; deny all;";
};
};
virtualHosts."search.axiomania.org" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8888";
proxyWebsockets = true;
extraConfig = "allow 10.100.0.0/24; allow 127.0.0.1; deny all;";
};
};
virtualHosts."home.axiomania.org" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8082";
proxyWebsockets = true;
extraConfig = "allow 10.100.0.0/24; allow 127.0.0.1; deny all;";
};
};
# virtualHosts."share.axiomania.org" = {
# enableACME = true;
# forceSSL = true;
# locations."/" = {
# proxyPass = "http://127.0.0.1:53842";
# proxyWebsockets = true;
# };
# };
};
security.acme = {
acceptTerms = true;
defaults.email = "gamma.kinematics@gmail.com";
};
# ============================================================================
# Packages
# ============================================================================
environment.systemPackages = with pkgs; [
git
vim
htop
wireguard-tools
];
}

26
Hetzner/builder.nix Normal file
View file

@ -0,0 +1,26 @@
{ pkgs, ... }:
{
imports = [ ./common.nix ];
networking.hostName = "builder";
nix.settings = {
max-jobs = "auto";
substituters = [
"https://cache.nixos.org"
"https://cache.axiomania.org/main"
];
trusted-public-keys = [
"main:Uz5F0MbXItVx2XCmBbEAMmQ0T6+DZDgLaXWalh1k++o="
];
};
environment.systemPackages = with pkgs; [
git
tmux
attic-client
vim
htop
];
}

34
Hetzner/common.nix Normal file
View file

@ -0,0 +1,34 @@
{ modulesPath, ... }:
{
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
(modulesPath + "/installer/scan/not-detected.nix")
./disk-config.nix
];
system.stateVersion = "25.11";
time.timeZone = "Europe/Berlin";
nix.settings.experimental-features = [ "nix-command" "flakes" ];
boot.loader.grub = {
enable = true;
efiSupport = true;
efiInstallAsRemovable = true;
};
services.qemuGuest.enable = true;
users.users.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA7XcPUNMwpUqXMqhwTlW3YV2BUj0I6efk0LxqFcyYgA gamma.kinematics@gmail.com"
];
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "prohibit-password";
};
};
}

34
Hetzner/disk-config.nix Normal file
View file

@ -0,0 +1,34 @@
{ lib, ... }:
{
disko.devices.disk.main = {
device = lib.mkDefault "/dev/sda";
type = "disk";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # BIOS boot partition
};
esp = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
}

121
Hetzner/setup.nix Normal file
View file

@ -0,0 +1,121 @@
{ pkgs, ... }:
let
sshKeyPath = "/home/lebowski/.ssh/id_ed25519";
sshKeyName = "V3";
serverName = "nix-builder";
serverType = "ccx43";
location = "nbg1";
sshCmd = "ssh -o StrictHostKeyChecking=no -o BatchMode=yes -i ${sshKeyPath}";
# Spin up a builder from the NixOS snapshot
builder-up = pkgs.writeShellApplication {
name = "builder-up";
runtimeInputs = with pkgs; [ hcloud openssh ];
text = ''
SERVER_NAME="${serverName}"
SSH_KEY_PATH="${sshKeyPath}"
# ── Check if server already exists ──
if hcloud server describe "$SERVER_NAME" &>/dev/null; then
IP=$(hcloud server ip "$SERVER_NAME")
echo "Server '$SERVER_NAME' already running at $IP"
echo " builder-ssh"
exit 0
fi
# ── Find snapshot ──
SNAPSHOT_ID=$(hcloud image list --type snapshot --selector description="${snapshotDesc}" -o noheader -o columns=id 2>/dev/null | head -1)
if [ -z "$SNAPSHOT_ID" ]; then
# Fallback: search by description
SNAPSHOT_ID=$(hcloud image list --type snapshot -o noheader -o columns=id,description | grep "${snapshotDesc}" | awk '{print $1}' | head -1)
fi
if [ -z "$SNAPSHOT_ID" ]; then
echo "ERROR: No snapshot '${snapshotDesc}' found."
echo " Create one with: builder-snapshot"
exit 1
fi
# ── Create server from snapshot ──
echo "==> Creating ${serverType} in ${location} from snapshot $SNAPSHOT_ID..."
hcloud server create \
--name "$SERVER_NAME" \
--type "${serverType}" \
--image "$SNAPSHOT_ID" \
--location "${location}" \
--label role=nix-builder \
--ssh-key "${sshKeyName}"
IP=$(hcloud server ip "$SERVER_NAME")
# ── Wait for SSH ──
echo "==> Waiting for SSH..."
for i in $(seq 1 30); do
if ${sshCmd} "root@''${IP}" true 2>/dev/null; then
break
fi
if [ "$i" -eq 30 ]; then
echo "ERROR: SSH did not become available after 150s"
exit 1
fi
sleep 5
done
# ── Update known_hosts ──
ssh-keygen -R "$IP" 2>/dev/null || true
ssh-keyscan -H "$IP" >> ~/.ssh/known_hosts 2>/dev/null
echo ""
echo "=== Builder ready ==="
echo " builder-ssh"
'';
};
# SSH into the builder
builder-ssh = pkgs.writeShellApplication {
name = "builder-ssh";
runtimeInputs = with pkgs; [ hcloud openssh ];
text = ''
SERVER_NAME="${serverName}"
SSH_KEY_PATH="${sshKeyPath}"
if ! hcloud server describe "$SERVER_NAME" &>/dev/null; then
echo "No server '$SERVER_NAME' found. Run builder-up first."
exit 1
fi
IP=$(hcloud server ip "$SERVER_NAME")
ssh -i "$SSH_KEY_PATH" -t "root@$IP" "tmux new-session -A -s build"
'';
};
# Tear down the builder
builder-down = pkgs.writeShellApplication {
name = "builder-down";
runtimeInputs = with pkgs; [ hcloud openssh ];
text = ''
SERVER_NAME="${serverName}"
if hcloud server describe "$SERVER_NAME" &>/dev/null; then
IP=$(hcloud server ip "$SERVER_NAME")
echo "==> Deleting '$SERVER_NAME' ($IP)..."
hcloud server delete "$SERVER_NAME"
ssh-keygen -R "$IP" 2>/dev/null || true
echo " Done."
else
echo "No server '$SERVER_NAME' found."
fi
'';
};
in
{
environment.systemPackages = [
builder-up
builder-ssh
builder-down
pkgs.hcloud
];
}

41
Hyprsuck/home.nix Normal file
View file

@ -0,0 +1,41 @@
# Hyprsuck home-manager configuration
# Unstable extras and autostart
{ pkgs-unstable, ... }:
{
imports = [
./hyprland/hyprland.nix
./waybar.nix
];
# Screenshot annotation tool
programs.swappy = {
enable = true;
package = pkgs-unstable.swappy;
settings.Default = {
save_dir = "$HOME/Pictures/Screenshots";
save_filename_format = "%F_%T.png";
early_exit = true;
};
};
# Clipboard history manager
services.cliphist = {
enable = true;
package = pkgs-unstable.cliphist;
allowImages = true;
};
# Terminal emulator (Wayland-native)
programs.foot = {
enable = true;
package = pkgs-unstable.foot;
};
# Auto-start Hyprland on tty1
programs.bash.profileExtra = ''
if [ -z "$WAYLAND_DISPLAY" ] && [ "$XDG_VTNR" = 1 ]; then
exec Hyprland
fi
'';
}

View file

@ -0,0 +1,152 @@
# Hyprland keybindings configuration
{ ... }:
{
wayland.windowManager.hyprland.settings = {
# Variables
"$mod" = "SUPER";
"$terminal" = "foot";
"$browser" = "zen";
# Keybindings
bind = [
# Rofi
"$mod, Space, exec, rofi -show system:rofi-system"
"$mod ALT, Space, exec, rofi-websearch"
# Foot terminal - go to workspace 50, launch if not running
"$mod, A, exec, hyprctl dispatch workspace 50; hyprctl clients -j | grep -q foot || $terminal"
# Zed - go to workspace 60, launch if not running
"$mod, Z, exec, hyprctl dispatch workspace 60; hyprctl clients -j | grep -q dev.zed.Zed || zeditor"
# Zen Browser - workspace 70 (DP-3), launch if no browser on that workspace
"$mod, B, exec, hyprctl dispatch workspace 70; hyprctl clients -j | jq -e '.[] | select(.class == \"zen-twilight\" and .workspace.id == 70)' > /dev/null || hyprctl dispatch exec [workspace 70 silent] -- $browser"
# Zen Browser - workspace 71 (eDP-1), launch new window if no browser on that workspace
"$mod ALT, B, exec, hyprctl dispatch workspace 71; hyprctl clients -j | jq -e '.[] | select(.class == \"zen-twilight\" and .workspace.id == 71)' > /dev/null || hyprctl dispatch exec [workspace 71 silent] -- $browser --new-window"
# Nautilus - go to workspace 80, launch if not running
"$mod, N, exec, hyprctl dispatch workspace 80; hyprctl clients -j | grep -q org.gnome.Nautilus || nautilus"
# Haruna - go to workspace 90
"$mod, M, workspace, 90"
"$mod, Escape, exec, hyprlock"
# Switch to KiCad workspaces (both monitors)
"$mod, K, workspace, 101"
"$mod, K, workspace, 102"
# Launch KiCad project selector
"$mod CTRL, K, exec, kicad-projects"
# Launch library editors
"$mod, L, exec, kicad-lib-launch"
# Show project manager
"$mod SHIFT, K, togglespecialworkspace, kicad-pm"
# Swap SCH/PCB positions
"$mod ALT, K, exec, kicad-swap"
# Cycle through project instances
"$mod, bracketright, exec, kicad-cycle f"
"$mod, bracketleft, exec, kicad-cycle b"
# FreeCAD - go to workspace 110, launch if not running
"$mod, F, exec, hyprctl dispatch workspace 110; hyprctl clients -j | grep -q org.freecad.FreeCAD || env QT_QPA_PLATFORM=xcb FreeCAD --single-instance"
# Window management
"$mod, Q, killactive"
"$mod, W, fullscreen"
"$mod, R, togglefloating"
"$mod, P, pseudo"
"$mod, E, togglesplit"
# Focus movement
"$mod, left, movefocus, l"
"$mod, right, movefocus, r"
"$mod, up, movefocus, u"
"$mod, down, movefocus, d"
# Workspace switching
"$mod, 1, workspace, 1"
"$mod, 2, workspace, 2"
"$mod, 3, workspace, 3"
"$mod, 4, workspace, 4"
"$mod, 5, workspace, 5"
"$mod, 6, workspace, 6"
"$mod, 7, workspace, 7"
"$mod, 8, workspace, 8"
"$mod, 9, workspace, 9"
"$mod, 0, workspace, 10"
# Move window to workspace
"$mod SHIFT, 1, movetoworkspace, 1"
"$mod SHIFT, 2, movetoworkspace, 2"
"$mod SHIFT, 3, movetoworkspace, 3"
"$mod SHIFT, 4, movetoworkspace, 4"
"$mod SHIFT, 5, movetoworkspace, 5"
"$mod SHIFT, 6, movetoworkspace, 6"
"$mod SHIFT, 7, movetoworkspace, 7"
"$mod SHIFT, 8, movetoworkspace, 8"
"$mod SHIFT, 9, movetoworkspace, 9"
"$mod SHIFT, 0, movetoworkspace, 10"
# Special workspace (scratchpad)
"$mod, S, togglespecialworkspace, magic"
"$mod SHIFT, S, movetoworkspace, special:magic"
# Exit Hyprland
"$mod SHIFT, E, exit"
# Scroll through workspaces on current monitor (vertical model)
"$mod, mouse_down, workspace, m-1"
"$mod, mouse_up, workspace, m+1"
# Keyboard workspace navigation (up/down)
"$mod CTRL, up, workspace, m-1"
"$mod CTRL, down, workspace, m+1"
"$mod CTRL SHIFT, up, movetoworkspace, m-1"
"$mod CTRL SHIFT, down, movetoworkspace, m+1"
# Clipboard history (SUPER+SHIFT+V)
"$mod SHIFT, V, exec, cliphist list | rofi -dmenu -p 'Clipboard' | cliphist decode | wl-copy"
# Screenshot bindings (saves to ~/Pictures/Screenshots/)
", Print, exec, mkdir -p ~/Pictures/Screenshots && grimblast --notify copysave area ~/Pictures/Screenshots/$(date +%F_%T).png"
"SHIFT, Print, exec, mkdir -p ~/Pictures/Screenshots && grimblast --notify copysave output ~/Pictures/Screenshots/$(date +%F_%T).png"
"CTRL, Print, exec, grimblast --notify save area - | swappy -f -"
# System menu (rofi)
"$mod, X, exec, rofi -show system -modes 'system:rofi-system'"
# On-screen keyboard toggle (for tablet mode)
# "$mod, K, exec, pkill wvkbd-mobintl || wvkbd-mobintl"
# Refresh waybar (for dock/undock)
"$mod CTRL, Space, exec, pkill waybar; waybar-start"
];
# Mouse bindings
bindm = [
"$mod, mouse:272, movewindow"
"$mod, mouse:273, resizewindow"
];
# Repeat bindings (hold key for continuous action)
binde = [
"$mod ALT, left, resizeactive, -20 0"
"$mod ALT, right, resizeactive, 20 0"
"$mod ALT, up, resizeactive, 0 -20"
"$mod ALT, down, resizeactive, 0 20"
];
# Media/hardware key bindings (bindl = works even when locked)
bindl = [
", XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
", XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"
];
# Volume keys with repeat (binde = repeat when held)
bindel = [
", XF86AudioRaiseVolume, exec, wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%+"
", XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
];
};
}

View file

@ -0,0 +1,41 @@
# Hypridle - Idle daemon configuration
{ pkgs, ... }:
{
services.hypridle = {
enable = true;
package = pkgs.hypridle;
settings = {
general = {
lock_cmd = "pidof hyprlock || hyprlock";
before_sleep_cmd = "loginctl lock-session";
after_sleep_cmd = "hyprctl dispatch dpms on";
};
listener = [
# Dim screen after 2.5 minutes
{
timeout = 150;
on-timeout = "brightnessctl -s set 10";
on-resume = "brightnessctl -r";
}
# Lock after 5 minutes
{
timeout = 300;
on-timeout = "loginctl lock-session";
}
# Screen off after 5.5 minutes
{
timeout = 330;
on-timeout = "hyprctl dispatch dpms off";
on-resume = "hyprctl dispatch dpms on";
}
# Suspend after 30 minutes
{
timeout = 1800;
on-timeout = "systemctl suspend";
}
];
};
};
}

View file

@ -0,0 +1,54 @@
# Hyprland ecosystem configuration (stable packages)
# Core hyprland WM and hypr* tools only
{ pkgs, ... }:
{
imports = [
./bindings.nix
./settings.nix
./monitors.nix
./rules.nix
./hyprlock.nix
./hypridle.nix
./hyprpaper.nix
];
# ============================================================================
# Hyprland Window Manager (home-manager config)
# ============================================================================
# Package/portal set at NixOS level in hyprsuck.nix
wayland.windowManager.hyprland = {
enable = true;
systemd.enable = true;
};
# ============================================================================
# Hyprland ecosystem packages (stable)
# ============================================================================
home.packages = with pkgs; [
# Tablet mode
iio-hyprland
# Monitor hotplug
hyprland-monitor-attached
];
wayland.windowManager.hyprland.settings = {
exec-once = [
"iio-hyprland"
"hyprland-monitor-attached"
];
};
# Polkit authentication agent
services.hyprpolkitagent = {
enable = true;
package = pkgs.hyprpolkitagent;
};
# Blue light filter
services.hyprsunset = {
enable = true;
package = pkgs.hyprsunset;
};
}

View file

@ -0,0 +1,39 @@
# Hyprlock - Screen locker configuration
{ pkgs, ... }:
{
# Disable Stylix's hyprlock theming (we use screenshot blur)
stylix.targets.hyprlock.enable = false;
programs.hyprlock = {
enable = true;
package = pkgs.hyprlock;
settings = {
general = {
hide_cursor = true;
grace = 5; # seconds before lock takes effect
};
background = [
{
path = "screenshot";
blur_passes = 3;
blur_size = 8;
}
];
input-field = [
{
size = "200, 50";
position = "0, -80";
monitor = "";
dots_center = true;
fade_on_empty = false;
outline_thickness = 2;
placeholder_text = "Password...";
shadow_passes = 2;
}
];
};
};
}

View file

@ -0,0 +1,22 @@
# Hyprpaper - Wallpaper manager configuration
{ config, pkgs, ... }:
let
flakeDir = "${config.home.homeDirectory}/NixOS";
in
{
services.hyprpaper = {
enable = true;
package = pkgs.hyprpaper;
settings = {
ipc = "on";
splash = false;
# Change the wallpaper filename here
preload = [ "${flakeDir}/Wallpapers/Shift.png" ];
wallpaper = [ ", ${flakeDir}/Wallpapers/Shift.png" ];
};
};
}

View file

@ -0,0 +1,15 @@
# Hyprland monitor configuration
{ ... }:
{
wayland.windowManager.hyprland.settings = {
# Monitor configuration
# eDP-1: Laptop display in portrait mode (rotated 270deg)
# DP-3: External monitor in landscape, positioned to the right
monitor = [
"eDP-1, 1920x1200@60, 0x0, 1.25, transform, 3"
"DP-3, 1920x1080@100, 960x-250, 1"
", preferred, auto, 1" # Fallback for any other monitor
];
};
}

View file

@ -0,0 +1,59 @@
# Hyprland window rules and workspace configuration
{ ... }:
{
wayland.windowManager.hyprland.settings = {
# Window rules (old syntax for Hyprland 0.52.1)
windowrule = [
"float, class:^(pavucontrol)$"
"float, class:^(org.keepassxc.KeePassXC)$"
"float, title:^(Picture-in-Picture)$"
"pin, title:^(Picture-in-Picture)$"
# Foot terminal → workspace 50 (DP-3)
"workspace 50 silent, class:^(foot)$"
# Zed → workspace 60 (DP-3)
"workspace 60 silent, class:^(dev.zed.Zed)$"
# FreeCAD → workspace 110 (DP-3)
"workspace 110 silent, class:^(org.freecad.FreeCAD)$"
# Nautilus → workspace 80 (eDP-1)
"workspace 80 silent, class:^(org.gnome.Nautilus)$"
# Haruna → workspace 104, tabbed/grouped
"workspace 90 silent, class:^(org.kde.haruna)$"
"group set, class:^(org.kde.haruna)$"
# Schematic/Symbol Editor → workspace 102 (eDP-1), maximized, grouped
"workspace 102 silent, class:^(KiCad|kicad)$, title:.*Schematic Editor.*"
"maximize, class:^(KiCad|kicad)$, title:.*Schematic Editor.*"
"group set, class:^(KiCad|kicad)$, title:.*Schematic Editor.*"
"workspace 102 silent, class:^(KiCad|kicad)$, title:.*Symbol Editor.*"
"maximize, class:^(KiCad|kicad)$, title:.*Symbol Editor.*"
"group set, class:^(KiCad|kicad)$, title:.*Symbol Editor.*"
# PCB/Footprint Editor → workspace 101 (DP-3), maximized, grouped
"workspace 101 silent, class:^(KiCad|kicad)$, title:.*PCB Editor.*"
"maximize, class:^(KiCad|kicad)$, title:.*PCB Editor.*"
"group set, class:^(KiCad|kicad)$, title:.*PCB Editor.*"
"workspace 101 silent, class:^(KiCad|kicad)$, title:.*Footprint Editor.*"
"maximize, class:^(KiCad|kicad)$, title:.*Footprint Editor.*"
"group set, class:^(KiCad|kicad)$, title:.*Footprint Editor.*"
];
# Workspace rules
workspace = [
"50, monitor:eDP-1, defaultName:terminal"
"60, monitor:DP-3, defaultName:code"
"70, monitor:DP-3, defaultName:browser"
"71, monitor:eDP-1, defaultName:browser"
"80, monitor:eDP-1, defaultName:files"
"90, monitor:DP-3, defaultName:video"
"101, monitor:DP-3, defaultName:kicad_prim"
"102, monitor:eDP-1, defaultName:kicad_sec"
"110, monitor:DP-3, defaultName:freecad"
];
};
}

View file

@ -0,0 +1,81 @@
# Hyprland general settings
# Input, appearance, layout, gestures, misc
{ ... }:
{
wayland.windowManager.hyprland.settings = {
# Input configuration
input = {
kb_layout = "us";
numlock_by_default = true;
follow_mouse = 1;
sensitivity = 0;
touchpad = {
natural_scroll = true;
};
};
# General settings
general = {
gaps_in = 5;
gaps_out = 5;
border_size = 2;
layout = "dwindle";
};
# Decoration
decoration = {
rounding = 10;
# blur = {
# enabled = true;
# size = 3;
# passes = 1;
# };
# shadow = {
# enabled = true;
# range = 4;
# render_power = 3;
# };
};
# Animations
animations = {
enabled = true;
# bezier = "myBezier, 0.05, 0.9, 0.1, 1.05";
animation = [
# "windows, 1, 7, myBezier"
# "windowsOut, 1, 7, default, popin 80%"
# "border, 1, 10, default"
# "fade, 1, 7, default"
"workspaces, 1, 4, default, slidevert"
];
};
# Layout settings
dwindle = {
pseudotile = true;
preserve_split = true;
};
# Gestures (Hyprland 0.51+ new syntax)
gestures = {
workspace_swipe_distance = 500;
workspace_swipe_invert = false;
workspace_swipe_create_new = false;
};
# New gesture bindings (replaces workspace_swipe)
# Vertical 3-finger swipe for workspace switching
gesture = [
"3, vertical, workspace"
];
# Misc
misc = {
force_default_wallpaper = 0;
disable_hyprland_logo = true;
};
};
}

23
Hyprsuck/hyprsuck.nix Normal file
View file

@ -0,0 +1,23 @@
# Hyprsuck configuration entry point
# Wayland compositor setup with Hyprland
{ pkgs, ... }:
{
# ============================================================================
# Hyprland - NixOS level config
# ============================================================================
programs.hyprland = {
enable = true;
package = pkgs.hyprland;
portalPackage = pkgs.xdg-desktop-portal-hyprland;
xwayland.enable = true;
};
environment.systemPackages = with pkgs; [
# Screenshot tool for Hyprland
grimblast
# Tablet mode
wvkbd
];
}

219
Hyprsuck/waybar.nix Normal file
View file

@ -0,0 +1,219 @@
# Waybar - Dynamic config based on connected monitors
# Docked (DP-3): external gets workspaces+clock, internal gets workspaces
# Undocked: internal gets workspaces+clock
{ pkgs-unstable, pkgs, ... }:
let
workspaceIcons = {
"terminal" = "";
"code" = "";
"browser" = "󰖟";
"files" = "";
"video" = "";
"kicad_prim" = "";
"kicad_sec" = "";
"freecad" = "󰻬";
};
sharedStyle = ''
* {
font-family: "JetBrainsMono Nerd Font Mono", monospace;
}
window#waybar {
background: transparent;
}
.modules-left,
.modules-center,
.modules-right {
background: alpha(@theme_bg_color, 0.8);
border-radius: 8px;
padding: 2px;
margin: 0px;
}
window#waybar.right .modules-center {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
margin-right: 0;
}
window#waybar.left .modules-center {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
margin-left: 0;
}
#workspaces {
padding: 0px;
}
.modules-center #workspaces button,
.modules-left #workspaces button,
.modules-right #workspaces button {
padding: 1px 2px;
min-width: 14px;
min-height: 12px;
background: transparent;
border: none;
border-bottom: none;
box-shadow: none;
text-shadow: none;
border-radius: 0;
}
.modules-center #workspaces button label,
.modules-left #workspaces button label,
.modules-right #workspaces button label {
font-size: 13px;
}
.modules-center #workspaces button.active,
.modules-left #workspaces button.active,
.modules-right #workspaces button.active {
color: @base0D;
border-bottom: none;
box-shadow: none;
}
.modules-center #workspaces button.active label,
.modules-left #workspaces button.active label,
.modules-right #workspaces button.active label {
font-size: 15px;
}
#clock {
padding: 0px;
font-weight: bold;
}
'';
# JSON config for docked mode (DP-3 primary)
dockedConfig = builtins.toJSON [
# DP-3: workspaces on right
{
name = "dp3-workspaces";
output = "DP-3";
layer = "top";
exclusive = false;
passthrough = true;
position = "right";
margin-right = 0;
modules-center = [ "hyprland/workspaces" ];
"hyprland/workspaces" = {
format = "{icon}";
format-icons = workspaceIcons;
show-special = false;
persistent-workspaces = { };
};
}
# DP-3: clock on left
{
name = "dp3-clock";
output = "DP-3";
layer = "top";
exclusive = false;
passthrough = true;
position = "left";
margin-left = 0;
modules-center = [ "clock" ];
clock = {
format = "{:%H\n%M}";
tooltip-format = "{:%A, %B %d, %Y}";
};
}
# eDP-1: workspaces on left (secondary)
{
name = "edp1-workspaces";
output = "eDP-1";
layer = "top";
exclusive = false;
passthrough = true;
position = "left";
margin-left = 0;
modules-center = [ "hyprland/workspaces" ];
"hyprland/workspaces" = {
format = "{icon}";
format-icons = workspaceIcons;
show-special = false;
persistent-workspaces = { };
};
}
];
# JSON config for undocked mode (eDP-1 only)
undockedConfig = builtins.toJSON [
# eDP-1: workspaces on right
{
name = "edp1-workspaces";
output = "eDP-1";
layer = "top";
exclusive = false;
passthrough = true;
position = "right";
margin-right = 0;
modules-center = [ "hyprland/workspaces" ];
"hyprland/workspaces" = {
format = "{icon}";
format-icons = workspaceIcons;
show-special = false;
persistent-workspaces = { };
};
}
# eDP-1: clock on left
{
name = "edp1-clock";
output = "eDP-1";
layer = "top";
exclusive = false;
passthrough = true;
position = "left";
margin-left = 0;
modules-center = [ "clock" ];
clock = {
format = "{:%H\n%M}";
tooltip-format = "{:%A, %B %d, %Y}";
};
}
];
# Simple startup script - check for DP-3 and load appropriate config
startupScript = ''
sleep 1
CONFIG_DIR="$HOME/.config/waybar"
if hyprctl monitors -j | grep -q '"name": "DP-3"'; then
waybar -c "$CONFIG_DIR/config-docked.jsonc" &
else
waybar -c "$CONFIG_DIR/config-undocked.jsonc" &
fi
'';
in
{
# Waybar - disable systemd, we manage startup ourselves
programs.waybar = {
enable = true;
package = pkgs-unstable.waybar;
systemd.enable = false;
style = sharedStyle;
};
# Write config files (style via programs.waybar.style to avoid conflict)
home.file.".config/waybar/config-docked.jsonc".text = dockedConfig;
home.file.".config/waybar/config-undocked.jsonc".text = undockedConfig;
# Waybar startup script
home.packages = [
(pkgs.writeShellScriptBin "waybar-start" startupScript)
];
# Start waybar
wayland.windowManager.hyprland.settings.exec-once = [
"waybar-start"
];
}

View file

@ -0,0 +1,44 @@
#!/usr/bin/env bash
# Cycle through KiCad windows/projects
# Usage: kicad-cycle [f|b] (forward/backward, default: f)
# Supports both Hyprland (Wayland) and dwm (X11)
DIRECTION="${1:-f}"
# ==============================================================================
# Environment Detection
# ==============================================================================
if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then
WM="hyprland"
else
WM="dwm"
fi
# ==============================================================================
# Main
# ==============================================================================
if [[ "$WM" == "hyprland" ]]; then
# Hyprland: Cycle through groups on both workspaces
hyprctl dispatch workspace 101
hyprctl dispatch changegroupactive "$DIRECTION"
hyprctl dispatch workspace 102
hyprctl dispatch changegroupactive "$DIRECTION"
else
# Ensure we're on kicad tag on both monitors first
kicad-show
# Determine focus direction
FOCUS_CMD="focus-next"
[[ "$DIRECTION" == "b" ]] && FOCUS_CMD="focus-prev"
if [[ $(autorandr --detected) != "mobile" ]]; then
# Multi-monitor: cycle on both monitors
echo "mon-prim" > /tmp/dwm.fifo
echo "$FOCUS_CMD" > /tmp/dwm.fifo
echo "mon-sec" > /tmp/dwm.fifo
echo "$FOCUS_CMD" > /tmp/dwm.fifo
else
# Single monitor: just cycle windows
echo "$FOCUS_CMD" > /tmp/dwm.fifo
fi
fi

View file

@ -0,0 +1,103 @@
#!/usr/bin/env bash
# Launch KiCad with managed window layout
# Usage: kicad-launch <project.kicad_pro>
# Supports both Hyprland (Wayland) and dwm (X11)
set -euo pipefail
# ==============================================================================
# Environment Detection
# ==============================================================================
if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then
WM="hyprland"
else
WM="dwm"
fi
# ==============================================================================
# Main
# ==============================================================================
PROJECT="${1:-}"
if [[ -z "$PROJECT" ]]; then
echo "Usage: kicad-launch <project.kicad_pro>"
exit 1
fi
PROJECT_NAME=$(basename "$PROJECT" .kicad_pro)
if [[ "$WM" == "hyprland" ]]; then
# Hyprland: Use special workspace for PM
hyprctl dispatch workspace 102
hyprctl dispatch togglespecialworkspace kicad-pm
hyprctl dispatch exec "[workspace special:kicad-pm silent]" -- env GDK_BACKEND=x11 kicad "$PROJECT"
# Wait for project manager window
PM_ADDR=""
for i in {1..50}; do
PM_ADDR=$(hyprctl clients -j | jq -r --arg name "$PROJECT_NAME" \
'.[] | select(.class == "KiCad" and (.title | test($name))) | .address' 2>/dev/null | head -1)
[[ -n "$PM_ADDR" ]] && break
sleep 0.1
done
if [[ -z "$PM_ADDR" ]]; then
hyprctl dispatch togglespecialworkspace kicad-pm
echo "Failed to find KiCad project manager window"
exit 1
fi
# Focus PM and open PCB editor
hyprctl dispatch focuswindow "address:$PM_ADDR"
sleep 0.25
xdotool key ctrl+p
sleep 0.25
# Hide special workspace
hyprctl dispatch togglespecialworkspace kicad-pm
# Focus PCB editor and open schematic
sleep 2
hyprctl dispatch workspace 101
sleep 0.25
xdotool key ctrl+e
sleep 0.25
# Switch to KiCad workspaces
hyprctl dispatch workspace 101
hyprctl dispatch workspace 102
else
# dwm: Launch KiCad, window rules handle placement
# Tag 16 = kicad-pm, Tag 17 = kicad (sch/pcb), Tag 18 = kicad-aux (mobile)
# Detect profile
PROFILE=$(autorandr --detected 2>/dev/null || echo "mobile")
# Switch to PM tag and launch
[[ "$PROFILE" != "mobile" ]] && echo "mon-sec" > /tmp/dwm.fifo
echo "kicad-pm" > /tmp/dwm.fifo
kicad "$PROJECT" &
# Wait for PM, open PCB editor
sleep 1
sleep 0.25
xdotool key ctrl+p
# Wait for PCB Editor
sleep 2
[[ "$PROFILE" != "mobile" ]] && echo "mon-prim" > /tmp/dwm.fifo
echo "kicad" > /tmp/dwm.fifo
if [[ "$PROFILE" == "mobile" ]]; then
# Mobile: retag PCB to tag 18 (kicad-aux)
# PCB landed on tag 17, focus it, retag to 18, follow
xdotool search --name "PCB Editor" windowactivate --sync
sleep 0.25
echo "tag 18" > /tmp/dwm.fifo
echo "kicad-aux" > /tmp/dwm.fifo
fi
# Focus PCB Editor, open Schematic
sleep 0.25
xdotool key ctrl+e
fi

View file

@ -0,0 +1,45 @@
#!/usr/bin/env bash
# Open Symbol + Footprint editors from running schematic/PCB editors
# Supports both Hyprland (Wayland) and dwm (X11)
set -euo pipefail
# ==============================================================================
# Environment Detection
# ==============================================================================
if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then
WM="hyprland"
else
WM="dwm"
PROFILE=$(autorandr --detected 2>/dev/null || echo "mobile")
fi
# ==============================================================================
# Main
# ==============================================================================
if [[ "$WM" == "hyprland" ]]; then
# Hyprland: Focus workspaces and send keys
hyprctl dispatch workspace 101
sleep 0.25
xdotool key ctrl+u # Open Footprint Editor from PCB
sleep 2
hyprctl dispatch workspace 102
sleep 0.25
xdotool key ctrl+i # Open Symbol Editor from Schematic
else
# Focus PCB Editor, open Footprint Editor
[[ "$PROFILE" != "mobile" ]] && echo "mon-prim" > /tmp/dwm.fifo
echo "kicad" > /tmp/dwm.fifo
sleep 0.25
xdotool key ctrl+u
sleep 0.5
# Focus right monitor, Schematic Editor, open Symbol Editor
[[ "$PROFILE" != "mobile" ]] && echo "mon-sec" > /tmp/dwm.fifo
echo "kicad" > /tmp/dwm.fifo
sleep 0.25
xdotool key ctrl+i
fi

View file

@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Rofi KiCad project launcher
# Two-step: select folder, then select project
# Works with both Hyprland and dwm
# Define project folders: "Display Name|/path/to/folder"
PROJECT_FOLDERS=(
"CNC|/data/3D-Printer/Electronics/PCBs"
"Keyboard|/data/Keyboard/PCB"
"eGPU|/data/eGPU"
)
# Step 1: Select project folder
FOLDER_MENU=""
for entry in "${PROJECT_FOLDERS[@]}"; do
FOLDER_MENU+="${entry%%|*}\n"
done
SELECTED_FOLDER=$(echo -e "$FOLDER_MENU" | rofi -dmenu -p "KiCad Folder" -i)
[ -z "$SELECTED_FOLDER" ] && exit 0
# Get the path for selected folder
FOLDER_PATH=""
for entry in "${PROJECT_FOLDERS[@]}"; do
name="${entry%%|*}"
path="${entry#*|}"
if [ "$name" = "$SELECTED_FOLDER" ]; then
FOLDER_PATH="$path"
break
fi
done
[ -z "$FOLDER_PATH" ] && exit 1
# Step 2: Find .kicad_pro files in selected folder
# Sort by path length (shallowest first) to handle submodule duplicates
mapfile -t PROJECTS < <(find "$FOLDER_PATH" -name "*.kicad_pro" -type f 2>/dev/null | awk '{print length, $0}' | sort -n | cut -d' ' -f2-)
if [ ${#PROJECTS[@]} -eq 0 ]; then
rofi -e "No KiCad projects found in $FOLDER_PATH"
exit 1
fi
# Build menu with duplicate filtering (keep shallowest path for each project name)
declare -A SEEN_PROJECTS
MENU=""
for project in "${PROJECTS[@]}"; do
name=$(basename "$project" .kicad_pro)
# Skip if we've already seen this project name (deeper path = submodule)
if [ -z "${SEEN_PROJECTS[$name]}" ]; then
SEEN_PROJECTS[$name]="$project"
MENU+="$name\n"
fi
done
# Show rofi menu
SELECTED=$(echo -e "$MENU" | rofi -dmenu -p "Project" -i)
[ -z "$SELECTED" ] && exit 0
# Launch selected project
PROJECT_PATH="${SEEN_PROJECTS[$SELECTED]}"
if [ -n "$PROJECT_PATH" ]; then
kicad-launch "$PROJECT_PATH"
fi

View file

@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Show KiCad tag on both monitors
# Supports both Hyprland (Wayland) and dwm (X11)
# ==============================================================================
# Environment Detection
# ==============================================================================
if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then
WM="hyprland"
else
WM="dwm"
fi
# ==============================================================================
# Main
# ==============================================================================
if [[ "$WM" == "hyprland" ]]; then
hyprctl dispatch workspace 101
hyprctl dispatch workspace 102
else
if [[ $(autorandr --detected) != "mobile" ]]; then
# Multi-monitor: set kicad tag on both monitors
echo "mon-prim" > /tmp/dwm.fifo
echo "kicad" > /tmp/dwm.fifo
echo "mon-sec" > /tmp/dwm.fifo
echo "kicad" > /tmp/dwm.fifo
else
# Single monitor: just switch to kicad tag
echo "kicad" > /tmp/dwm.fifo
fi
fi

View file

@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Swap KiCad workspaces between monitors
# Supports both Hyprland (Wayland) and dwm (X11)
# ==============================================================================
# Environment Detection
# ==============================================================================
if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then
WM="hyprland"
else
WM="dwm"
fi
# ==============================================================================
# Main
# ==============================================================================
if [[ "$WM" == "hyprland" ]]; then
hyprctl dispatch swapactiveworkspaces DP-3 eDP-1
else
# Ensure we're on kicad tag on both monitors first
kicad-show
echo "mon-swap" > /tmp/dwm.fifo
fi

20
KiCad/kicad.nix Normal file
View file

@ -0,0 +1,20 @@
# KiCad configuration and scripts
# Works with both Hyprland (Wayland) and dwm (X11)
{ pkgs-stable, ... }:
let
scriptsDir = ./Scripts;
in
{
home.packages = with pkgs-stable; [
kicad
# Custom scripts
(writeShellScriptBin "kicad-launch" (builtins.readFile "${scriptsDir}/kicad-launch.sh"))
(writeShellScriptBin "kicad-projects" (builtins.readFile "${scriptsDir}/kicad-projects.sh"))
(writeShellScriptBin "kicad-show" (builtins.readFile "${scriptsDir}/kicad-show.sh"))
(writeShellScriptBin "kicad-swap" (builtins.readFile "${scriptsDir}/kicad-swap.sh"))
(writeShellScriptBin "kicad-cycle" (builtins.readFile "${scriptsDir}/kicad-cycle.sh"))
(writeShellScriptBin "kicad-lib-launch" (builtins.readFile "${scriptsDir}/kicad-lib-launch.sh"))
];
}

86
Rofi/Scripts/favorites.sh Normal file
View file

@ -0,0 +1,86 @@
#!/usr/bin/env bash
# Rofi Favorites - Reads Zen/Firefox bookmarks directly from SQLite
# Supports folder navigation with ROFI_DATA state tracking
# Works with both Hyprland and dwm (no WM-specific code)
PLACES_DB="$HOME/.zen/default/places.sqlite"
TEMP_DB="/tmp/rofi-bookmarks.sqlite"
STATE="${ROFI_DATA:-3}" # 3 = Bookmarks Toolbar folder ID
SELECTION="$1"
# Copy database to avoid lock issues (browser keeps it locked)
cp "$PLACES_DB" "$TEMP_DB" 2>/dev/null
show_folder() {
local folder_id="$1"
echo -en "\0data\x1f${folder_id}\n"
echo -en "\0keep-selection\x1ftrue\n"
# Show back option if not at root
[[ "$folder_id" != "3" ]] && echo "󰁍 .."
# Query bookmarks in this folder
# type 1 = bookmark, type 2 = folder
sqlite3 -separator '|' "$TEMP_DB" "
SELECT
b.id,
b.type,
COALESCE(b.title, ''),
COALESCE(p.url, '')
FROM moz_bookmarks b
LEFT JOIN moz_places p ON b.fk = p.id
WHERE b.parent = $folder_id
AND b.title IS NOT NULL
AND b.title != ''
ORDER BY b.position;
" | while IFS='|' read -r id type title url; do
if [[ "$type" == "2" ]]; then
# Folder
echo -en "󰉋 ${title}\0info\x1ffolder:${id}\n"
elif [[ "$type" == "1" && -n "$url" ]]; then
# Bookmark
echo -en "󰈙 ${title}\0info\x1f${url}\n"
fi
done
}
handle_selection() {
local info="$ROFI_INFO"
# Back/parent
if [[ "$SELECTION" == "󰁍 .." ]]; then
parent=$(sqlite3 "$TEMP_DB" "SELECT parent FROM moz_bookmarks WHERE id = $STATE;")
[[ -z "$parent" || "$parent" == "0" || "$parent" == "1" ]] && parent=3
show_folder "$parent"
return
fi
# Folder - navigate into it
if [[ "$info" == folder:* ]]; then
local folder_id="${info#folder:}"
show_folder "$folder_id"
return
fi
# URL - open it
if [[ -n "$info" && "$info" != folder:* ]]; then
coproc (xdg-open "$info" &)
exit 0
fi
show_folder "$STATE"
}
# Check if database exists
if [[ ! -f "$PLACES_DB" ]]; then
echo "󰀨 No Zen browser profile found"
echo "󰈙 Expected: ~/.zen/default/places.sqlite"
exit 0
fi
# Main entry
if [[ -z "$SELECTION" ]]; then
show_folder "$STATE"
else
handle_selection
fi

46
Rofi/Scripts/max30.sh Normal file
View file

@ -0,0 +1,46 @@
#!/usr/bin/env bash
# Rofi Max30 mode - list and open video files with custom names
# Supports both Hyprland (Wayland) and dwm (X11)
# ==============================================================================
# Environment Detection
# ==============================================================================
if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then
WM="hyprland"
else
WM="dwm"
fi
VIDEOS=(
"Max Out Cardio|/data-bis/Insanity MAX 30/Max Out Cardio.mkv"
"Max Out Power|/data-bis/Insanity MAX 30/Max Out Power.mkv"
"Max Out Sweat|/data-bis/Insanity MAX 30/Max Out Sweat.mkv"
"Max Out Strength|/data-bis/Insanity MAX 30/Max Out Strength.mkv"
"Friday Fight #2|/data-bis/Insanity MAX 30/Friday Fight Round 2.mkv"
)
# If no argument, list video names
if [ -z "$1" ]; then
for entry in "${VIDEOS[@]}"; do
echo "${entry%%|*}"
done
exit 0
fi
# If argument provided, find and open the matching video
SELECTED="$1"
for entry in "${VIDEOS[@]}"; do
name="${entry%%|*}"
path="${entry#*|}"
if [ "$name" = "$SELECTED" ]; then
setsid xdg-open "$path" >/dev/null 2>&1 &
if [[ "$WM" == "hyprland" ]]; then
hyprctl dispatch workspace 90 >/dev/null 2>&1
else
# Focus primary monitor first if not on mobile (single monitor) profile
[[ $(autorandr --detected) != "mobile" ]] && echo "mon-prim" > /tmp/dwm.fifo
echo "video" > /tmp/dwm.fifo
fi
exit 0
fi
done

317
Rofi/Scripts/system.sh Normal file
View file

@ -0,0 +1,317 @@
#!/usr/bin/env bash
# Rofi System Menu - as rofi mode with inline submenus
# Uses ROFI_DATA for state tracking between calls
# Supports both Hyprland (Wayland) and dwm (X11)
STATE="${ROFI_DATA:-main}"
SELECTION="$1"
# ==============================================================================
# Environment Detection
# ==============================================================================
if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then
WM="hyprland"
else
WM="dwm"
fi
# ==============================================================================
# Helper Functions
# ==============================================================================
get_volume() { wpctl get-volume @DEFAULT_AUDIO_SINK@ 2>/dev/null | awk '{print int($2*100)}'; }
get_mic_volume() { wpctl get-volume @DEFAULT_AUDIO_SOURCE@ 2>/dev/null | awk '{print int($2*100)}'; }
is_muted() { wpctl get-volume @DEFAULT_AUDIO_SINK@ 2>/dev/null | grep -q MUTED && echo "yes" || echo "no"; }
is_mic_muted() { wpctl get-volume @DEFAULT_AUDIO_SOURCE@ 2>/dev/null | grep -q MUTED && echo "yes" || echo "no"; }
get_brightness_internal() { brightnessctl -m 2>/dev/null | cut -d',' -f4 | tr -d '%'; }
get_brightness_external() {
if [[ "$WM" == "hyprland" ]]; then
local val=$(busctl --user get-property rs.wl-gammarelay /outputs/DP_3 rs.wl.gammarelay Brightness 2>/dev/null | awk '{print $2}')
echo "${val:-1}" | awk '{print int($1*100)}'
else
# X11: Use xrandr gamma (approximate)
local gamma=$(xrandr --verbose | grep -A5 "DP-3" | grep "Brightness" | awk '{print $2}' 2>/dev/null)
echo "${gamma:-1}" | awk '{print int($1*100)}'
fi
}
get_power_profile() { powerprofilesctl get 2>/dev/null || echo "balanced"; }
get_default_sink_id() {
wpctl inspect @DEFAULT_AUDIO_SINK@ 2>/dev/null | head -1 | awk '{print $2}' | tr -d ','
}
get_default_source_id() {
wpctl inspect @DEFAULT_AUDIO_SOURCE@ 2>/dev/null | head -1 | awk '{print $2}' | tr -d ','
}
get_sinks() {
local default_id=$(get_default_sink_id)
pw-cli list-objects Node 2>/dev/null | awk '
/^[[:space:]]*id [0-9]+/ { id = $2; gsub(",", "", id) }
/node.description = / { gsub(/.*node.description = "|"$/, ""); desc = $0 }
/media.class = "Audio\/Sink"/ { print id "|" desc }
' | while IFS='|' read -r id desc; do
if [[ "$id" == "$default_id" ]]; then
echo "${desc}|${id}"
else
echo "${desc}|${id}"
fi
done
}
get_sources() {
local default_id=$(get_default_source_id)
pw-cli list-objects Node 2>/dev/null | awk '
/^[[:space:]]*id [0-9]+/ { id = $2; gsub(",", "", id) }
/node.description = / { gsub(/.*node.description = "|"$/, ""); desc = $0 }
/media.class = "Audio\/Source"/ { print id "|" desc }
' | while IFS='|' read -r id desc; do
if [[ "$id" == "$default_id" ]]; then
echo "${desc}|${id}"
else
echo "${desc}|${id}"
fi
done
}
# ==============================================================================
# Menu Display Functions
# ==============================================================================
show_main() {
echo -en "\0data\x1fmain\n"
echo -en "\0keep-selection\x1ftrue\n"
echo "󰕾 Sound"
echo "󰃟 Brightness"
echo "󰤨 WiFi"
echo "󰂯 Bluetooth"
echo "󱐋 Power Profile"
echo "󰐥 Power"
}
show_sound() {
echo -en "\0data\x1fsound\n"
echo -en "\0keep-selection\x1ftrue\n"
local vol=$(get_volume)
local mic=$(get_mic_volume)
local muted=$(is_muted)
local mic_muted=$(is_mic_muted)
local vol_icon="󰕾"; [[ "$muted" == "yes" ]] && vol_icon="󰝟"
local mic_icon="󰍬"; [[ "$mic_muted" == "yes" ]] && mic_icon="󰍭"
echo "$vol_icon Volume: ${vol}%"
echo "󰝝 Volume +5%"
echo "󰝞 Volume -5%"
echo "󰓃 Output Device"
echo "$mic_icon Mic: ${mic}%"
echo "󰍮 Mic +5%"
echo "󰍯 Mic -5%"
echo "󰍬 Input Device"
echo "󰁍 Back"
}
show_output() {
echo -en "\0data\x1foutput\n"
echo -en "\0keep-selection\x1ftrue\n"
get_sinks | while IFS='|' read -r name id; do
echo -en "󰓃 ${name}\0info\x1f${id}\n"
done
echo "󰁍 Back"
}
show_input() {
echo -en "\0data\x1finput\n"
echo -en "\0keep-selection\x1ftrue\n"
get_sources | while IFS='|' read -r name id; do
echo -en "󰍬 ${name}\0info\x1f${id}\n"
done
echo "󰁍 Back"
}
show_brightness() {
echo -en "\0data\x1fbrightness\n"
echo -en "\0keep-selection\x1ftrue\n"
local internal=$(get_brightness_internal)
local external=$(get_brightness_external)
echo "󰛩 Internal: ${internal}%"
echo "󰹐 Internal +5%"
echo "󰹏 Internal -5%"
echo "󰍹 External: ${external}%"
echo "󰹐 External +5%"
echo "󰹏 External -5%"
echo "󰁍 Back"
}
show_power_profile() {
echo -en "\0data\x1fprofile\n"
echo -en "\0keep-selection\x1ftrue\n"
local current=$(get_power_profile)
local perf="" bal="" saver=""
[[ "$current" == "performance" ]] && perf="✓ "
[[ "$current" == "balanced" ]] && bal="✓ "
[[ "$current" == "power-saver" ]] && saver="✓ "
echo "${perf}󱐌 Performance"
echo "${bal}󰗑 Balanced"
echo "${saver}󰌪 Power Saver"
echo "󰢻 Edit Tuning"
echo "󰁍 Back"
}
show_power() {
echo -en "\0data\x1fpower\n"
echo -en "\0keep-selection\x1ftrue\n"
echo "󰐥 Shutdown"
echo "󰜉 Reboot"
echo "󰍃 Logout"
echo "󰌾 Lock"
echo "󰁍 Back"
}
# ==============================================================================
# Action Handlers
# ==============================================================================
handle_main() {
case "$SELECTION" in
*"Sound"*) show_sound ;;
*"Brightness"*) show_brightness ;;
*"WiFi"*) coproc (rofi-network-manager &); exit 0 ;;
*"Bluetooth"*) coproc (rofi-bluetooth &); exit 0 ;;
*"Power Profile"*) show_power_profile ;;
*"Power"*) show_power ;;
*) show_main ;;
esac
}
handle_sound() {
case "$SELECTION" in
*"Volume:"*) wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle; show_sound ;;
*"Volume +5%"*) wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+ -l 1.0; show_sound ;;
*"Volume -5%"*) wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-; show_sound ;;
*"Output Device"*) show_output ;;
*"Mic:"*) wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle; show_sound ;;
*"Mic +5%"*) wpctl set-volume @DEFAULT_AUDIO_SOURCE@ 5%+ -l 1.0; show_sound ;;
*"Mic -5%"*) wpctl set-volume @DEFAULT_AUDIO_SOURCE@ 5%-; show_sound ;;
*"Input Device"*) show_input ;;
*"Back"*) show_main ;;
*) show_sound ;;
esac
}
handle_output() {
case "$SELECTION" in
*"Back"*) show_sound ;;
*)
[[ -n "$ROFI_INFO" ]] && wpctl set-default "$ROFI_INFO"
show_output
;;
esac
}
handle_input() {
case "$SELECTION" in
*"Back"*) show_sound ;;
*)
[[ -n "$ROFI_INFO" ]] && wpctl set-default "$ROFI_INFO"
show_input
;;
esac
}
handle_brightness() {
case "$SELECTION" in
*"Internal +5%"*) brightnessctl -q set +5%; show_brightness ;;
*"Internal -5%"*) brightnessctl -q set 5%-; show_brightness ;;
*"External +5%"*)
if [[ "$WM" == "hyprland" ]]; then
current=$(get_brightness_external)
new=$((current + 5)); [[ $new -gt 100 ]] && new=100
val=$(echo "scale=2; $new/100" | bc)
busctl --user set-property rs.wl-gammarelay /outputs/DP_3 rs.wl.gammarelay Brightness d "$val" 2>/dev/null
else
current=$(get_brightness_external)
new=$((current + 5)); [[ $new -gt 100 ]] && new=100
val=$(echo "scale=2; $new/100" | bc)
xrandr --output DP-3 --brightness "$val" 2>/dev/null
fi
show_brightness ;;
*"External -5%"*)
if [[ "$WM" == "hyprland" ]]; then
current=$(get_brightness_external)
new=$((current - 5)); [[ $new -lt 5 ]] && new=5
val=$(echo "scale=2; $new/100" | bc)
busctl --user set-property rs.wl-gammarelay /outputs/DP_3 rs.wl.gammarelay Brightness d "$val" 2>/dev/null
else
current=$(get_brightness_external)
new=$((current - 5)); [[ $new -lt 5 ]] && new=5
val=$(echo "scale=2; $new/100" | bc)
xrandr --output DP-3 --brightness "$val" 2>/dev/null
fi
show_brightness ;;
*"Back"*) show_main ;;
*) show_brightness ;;
esac
}
handle_power_profile() {
case "$SELECTION" in
# systemd path watcher auto-applies power-tuning on profile change
*"Performance"*) powerprofilesctl set performance; show_power_profile ;;
*"Balanced"*) powerprofilesctl set balanced; show_power_profile ;;
*"Power Saver"*) powerprofilesctl set power-saver; show_power_profile ;;
*"Edit Tuning"*) coproc (zeditor "$HOME/NixOS/ryzenadj.nix" &); exit 0 ;;
*"Back"*) show_main ;;
*) show_power_profile ;;
esac
}
handle_power() {
case "$SELECTION" in
*"Shutdown"*) systemctl poweroff ;;
*"Reboot"*) systemctl reboot ;;
*"Logout"*)
if [[ "$WM" == "hyprland" ]]; then
hyprctl dispatch exit
else
pkill -x dwm
fi
;;
*"Lock"*)
if [[ "$WM" == "hyprland" ]]; then
hyprlock & exit 0
else
slock & exit 0
fi
;;
*"Back"*) show_main ;;
*) show_power ;;
esac
}
# ==============================================================================
# Main Entry Point
# ==============================================================================
if [[ -z "$SELECTION" ]]; then
case "$STATE" in
sound) show_sound ;;
output) show_output ;;
input) show_input ;;
brightness) show_brightness ;;
profile) show_power_profile ;;
power) show_power ;;
*) show_main ;;
esac
else
case "$STATE" in
sound) handle_sound ;;
output) handle_output ;;
input) handle_input ;;
brightness) handle_brightness ;;
profile) handle_power_profile ;;
power) handle_power ;;
*) handle_main ;;
esac
fi

86
Rofi/Scripts/websearch.sh Normal file
View file

@ -0,0 +1,86 @@
#!/usr/bin/env bash
# Rofi web search - with DuckDuckGo result preview
# Supports both Hyprland (Wayland) and dwm (X11)
# ==============================================================================
# Environment Detection
# ==============================================================================
if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then
WM="hyprland"
else
WM="dwm"
fi
switch_to_browser() {
if [[ "$WM" == "hyprland" ]]; then
hyprctl dispatch workspace 70
else
# Focus primary monitor first if not on mobile (single monitor) profile
[[ $(autorandr --detected) != "mobile" ]] && echo "mon-prim" > /tmp/dwm.fifo
echo "browser" > /tmp/dwm.fifo
fi
}
# Step 1: Query input (no list)
QUERY=$(echo "" | rofi -dmenu -p "Search : " -l 0)
[ -z "$QUERY" ] && exit 0
# Check if input looks like a URL - open directly
if echo "$QUERY" | grep -qE '\.(com|org|net|io|dev|co|me|gov|edu|app|xyz|info)(/|$)'; then
# Add https:// if no protocol specified
[[ "$QUERY" =~ ^https?:// ]] || QUERY="https://$QUERY"
switch_to_browser
xdg-open "$QUERY"
exit 0
fi
# Step 2: Engine selection (DuckDuckGo first)
ENGINE=$(printf " DuckDuckGo\n Google\n MyNixOS\n Nixpkgs\n NerdFonts" | rofi -dmenu -p "Engine : " -no-custom -l 5)
[ -z "$ENGINE" ] && exit 0
# Remove icon prefix
ENGINE="${ENGINE#* }"
ENCODED=$(echo "$QUERY" | sed 's/ /%20/g; s/&/%26/g; s/?/%3F/g; s/=/%3D/g')
case "$ENGINE" in
"DuckDuckGo")
# Fetch top 5 results using ddgr
RESULTS=$(ddgr --json -n 5 "$QUERY" 2>/dev/null | jq -r '.[] | "\(.title)\t\(.url)"')
if [ -z "$RESULTS" ]; then
# Fallback to direct search if no results
switch_to_browser
xdg-open "https://duckduckgo.com/?q=$ENCODED"
exit 0
fi
# Show results in rofi (title only)
TITLES=$(echo "$RESULTS" | cut -f1)
SELECTED=$(echo "$TITLES" | rofi -dmenu -p "Results : " -l 5)
[ -z "$SELECTED" ] && exit 0
# Get URL for selected title
URL=$(echo "$RESULTS" | grep "^$SELECTED " | cut -f2)
if [ -n "$URL" ]; then
switch_to_browser
xdg-open "$URL"
fi
;;
"Google")
switch_to_browser
xdg-open "https://www.google.com/search?q=$ENCODED"
;;
"MyNixOS")
switch_to_browser
xdg-open "https://mynixos.com/search?q=$ENCODED"
;;
"Nixpkgs")
switch_to_browser
xdg-open "https://search.nixos.org/packages?query=$ENCODED"
;;
"NerdFonts")
switch_to_browser
xdg-open "https://www.nerdfonts.com/cheat-sheet?q=$ENCODED"
;;
esac

162
Rofi/rofi.nix Normal file
View file

@ -0,0 +1,162 @@
# Rofi configuration and scripts
# Works with both Hyprland (Wayland) and dwm (X11)
{
pkgs-stable,
config,
lib,
...
}:
let
scriptsDir = ./Scripts;
# Detect if we're in Wayland or X11 based on session type
# This is evaluated at build time, so we need runtime detection in scripts
isWayland = config.wayland.windowManager.hyprland.enable or false;
# Terminal varies by environment
terminal = if isWayland then "foot" else "st";
in
{
# Wayland-specific packages (only when using Hyprland)
home.packages =
with pkgs-stable;
[
rofi-bluetooth
rofi-network-manager
ddgr
brightnessctl
bc
sqlite
# Custom scripts (environment-aware)
(writeShellScriptBin "rofi-websearch" (builtins.readFile "${scriptsDir}/websearch.sh"))
(writeShellScriptBin "rofi-max30" (builtins.readFile "${scriptsDir}/max30.sh"))
(writeShellScriptBin "rofi-system" (builtins.readFile "${scriptsDir}/system.sh"))
(writeShellScriptBin "rofi-favorites" (builtins.readFile "${scriptsDir}/favorites.sh"))
]
++ (
if isWayland then
[
wl-gammarelay-rs
]
else
[
]
);
# Start wl-gammarelay-rs on Hyprland
wayland.windowManager.hyprland.settings = lib.mkIf isWayland {
exec-once = [ "wl-gammarelay-rs" ];
};
programs.rofi = {
enable = true;
package = pkgs-stable.rofi;
terminal = terminal;
plugins = with pkgs-stable; [
rofi-calc
rofi-emoji
];
modes = [
"system:rofi-system"
"favorites:rofi-favorites"
"drun"
"calc"
"emoji"
"max30:rofi-max30"
];
extraConfig = {
show-icons = true;
display-system = "";
display-favorites = "";
display-drun = "";
display-calc = "";
display-emoji = "";
display-max30 = "";
drun-display-format = "{name}";
scroll-method = 0;
disable-history = false;
sidebar-mode = false;
sort = true;
sorting-method = "fzf";
matching = "fuzzy";
kb-primary-paste = "Control+V,Shift+Insert";
kb-secondary-paste = "Control+v,Insert";
kb-secondary-copy = "Control+c";
kb-clear-line = "Control+w";
kb-move-front = "Control+a";
kb-move-end = "Control+e";
kb-move-word-back = "Alt+b,Control+Left";
kb-move-word-forward = "Alt+f,Control+Right";
kb-move-char-back = "Control+b";
kb-move-char-forward = "Control+f";
kb-remove-word-back = "Control+Alt+h,Control+BackSpace";
kb-remove-word-forward = "Control+Alt+d";
kb-remove-char-forward = "Delete,Control+d";
kb-remove-char-back = "BackSpace,Shift+BackSpace,Control+h";
kb-remove-to-eol = "";
kb-remove-to-sol = "Control+u";
kb-accept-entry = "Return,KP_Enter";
kb-accept-custom = "Control+Return";
kb-accept-custom-alt = "Control+Shift+Return";
kb-accept-alt = "Shift+Return";
kb-delete-entry = "Shift+Delete";
kb-mode-next = "Right";
kb-mode-previous = "Left";
kb-mode-complete = "Control+l";
kb-row-left = "Control+Page_Up";
kb-row-right = "Control+Page_Down";
kb-row-up = "Up";
kb-row-down = "Down";
kb-row-tab = "";
kb-element-next = "Tab";
kb-element-prev = "ISO_Left_Tab";
kb-page-prev = "Page_Up";
kb-page-next = "Page_Down";
kb-row-first = "Home,KP_Home";
kb-row-last = "End,KP_End";
kb-row-select = "Control+space";
kb-screenshot = "Alt+S";
kb-ellipsize = "Alt+period";
kb-toggle-case-sensitivity = "grave,dead_grave";
kb-toggle-sort = "Alt+grave";
kb-cancel = "Escape";
};
theme =
let
mkLiteral = value: {
_type = "literal";
value = value;
};
in
{
window = {
width = mkLiteral "300px";
location = mkLiteral "center";
# border-radius = mkLiteral "12px";
border = mkLiteral "2px solid";
border-color = mkLiteral "@border-color";
};
listview = {
lines = 8;
fixed-height = false;
};
element = {
padding = mkLiteral "8px";
# border-radius = mkLiteral "6px";
};
# "element selected" = {
# border-radius = mkLiteral "6px";
# };
inputbar = {
padding = mkLiteral "8px";
};
};
};
}

92
Suckless/dwm/api.nix Normal file
View file

@ -0,0 +1,92 @@
# dwm fifo API - control dwm via named pipe
# Usage: echo "command" > /tmp/dwm.fifo
{ lib }:
{
config = ''
/* dwmfifo - control dwm via named pipe */
static const char *dwmfifo = "/tmp/dwm.fifo";
static Command commands[] = {
/*
* TAG NAVIGATION - Semantic names matching workflow
* */
{ "view", view, .parse = parsetag }, /* Generic: view 5 */
{ "view-all", view, {.ui = ~0} }, /* All tags */
{ "view-prev", viewprev, {0} }, /* Previous tag */
{ "view-next", viewnext, {0} }, /* Next tag */
/* Semantic tag shortcuts (matching tags.nix) */
{ "terminal", view, {.ui = 1 << 9} }, /* Tag 10: */
{ "files", view, {.ui = 1 << 10} }, /* Tag 11: */
{ "video", view, {.ui = 1 << 11} }, /* Tag 12: */
{ "browser", view, {.ui = 1 << 12} }, /* Tag 13: 󰖟 */
{ "code", view, {.ui = 1 << 13} }, /* Tag 14: */
{ "freecad", view, {.ui = 1 << 14} }, /* Tag 15: 󰻬 */
{ "kicad-pm", view, {.ui = 1 << 15} }, /* Tag 16: (project manager) */
{ "kicad", view, {.ui = 1 << 16} }, /* Tag 17: (sch/pcb) */
{ "kicad-aux", view, {.ui = 1 << 17} }, /* Tag 18: (pcb mobile) */
/*
* MONITOR CONTROL - Primary (left) / Secondary (right)
* */
{ "mon-prim", focusnthmon, {.i = 1} }, /* Focus primary monitor */
{ "mon-sec", focusnthmon, {.i = 0} }, /* Focus secondary monitor */
{ "mon-send-prim", tagnthmon, {.i = 1} }, /* Send window to primary */
{ "mon-send-sec", tagnthmon, {.i = 0} }, /* Send window to secondary */
{ "mon-swap", swapmon, {0} }, /* Swap monitor contents */
/*
* WINDOW MANAGEMENT
* */
{ "focus-next", focusstack, {.i = +1} }, /* Next window in stack */
{ "focus-prev", focusstack, {.i = -1} }, /* Previous window */
{ "focus", focusstack, .parse = parseplusminus },/* Generic: focus +2 */
{ "kill", killclient, {0} }, /* Close window */
{ "zoom", zoom, {0} }, /* Promote to master */
{ "float", togglefloating, {0} }, /* Toggle floating */
/*
* LAYOUT
* */
{ "layout-tile", setlayout, {.v = &layouts[0]} },
{ "layout-float", setlayout, {.v = &layouts[1]} },
{ "layout-mono", setlayout, {.v = &layouts[2]} },
{ "layout-toggle", setlayout, {0} }, /* Cycle layouts */
/*
* MASTER AREA
* */
{ "master-inc", incnmaster, {.i = +1} },
{ "master-dec", incnmaster, {.i = -1} },
{ "mfact", setmfact, .parse = parseplusminus },/* mfact +0.05 */
/*
* TAG MANAGEMENT
* */
{ "toggleview", toggleview, .parse = parsetag },
{ "tag", tag, .parse = parsetag },
{ "toggletag", toggletag, .parse = parsetag },
{ "tagmon", tagmon, .parse = parseplusminus },
/*
* ADVANCED (window targeting)
* */
{ "viewwin", viewwin, .parse = parsexid }, /* Focus by X window ID */
{ "viewname", viewname, .parse = parsestr }, /* Focus by name */
/*
* SPAWNS
* */
{ "spawn-term", spawn, {.v = termcmd} },
{ "spawn-browser", spawn, {.v = browsercmd} },
{ "spawn-rofi", spawn, {.v = roficmd} },
{ "spawn-files", spawn, {.v = filescmd} },
{ "spawn-editor", spawn, {.v = editorcmd} },
/*
* SESSION
* */
{ "quit", quit, {0} },
};
'';
}

View file

@ -0,0 +1,39 @@
# dwm appearance settings (colors, fonts, borders, gaps)
# Integrates with Stylix for consistent theming
{ config, lib }:
let
colors = config.lib.stylix.colors;
fonts = config.stylix.fonts;
in
{
config = ''
/* See LICENSE file for copyright and license details. */
/* appearance */
static const unsigned int borderpx = 2; /* border pixel of windows */
static const unsigned int gappx = 5; /* gaps between windows */
static const unsigned int snap = 32; /* snap pixel */
static const int showbar = 0; /* 0 means no bar (holdbar shows on key hold) */
static const int topbar = 1; /* 0 means bottom bar */
/* Display modes of the tab bar: never shown, always shown, shown only in */
/* monocle mode in the presence of several windows. */
/* Modes after showtab_nmodes are disabled. */
enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always};
static const int showtab = showtab_auto; /* Default tab bar show mode */
static const int toptab = False; /* False means bottom tab bar */
static const char *fonts[] = { "${fonts.monospace.name}:size=${toString fonts.sizes.terminal}" };
static const char dmenufont[] = "${fonts.monospace.name}:size=${toString fonts.sizes.terminal}";
static const char col_gray1[] = "#${colors.base00}";
static const char col_gray2[] = "#${colors.base01}";
static const char col_gray3[] = "#${colors.base04}";
static const char col_gray4[] = "#${colors.base05}";
static const char col_cyan[] = "#${colors.base0D}";
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
};
'';
}

File diff suppressed because it is too large Load diff

100
Suckless/dwm/dwm.nix Normal file
View file

@ -0,0 +1,100 @@
# dwm window manager configuration
#
# =============================================================================
# Patch Development Notes
# =============================================================================
#
# Base: dwm 6.6 (matches nixpkgs)
# Clone: ~/NixOS/dwm
#
# Clean & Build Commands:
# cd ~/NixOS/dwm
# git reset --hard 6.6
# rm -f config.h drw.o dwm dwm.o util.o
# nix-shell -p xorg.libX11 xorg.libXft xorg.libXinerama xorg.libXcursor pkg-config gnumake gcc
# make clean && make
#
# Generate patch after all patches applied:
# git diff 6.6 > ~/NixOS/Suckless/dwm/dwm-lebowski.patch
#
# =============================================================================
# Patches (apply in order):
# =============================================================================
#
# 1. pertag - Per-tag layouts (applied separately, then tab)
# 2. tab - Tabbed monocle bar
# 3. fullgaps - Simple gaps between windows
# 4. holdbar-modkey - Show bar while holding mod key
# 5. hide_vacant_tags - Only show occupied tags
# 6. bardwmlogo - DWM logo in bar
# 7. cursorwarp - Cursor follows focus
# 8. alwayscenter - Float windows spawn centered
# 9. swapmonitors - Swap tagsets between monitors
# 10. adjacenttag - Navigate tags with arrows (skipvacant)
# 11. accessnthmonitor - Focus/send to specific monitor by number
# 12. dwmfifo - Control dwm via named pipe
# 13. xcursor - Use system Xcursor theme (Stylix)
# 14. actualfullscreen
#
# Custom additions:
# - def_layouts array for per-tag default layouts
#
# =============================================================================
{
config,
pkgs,
lib,
...
}:
let
appearance = import ./appearance.nix { inherit config lib; };
tags = import ./tags.nix { inherit lib; };
rules = import ./rules.nix { inherit lib; };
layouts = import ./layouts.nix { inherit lib; };
keybindings = import ./keybindings.nix { inherit lib; };
api = import ./api.nix { inherit lib; };
configDefH = ''
${appearance.config}
${tags.config}
${rules.config}
${layouts.config}
${keybindings.config}
${api.config}
'';
in
{
services.xserver.windowManager.dwm = {
enable = true;
package =
(pkgs.dwm.override {
conf = configDefH;
}).overrideAttrs
(old: {
patches = [ ./dwm-lebowski.patch ];
buildInputs = old.buildInputs ++ [ pkgs.xorg.libXcursor ];
});
};
environment.systemPackages = [
(pkgs.writeShellScriptBin "vieworspawn" ''
mon=$1; tag=$2; class=$3; shift 3
# Focus monitor if multi-monitor setup (0=secondary, 1=primary)
if [[ $(${pkgs.autorandr}/bin/autorandr --detected) != "mobile" ]]; then
[[ "$mon" == "0" ]] && echo "mon-sec" > /tmp/dwm.fifo || echo "mon-prim" > /tmp/dwm.fifo
fi
echo "view $tag" > /tmp/dwm.fifo
sleep 0.02
if ! ${pkgs.xdotool}/bin/xdotool search --class "$class" >/dev/null 2>&1; then
exec "$@"
fi
'')
];
}

View file

@ -0,0 +1,181 @@
# dwm keybindings
{ lib }:
{
config = ''
/* key definitions */
#define MODKEY Mod4Mask
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
#define HOLDKEY 0xffeb // 0 - disable; 0xffe9 - Mod1Mask; 0xffeb - Mod4Mask (Super)
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
/* commands */
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *roficmd[] = { "rofi", "-show", "system", NULL };
static const char *rofiwebcmd[] = { "rofi-websearch", NULL };
static const char *browsercmd[] = { "zen-twilight", NULL };
static const char *lockcmd[] = { "slock", NULL };
static const char *kicadshowcmd[] = { "kicad-show", NULL };
static const char *kicadprojectscmd[] = { "kicad-projects", NULL };
static const char *kicadlibcmd[] = { "kicad-lib-launch", NULL };
static const char *kicadswapcmd[] = { "kicad-swap", NULL };
static const char *kicadcyclefcmd[] = { "kicad-cycle", "f", NULL };
static const char *kicadcyclebcmd[] = { "kicad-cycle", "b", NULL };
static const char *screenshotcmd[] = { "flameshot", "gui", NULL };
static const char *screenshotfullcmd[] = { "flameshot", "full", "--path", "/home/lebowski/Pictures/Screenshots", NULL };
static const char *cliphistcmd[] = { "sh", "-c", "greenclip print | rofi -dmenu -p Clipboard | xclip -selection clipboard", NULL };
static const char *volmutecmd[] = { "wpctl", "set-mute", "@DEFAULT_AUDIO_SINK@", "toggle", NULL };
static const char *micmutecmd[] = { "wpctl", "set-mute", "@DEFAULT_AUDIO_SOURCE@", "toggle", NULL };
static const char *volupcmd[] = { "sh", "-c", "wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%+", NULL };
static const char *voldowncmd[] = { "wpctl", "set-volume", "@DEFAULT_AUDIO_SINK@", "5%-", NULL };
/* vieworspawn: monitor, tag (1-based), class, cmd... */
static const char *termvos[] = { "vieworspawn", "0", "10", "st-256color", "st", NULL };
static const char *filesvos[] = { "vieworspawn", "0", "11", "Thunar", "thunar", NULL };
static const char *editorvos[] = { "vieworspawn", "1", "14", "dev.zed.Zed", "zeditor", NULL };
static const char *freecadvos[] = { "vieworspawn", "1", "15", "FreeCAD", "FreeCAD", "--single-instance", NULL };
static const char *bambuvos[] = { "vieworspawn", "1", "19", "BambuStudio", "bambu-studio", NULL };
/* fallback spawn commands */
static const char *termcmd[] = { "st", NULL };
static const char *filescmd[] = { "thunar", NULL };
static const char *editorcmd[] = { "zeditor", NULL };
static const char *freecadcmd[] = { "FreeCAD", "--single-instance", NULL };
static const char *bambucmd[] = { "bambu-studio", NULL };
static const Key keys[] = {
/* modifier key function argument */
/* Rofi */
{ MODKEY, XK_space, spawn, {.v = roficmd } },
{ MODKEY|Mod1Mask, XK_space, spawn, {.v = rofiwebcmd } },
/* Terminal */
{ MODKEY, XK_a, spawn, {.v = termvos } },
{ MODKEY|ShiftMask, XK_a, spawn, {.v = termcmd } },
/* Zed (code) */
{ MODKEY, XK_z, spawn, {.v = editorvos } },
{ MODKEY|ShiftMask, XK_z, spawn, {.v = editorcmd } },
/* Zen Browser (web) */
{ MODKEY, XK_x, view, {.ui = 1 << 12 } },
{ MODKEY|ShiftMask, XK_x, spawn, {.v = browsercmd } },
/* Thunar (files) */
{ MODKEY, XK_n, spawn, {.v = filesvos } },
{ MODKEY|ShiftMask, XK_n, spawn, {.v = filescmd } },
/* Video */
{ MODKEY, XK_m, view, {.ui = 1 << 11 } },
/* Lock */
{ MODKEY, XK_Escape, spawn, {.v = lockcmd } },
/* KiCad */
{ MODKEY, XK_k, spawn, {.v = kicadshowcmd } },
{ MODKEY|ShiftMask, XK_k, spawn, {.v = kicadprojectscmd } },
{ MODKEY, XK_l, spawn, {.v = kicadlibcmd } },
{ MODKEY|ControlMask, XK_k, view, {.ui = 1 << 15 } },
{ MODKEY|Mod1Mask, XK_k, spawn, {.v = kicadswapcmd } },
{ MODKEY, XK_bracketright, spawn, {.v = kicadcyclefcmd } },
{ MODKEY, XK_bracketleft, spawn, {.v = kicadcyclebcmd } },
/* FreeCAD */
{ MODKEY, XK_f, spawn, {.v = freecadvos } },
{ MODKEY|ShiftMask, XK_f, spawn, {.v = freecadcmd } },
/* Bambu Studio */
{ MODKEY, XK_p, spawn, {.v = bambuvos } },
{ MODKEY|ShiftMask, XK_p, spawn, {.v = bambucmd } },
/* Window management */
{ MODKEY, XK_q, killclient, {0} },
{ MODKEY, XK_e, togglefloating, {0} },
{ MODKEY, XK_w, togglefullscr, {0} },
{ MODKEY, XK_t, tabmode, {-1} },
/* Focus movement - stack */
{ MODKEY, XK_j, focusstack, {.i = +1 } },
{ MODKEY|ShiftMask, XK_j, focusstack, {.i = -1 } },
/* Adjacent tags - Up/Down */
{ MODKEY, XK_Up, viewprev, {0} },
{ MODKEY, XK_Down, viewnext, {0} },
{ MODKEY|ShiftMask, XK_Up, tagtoprev, {0} },
{ MODKEY|ShiftMask, XK_Down, tagtonext, {0} },
/* Move to monitor */
{ MODKEY, XK_Left, focusnthmon, {.i = 1 } },
{ MODKEY, XK_Right, focusnthmon, {.i = 0 } },
{ MODKEY|ShiftMask, XK_Left, tagnthmon, {.i = 1 } },
{ MODKEY|ShiftMask, XK_Right, tagnthmon, {.i = 0 } },
/* Swap monitors */
{ MODKEY|ShiftMask, XK_apostrophe, swapmon, {0} },
/* Resize master */
{ MODKEY|Mod1Mask, XK_Left, setmfact, {.f = -0.05} },
{ MODKEY|Mod1Mask, XK_Right, setmfact, {.f = +0.05} },
/* Tags */
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
TAGKEYS( XK_4, 3)
TAGKEYS( XK_5, 4)
TAGKEYS( XK_6, 5)
TAGKEYS( XK_7, 6)
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ MODKEY, XK_0, view, {.ui = ~0 } },
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
/* Clipboard */
{ MODKEY|ShiftMask, XK_v, spawn, {.v = cliphistcmd } },
/* Screenshots */
{ 0, XK_Print, spawn, {.v = screenshotcmd } },
{ ShiftMask, XK_Print, spawn, {.v = screenshotfullcmd } },
/* Media keys */
{ 0, XF86XK_AudioMute, spawn, {.v = volmutecmd } },
{ 0, XF86XK_AudioMicMute, spawn, {.v = micmutecmd } },
{ 0, XF86XK_AudioRaiseVolume, spawn, {.v = volupcmd } },
{ 0, XF86XK_AudioLowerVolume, spawn, {.v = voldowncmd } },
/* Holdbar */
{ 0, HOLDKEY, holdbar, {0} },
};
/* button definitions */
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static const Button buttons[] = {
/* click event mask button function argument */
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },
{ ClkTagBar, 0, Button1, view, {0} },
{ ClkTagBar, 0, Button3, toggleview, {0} },
{ ClkTagBar, MODKEY, Button1, tag, {0} },
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
{ ClkTabBar, 0, Button1, focuswin, {0} },
/* Scroll through tags with Mod+scroll */
{ ClkRootWin, MODKEY, Button4, viewprev, {0} },
{ ClkRootWin, MODKEY, Button5, viewnext, {0} },
{ ClkClientWin, MODKEY, Button4, viewprev, {0} },
{ ClkClientWin, MODKEY, Button5, viewnext, {0} },
};
'';
}

49
Suckless/dwm/layouts.nix Normal file
View file

@ -0,0 +1,49 @@
# dwm layouts
{ lib }:
let
# Layout indices: 0 = tile, 1 = floating, 2 = monocle
defLayouts = [
0 # index 0: all-tags view
0 # tag 1
0 # tag 2
0 # tag 3
0 # tag 4
0 # tag 5
0 # tag 6
0 # tag 7
0 # tag 8
0 # tag 9
2 # tag 10: terminal
2 # tag 11: files
2 # tag 12: video
2 # tag 13: web/browser
2 # tag 14: code
2 # tag 15: freecad
2 # tag 16: kicad project manager
2 # tag 17: kicad (sch/pcb editors)
2 # tag 18: kicad-aux (pcb/footprint mobile)
2 # tag 19: bambu studio
];
defLayoutsStr = lib.concatMapStringsSep ", " toString defLayouts;
in
{
config = ''
/* layout(s) */
static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
static const int nmaster = 1; /* number of clients in master area */
static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
static const Layout layouts[] = {
/* symbol arrange function */
{ "[]=", tile }, /* 0: first entry is default */
{ "><>", NULL }, /* 1: floating */
{ "[M]", monocle }, /* 2: monocle (tabbed) */
};
/* default layout per tag */
/* 0 = tile, 1 = floating, 2 = monocle */
static int def_layouts[1 + LENGTH(tags)] = { ${defLayoutsStr} };
'';
}

41
Suckless/dwm/rules.nix Normal file
View file

@ -0,0 +1,41 @@
# dwm window rules
# Tag mapping: 1<<9=term, 1<<10=files, 1<<11=video, 1<<12=web, 1<<13=code,
# 1<<14=freecad, 1<<15=kicad-pm, 1<<16=kicad, 1<<17=kicad-aux
{ lib }:
{
config = ''
static const Rule rules[] = {
/* xprop(1):
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating monitor */
{ "Polkit-gnome-authentication-agent-1", NULL, NULL, 0, 1, -1 },
{ "KeePassXC", NULL, NULL, 0, 1, -1 },
{ NULL, NULL, "Picture-in-Picture", 0, 1, -1 },
{ "slint-viewer", NULL, NULL, 0, 1, -1 },
{ "LVGL Simulator", NULL, NULL, 0, 1, -1 },
{ NULL, NULL, "Axium Browser", 0, 1, -1 },
{ NULL, NULL, "Axium", 0, 1, -1 },
{ NULL, NULL, "Fex", 0, 1, -1 },
{ NULL, NULL, "@", 0, 1, -1 },
{ NULL, NULL, "OSM", 0, 1, -1 },
{ NULL, NULL, "YouNix", 0, 1, -1 },
{ "st-256color", NULL, NULL, 1 << 9, 0, 0 }, /* terminal */
{ "Thunar", NULL, NULL, 1 << 10, 0, 0 }, /* files */
{ "haruna", NULL, NULL, 1 << 11, 0, 1 }, /* video */
{ "zen-twilight", NULL, "Zen Twilight", 1 << 12, 0, 1 }, /* web */
{ "dev.zed.Zed", NULL, NULL, 1 << 13, 0, 1 }, /* code */
{ "FreeCAD", NULL, "Expression editor", 0, 1, -1 }, /* freecad formula popup */
{ "FreeCAD", NULL, "Insert length", 0, 1, -1 }, /* freecad dimension popup */
{ "FreeCAD", NULL, NULL, 1 << 14, 0, 1 }, /* freecad */
{ "KiCad", NULL, "KiCad 9", 1 << 15, 0, 0 }, /* kicad-pm: tag 16 */
{ "KiCad", NULL, "Schematic Editor", 1 << 16, 0, 0 }, /* kicad: tag 17, mon 0 */
{ "KiCad", NULL, "Symbol Editor", 1 << 16, 0, 0 }, /* kicad: tag 17, mon 0 */
{ "KiCad", NULL, "PCB Editor", 1 << 16, 0, 1 }, /* kicad: tag 17, mon 1 */
{ "KiCad", NULL, "Footprint Editor", 1 << 16, 0, 1 }, /* kicad: tag 17, mon 1 */
{ "BambuStudio", NULL, NULL, 1 << 18, 0, 1 }, /* bambu studio */
};
'';
}

33
Suckless/dwm/tags.nix Normal file
View file

@ -0,0 +1,33 @@
# dwm tag names (Nerd Font icons for special workspaces)
{ lib }:
let
tags = [
"1"
"2"
"3"
"4"
"5"
"6"
"7"
"8"
"9"
"" # 10: terminal
"" # 11: files
"" # 12: video
"󰖟" # 13: web/browser
"" # 14: code
"󰻬" # 15: freecad
"" # 16: kicad project manager
"" # 17: kicad (sch/pcb editors)
"" # 18: kicad-aux (pcb/footprint mobile)
"󰹜" # 19: bambu studio
];
tagsStr = lib.concatMapStringsSep ", " (t: ''"${t}"'') tags;
in
{
config = ''
/* tagging */
static const char *tags[] = { ${tagsStr} };
'';
}

144
Suckless/home.nix Normal file
View file

@ -0,0 +1,144 @@
# Suckless home-manager configuration
# X11-specific settings that integrate with home-manager
{ pkgs-stable, ... }:
{
# Monitor management
programs.autorandr = {
enable = true;
package = pkgs-stable.autorandr;
hooks.postswitch = {
"set-xft-dpi" = "echo 'Xft.dpi: 96' | xrdb -merge";
"set-wallpaper" = ''
case "$AUTORANDR_CURRENT_PROFILE" in
docked)
xwallpaper --output eDP-1 --zoom ~/NixOS/Wallpapers/pearl.jpg --output DP-3 --zoom ~/NixOS/Wallpapers/siege.png
;;
mobile)
xwallpaper --output eDP-1 --zoom ~/NixOS/Wallpapers/siege.png
;;
esac
'';
};
profiles = {
"docked" = {
fingerprint = {
eDP-1 = "00ffffffffffff002c831207000000001d220104a51e1378025645935e5b9325185054000000010101010101010101010101010101010f3c80a070b0204018303c002ebd10000018000000000000000000000000000000000000000000000000000000000000000000000000000000fe004b443134304e3336333041303100df";
DP-3 = "00ffffffffffff0061a906b00100000025220103803c2278afa545ad504da6260c5054a5cb0081809500a9c0b300d1c0010101010101023a801871382d40582c450055502100001e000000ff0035333738323030303434353132000000fd0030a561ba3c000a202020202020000000fc005032374642422d52410a20202001c0020319b349010311130414051f90e200ca67030c00100038448e4480a070382d40582c450055502100001e605980a0703814403024350055502100001c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050";
};
config = {
DP-3 = {
enable = true;
primary = false;
mode = "1920x1080";
rate = "75.00";
dpi = 96;
position = "0x180";
};
eDP-1 = {
enable = true;
primary = true;
mode = "1920x1200";
rate = "60.00";
dpi = 96;
position = "1920x0";
rotate = "left";
scale = {
x = 0.75;
y = 0.75;
};
};
};
};
"mobile" = {
fingerprint = {
eDP-1 = "00ffffffffffff002c831207000000001d220104a51e1378025645935e5b9325185054000000010101010101010101010101010101010f3c80a070b0204018303c002ebd10000018000000000000000000000000000000000000000000000000000000000000000000000000000000fe004b443134304e3336333041303100df";
};
config = {
eDP-1 = {
enable = true;
primary = true;
mode = "1920x1200";
rate = "60.00";
dpi = 96;
position = "0x0";
};
};
};
};
};
services.autorandr = {
enable = true;
package = pkgs-stable.autorandr;
};
# Screenshots
services.flameshot = {
enable = true;
package = pkgs-stable.flameshot;
};
# Touchpad gestures (for mobile mode)
home.file.".config/libinput-gestures.conf".text = ''
# 3-finger up/down: tag navigation
gesture swipe up 3 sh -c 'echo "view-prev" > /tmp/dwm.fifo'
gesture swipe down 3 sh -c 'echo "view-next" > /tmp/dwm.fifo'
# 3-finger left/right: window cycling
gesture swipe left 3 sh -c 'echo "focus-prev" > /tmp/dwm.fifo'
gesture swipe right 3 sh -c 'echo "focus-next" > /tmp/dwm.fifo'
# 4-finger up/down: layout
gesture swipe up 4 sh -c 'echo "layout-mono" > /tmp/dwm.fifo'
gesture swipe down 4 sh -c 'echo "layout-tile" > /tmp/dwm.fifo'
# Pinch: rofi system menu / view all
gesture pinch in rofi -show system
gesture pinch out sh -c 'echo "view-all" > /tmp/dwm.fifo'
'';
# X11 startup script
home.file.".xinitrc".text = ''
# D-Bus environment for GTK apps (fixes slow first launch)
dbus-update-activation-environment --systemd DBUS_SESSION_BUS_ADDRESS DISPLAY XAUTHORITY
# Apply monitor profile
autorandr --change --default mobile
# Disable DPMS and screen blanking
xset s off
xset -dpms
xset s noblank
# Numlock on
numlockx
# Status bar
slstatus &
# Compositor
# picom &
# Touchpad gestures
libinput-gestures-setup start &
# Auto-rotate
auto-rotate &
# KeePassXC (minimized, ready for browser extension)
keepassxc --minimized &
# Create dwmfifo for IPC
mkfifo /tmp/dwm.fifo 2>/dev/null || true
# Start dwm
exec dwm
'';
# Auto-start X11/dwm on tty1
programs.bash.profileExtra = ''
if [ -z "$DISPLAY" ] && [ "$XDG_VTNR" = 1 ]; then
exec startx
fi
'';
}

93
Suckless/slstatus.nix Normal file
View file

@ -0,0 +1,93 @@
# slstatus - suckless status bar
{ pkgs, ... }:
let
# Dynamic icon scripts
batteryScript = pkgs.writeShellScript "slstatus-battery" ''
perc=$(cat /sys/class/power_supply/BATT/capacity 2>/dev/null || echo "0")
status=$(cat /sys/class/power_supply/BATT/status 2>/dev/null || echo "Unknown")
if [ "$status" = "Charging" ]; then icon="󰂄"
elif [ "$perc" -ge 90 ]; then icon="󰁹"
elif [ "$perc" -ge 80 ]; then icon="󰂂"
elif [ "$perc" -ge 70 ]; then icon="󰂁"
elif [ "$perc" -ge 60 ]; then icon="󰂀"
elif [ "$perc" -ge 50 ]; then icon="󰁿"
elif [ "$perc" -ge 40 ]; then icon="󰁾"
elif [ "$perc" -ge 30 ]; then icon="󰁽"
elif [ "$perc" -ge 20 ]; then icon="󰁼"
elif [ "$perc" -ge 10 ]; then icon="󰁻"
else icon="󰁺"
fi
printf "%s %s%%" "$icon" "$perc"
'';
mouseScript = pkgs.writeShellScript "slstatus-mouse" ''
perc=$(cat /sys/class/power_supply/hidpp_battery_0/capacity 2>/dev/null || echo "")
[ -z "$perc" ] && exit 0
printf "󰍽 %s%%" "$perc"
'';
volumeScript = pkgs.writeShellScript "slstatus-volume" ''
vol=$(${pkgs.wireplumber}/bin/wpctl get-volume @DEFAULT_AUDIO_SINK@ 2>/dev/null)
muted=$(echo "$vol" | grep -c MUTED)
perc=$(echo "$vol" | awk '{printf "%.0f", $2*100}')
if [ "$muted" -eq 1 ]; then icon="󰝟"
elif [ "$perc" -ge 66 ]; then icon="󰕾"
elif [ "$perc" -ge 33 ]; then icon="󰖀"
else icon="󰕿"
fi
printf "%s %s%%" "$icon" "$perc"
'';
wifiScript = pkgs.writeShellScript "slstatus-wifi" ''
essid=$(cat /sys/class/net/wlp2s0/wireless/../uevent 2>/dev/null | grep INTERFACE | cut -d= -f2)
essid=$(${pkgs.iw}/bin/iw dev wlp2s0 link 2>/dev/null | grep SSID | awk '{print $2}')
[ -z "$essid" ] && exit 0
perc=$(awk 'NR==3 {printf "%.0f", $3*100/70}' /proc/net/wireless 2>/dev/null || echo "0")
if [ "$perc" -ge 75 ]; then icon="󰤥"
elif [ "$perc" -ge 50 ]; then icon="󰤢"
elif [ "$perc" -ge 25 ]; then icon="󰤟"
else icon="󰤯"
fi
printf "%s %s %s%%" "$icon" "$essid" "$perc"
'';
config = ''
/* See LICENSE file for copyright and license details. */
/* interval between updates (in ms) */
const unsigned int interval = 1000;
/* text to show if no value can be retrieved */
static const char unknown_str[] = "";
/* maximum output string length */
#define MAXLEN 512
static const struct arg args[] = {
/* function format argument */
{ cpu_freq, " %s/", NULL },
{ cpu_perc, "%s%% | ", NULL },
{ ram_used, " %s/", NULL },
{ ram_perc, "%s%% | ", NULL },
{ temp, " %s°C | ", "/sys/class/thermal/thermal_zone0/temp" },
{ run_command, "%s | ", "${wifiScript}" },
{ run_command, "%s | ", "${volumeScript}" },
{ run_command, "%s | ", "${batteryScript}" },
{ run_command, "%s | ", "${mouseScript}" },
{ datetime, " %s ", "%d/%m %H:%M:%S" },
};
'';
in
{
environment.systemPackages = [
(pkgs.slstatus.override { conf = config; })
];
}

View file

@ -0,0 +1,46 @@
# st appearance settings
{ config, lib }:
let
colors = config.lib.stylix.colors;
fonts = config.stylix.fonts;
in
{
font = "${fonts.monospace.name}:pixelsize=${
toString (fonts.sizes.terminal + 6)
}:antialias=true:autohint=true";
alpha = "0.9";
colorsSed = ''
sed -i '/static const char \*colorname\[\]/,/^};/c\
static const char *colorname[] = {\
/* 8 normal colors */\
"#${colors.base00}",\
"#${colors.base08}",\
"#${colors.base0B}",\
"#${colors.base0A}",\
"#${colors.base0D}",\
"#${colors.base0E}",\
"#${colors.base0C}",\
"#${colors.base05}",\
\
/* 8 bright colors */\
"#${colors.base03}",\
"#${colors.base08}",\
"#${colors.base0B}",\
"#${colors.base0A}",\
"#${colors.base0D}",\
"#${colors.base0E}",\
"#${colors.base0C}",\
"#${colors.base07}",\
\
[255] = 0,\
\
/* more colors can be added after 255 to use with DefaultXX */\
"#${colors.base04}", /* 256: cursor */\
"#${colors.base03}", /* 257: reverse cursor */\
"#${colors.base05}", /* 258: foreground */\
"#${colors.base00}", /* 259: background */\
};' config.def.h
'';
}

69
Suckless/st/st.nix Normal file
View file

@ -0,0 +1,69 @@
# st terminal configuration
{ config, pkgs, lib, ... }:
let
appearance = import ./appearance.nix { inherit config lib; };
in
{
nixpkgs.overlays = [(final: prev: {
st = prev.st.overrideAttrs (oldAttrs: {
buildInputs = oldAttrs.buildInputs ++ [ final.harfbuzz ];
patches = (oldAttrs.patches or []) ++ [
# Scrollback (ringbuffer + float + mouse)
(final.fetchurl {
url = "https://st.suckless.org/patches/scrollback/st-scrollback-ringbuffer-0.9.2.diff";
sha256 = "1r23q4mi5bkam49ld5c3ccwaa1li7bbjx0ndjgm207p02az9h4cn";
})
(final.fetchurl {
url = "https://st.suckless.org/patches/scrollback/st-scrollback-float-0.9.2.diff";
sha256 = "01r1gdgkcpf9194257myjnr5nn1fj1baj13wjm9rf2nclbagifgm";
})
(final.fetchurl {
url = "https://st.suckless.org/patches/scrollback/st-scrollback-mouse-0.9.2.diff";
sha256 = "068s5rjvvw2174y34i5xxvpw4jvjy58akd1kgf025h1153hmf7jy";
})
# Alpha (transparency)
(final.fetchurl {
url = "https://st.suckless.org/patches/alpha/st-alpha-20240814-a0274bc.diff";
sha256 = "0hld9dwkk7i1f0z0k9biigx2g4wzlqa2yb7vdn5rrf6ymr5nlbsn";
})
# Anysize (no gaps)
(final.fetchurl {
url = "https://st.suckless.org/patches/anysize/st-expected-anysize-0.9.diff";
sha256 = "04gvkf80lhaiwyv3m7fdkf81msf8al1kfb7inx1bf02ygx9152v2";
})
# Bold is not bright
(final.fetchurl {
url = "https://st.suckless.org/patches/bold-is-not-bright/st-bold-is-not-bright-20190127-3be4cf1.diff";
sha256 = "1cpap2jz80n90izhq5fdv2cvg29hj6bhhvjxk40zkskwmjn6k49j";
})
# Clipboard
(final.fetchurl {
url = "https://st.suckless.org/patches/clipboard/st-clipboard-0.8.3.diff";
sha256 = "1h1nwilwws02h2lnxzmrzr69lyh6pwsym21hvalp9kmbacwy6p0g";
})
# Ligatures (scrollback-ringbuffer variant)
(final.fetchurl {
url = "https://st.suckless.org/patches/ligatures/0.9.3/st-ligatures-scrollback-ringbuffer-20251007-0.9.3.diff";
sha256 = "0c2w1p0siafiyarfx6skdighwzw29d1mydpjfrwgrvdsywwyq2di";
})
];
postPatch = (oldAttrs.postPatch or "") + ''
# Font
substituteInPlace config.def.h \
--replace '"Liberation Mono:pixelsize=12:antialias=true:autohint=true"' \
'"${appearance.font}"'
# Alpha
substituteInPlace config.def.h \
--replace 'float alpha = 0.8;' \
'float alpha = ${appearance.alpha};'
# Colors
${appearance.colorsSed}
'';
});
})];
environment.systemPackages = [ pkgs.st ];
}

121
Suckless/suckless.nix Normal file
View file

@ -0,0 +1,121 @@
# Suckless configuration entry point
# X11 window manager setup with dwm, st, and related tools
{ pkgs, ... }:
{
imports = [
./dwm/dwm.nix
./st/st.nix
./slstatus.nix
];
# ============================================================================
# X11 Display Server
# ============================================================================
services.xserver = {
enable = true;
# Keyboard layout
xkb.layout = "us";
displayManager.startx.enable = true;
# Auto-lock after 10 minutes of inactivity
xautolock = {
enable = true;
time = 10;
locker = "${pkgs.slock}/bin/slock";
};
};
# Input (libinput)
services.libinput.enable = true;
# Tablet/stylus support
services.xserver.wacom.enable = true;
# Accelerometer for auto-rotation
hardware.sensor.iio.enable = true;
# ============================================================================
# Screen Locker
# ============================================================================
programs.slock = {
enable = true;
package = pkgs.slock;
};
# ============================================================================
# XDG Portal for X11
# ============================================================================
xdg.portal = {
enable = true;
extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
config.common.default = "gtk";
};
# ============================================================================
# X11 ecosystem packages
# ============================================================================
environment.systemPackages = with pkgs; [
# Clipboard
xclip
# Wallpaper
xwallpaper
# Numlock on startup
numlockx
# Touchpad gestures
libinput-gestures
# Auto-rotate based on accelerometer (with wallpaper switching, mobile only)
(writeShellScriptBin "auto-rotate" ''
export DISPLAY=''${DISPLAY:-:0}
WALLPAPER_DIR="$HOME/NixOS/Wallpapers"
${iio-sensor-proxy}/bin/monitor-sensor | while read -r line; do
# Skip rotation when docked (would mess up monitor positions)
[[ $(${autorandr}/bin/autorandr --detected) != "mobile" ]] && continue
case "$line" in
*"normal"*)
${xorg.xrandr}/bin/xrandr --output eDP-1 --rotate normal
${xwallpaper}/bin/xwallpaper --output eDP-1 --zoom "$WALLPAPER_DIR/siege.png"
;;
*"left-up"*)
${xorg.xrandr}/bin/xrandr --output eDP-1 --rotate left
${xwallpaper}/bin/xwallpaper --output eDP-1 --zoom "$WALLPAPER_DIR/pearl.jpg"
;;
*"right-up"*)
${xorg.xrandr}/bin/xrandr --output eDP-1 --rotate right
${xwallpaper}/bin/xwallpaper --output eDP-1 --zoom "$WALLPAPER_DIR/pearl.jpg"
;;
*"bottom-up"*)
${xorg.xrandr}/bin/xrandr --output eDP-1 --rotate inverted
${xwallpaper}/bin/xwallpaper --output eDP-1 --zoom "$WALLPAPER_DIR/siege.png"
;;
esac
done
'')
];
# ============================================================================
# Polkit authentication agent
# ============================================================================
security.polkit.enable = true;
systemd.user.services.polkit-gnome-authentication-agent-1 = {
description = "polkit-gnome-authentication-agent-1";
wantedBy = [ "graphical-session.target" ];
wants = [ "graphical-session.target" ];
after = [ "graphical-session.target" ];
serviceConfig = {
Type = "simple";
ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1";
Restart = "on-failure";
RestartSec = 1;
TimeoutStopSec = 10;
};
};
}

BIN
Wallpapers/Shift.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 KiB

BIN
Wallpapers/caesar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 MiB

BIN
Wallpapers/norse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

BIN
Wallpapers/pearl.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 MiB

BIN
Wallpapers/shift_base.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 KiB

BIN
Wallpapers/siege.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

176
configuration.nix Executable file
View file

@ -0,0 +1,176 @@
{
pkgs,
...
}:
{
imports = [
./hardware-configuration.nix
./stylix.nix
# ./Hyprsuck/hyprsuck.nix
./Suckless/suckless.nix
./ryzenadj.nix
# ./virtualisation.nix
# ./Hetzner/setup.nix
];
nix.settings = {
experimental-features = [
"nix-command"
"flakes"
];
# Axium binary caches
substituters = [
"https://cache.nixos.org"
"https://axium.cachix.org"
"https://cache.axiomania.org/main"
];
http-connections = 128;
max-substitution-jobs = 128;
max-jobs = "auto";
trusted-public-keys = [
"axium.cachix.org-1:BfzPfRTbbCYmaQrVLSWchgsR4ScA9ZCZ389FyWspUH8="
"main:Uz5F0MbXItVx2XCmBbEAMmQ0T6+DZDgLaXWalh1k++o="
];
};
nix.distributedBuilds = true;
# Required for home-manager xdg.portal with useUserPackages
environment.pathsToLink = [
"/share/applications"
"/share/xdg-desktop-portal"
];
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.systemd-boot.configurationLimit = 10;
boot.loader.timeout = 1;
networking.hostName = "v3";
networking.networkmanager.enable = true;
hardware.bluetooth.enable = true;
time.timeZone = "Asia/Ho_Chi_Minh";
i18n.defaultLocale = "en_US.UTF-8";
i18n.extraLocaleSettings = {
LC_ADDRESS = "en_US.UTF-8";
LC_IDENTIFICATION = "en_US.UTF-8";
LC_MEASUREMENT = "en_US.UTF-8";
LC_MONETARY = "en_US.UTF-8";
LC_NAME = "en_US.UTF-8";
LC_NUMERIC = "en_US.UTF-8";
LC_PAPER = "en_US.UTF-8";
LC_TELEPHONE = "en_US.UTF-8";
LC_TIME = "en_US.UTF-8";
};
services.getty.autologinUser = "lebowski";
programs.nix-ld.enable = true;
services.pulseaudio.enable = false;
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
users.users.lebowski = {
isNormalUser = true;
description = "Antoine Lespinasse";
extraGroups = [
"networkmanager"
"wheel"
"input"
];
};
# GVfs - for Nautilus trash, network shares, MTP devices
services.gvfs.enable = true;
# UPower - battery/power device monitoring
services.upower.enable = true;
# Power Profiles Daemon - power management (performance/balanced/power-saver)
services.power-profiles-daemon.enable = true;
# System packages (stable for core tools)
environment.systemPackages = with pkgs; [
wget
git
bat
btop
ncdu
nmap
neofetch
xdotool
jq
attic-client
wireguard-tools
sox
ffmpegthumbnailer
gdk-pixbuf
librsvg
evince
libgsf
libjxl
libavif
# Disk utilities
gparted
gnome-disk-utility
# Image / PDF viewers
kdePackages.gwenview
kdePackages.okular
# File browser
xfce.thunar
xfce.thunar-volman
xfce.tumbler # thumbnails
];
# ============================================================================
# WireGuard VPN profiles
# ============================================================================
# Generate keys: wg genkey | sudo tee /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key
# Then add the public key as a peer on the VPS.
# Start/stop: sudo systemctl start/stop wg-quick-wg-services / wg-quick-wg-vpn
# Profile 1: Internal services only (split tunnel)
networking.wg-quick.interfaces.wg-services = {
autostart = false;
address = [ "10.100.0.2/24" ];
privateKeyFile = "/etc/wireguard/private.key";
peers = [{
publicKey = "F2hvz4vx9VrM6IZ2zMUG2FMPMCMwmfxGH9qocbe4q3U=";
endpoint = "178.104.15.221:51820";
allowedIPs = [ "10.100.0.0/24" "178.104.15.221/32" ];
persistentKeepalive = 25;
}];
};
# Profile 2: Full VPN + AdGuard DNS ad-blocking
networking.wg-quick.interfaces.wg-vpn = {
autostart = false;
address = [ "10.100.0.2/24" ];
dns = [ "10.100.0.1" ];
privateKeyFile = "/etc/wireguard/private.key";
peers = [{
publicKey = "F2hvz4vx9VrM6IZ2zMUG2FMPMCMwmfxGH9qocbe4q3U=";
endpoint = "178.104.15.221:51820";
allowedIPs = [ "0.0.0.0/0" ];
persistentKeepalive = 25;
}];
};
system.stateVersion = "25.11";
}

420
flake.lock generated Executable file
View file

@ -0,0 +1,420 @@
{
"nodes": {
"base16": {
"inputs": {
"fromYaml": "fromYaml"
},
"locked": {
"lastModified": 1755819240,
"narHash": "sha256-qcMhnL7aGAuFuutH4rq9fvAhCpJWVHLcHVZLtPctPlo=",
"owner": "SenchoPens",
"repo": "base16.nix",
"rev": "75ed5e5e3fce37df22e49125181fa37899c3ccd6",
"type": "github"
},
"original": {
"owner": "SenchoPens",
"repo": "base16.nix",
"type": "github"
}
},
"base16-fish": {
"flake": false,
"locked": {
"lastModified": 1765809053,
"narHash": "sha256-XCUQLoLfBJ8saWms2HCIj4NEN+xNsWBlU1NrEPcQG4s=",
"owner": "tomyun",
"repo": "base16-fish",
"rev": "86cbea4dca62e08fb7fd83a70e96472f92574782",
"type": "github"
},
"original": {
"owner": "tomyun",
"repo": "base16-fish",
"rev": "86cbea4dca62e08fb7fd83a70e96472f92574782",
"type": "github"
}
},
"base16-helix": {
"flake": false,
"locked": {
"lastModified": 1760703920,
"narHash": "sha256-m82fGUYns4uHd+ZTdoLX2vlHikzwzdu2s2rYM2bNwzw=",
"owner": "tinted-theming",
"repo": "base16-helix",
"rev": "d646af9b7d14bff08824538164af99d0c521b185",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "base16-helix",
"type": "github"
}
},
"base16-vim": {
"flake": false,
"locked": {
"lastModified": 1732806396,
"narHash": "sha256-e0bpPySdJf0F68Ndanwm+KWHgQiZ0s7liLhvJSWDNsA=",
"owner": "tinted-theming",
"repo": "base16-vim",
"rev": "577fe8125d74ff456cf942c733a85d769afe58b7",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "base16-vim",
"rev": "577fe8125d74ff456cf942c733a85d769afe58b7",
"type": "github"
}
},
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1773889306,
"narHash": "sha256-PAqwnsBSI9SVC2QugvQ3xeYCB0otOwCacB1ueQj2tgw=",
"owner": "nix-community",
"repo": "disko",
"rev": "5ad85c82cc52264f4beddc934ba57f3789f28347",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"firefox-gnome-theme": {
"flake": false,
"locked": {
"lastModified": 1764873433,
"narHash": "sha256-1XPewtGMi+9wN9Ispoluxunw/RwozuTRVuuQOmxzt+A=",
"owner": "rafaelmardojai",
"repo": "firefox-gnome-theme",
"rev": "f7ffd917ac0d253dbd6a3bf3da06888f57c69f92",
"type": "github"
},
"original": {
"owner": "rafaelmardojai",
"repo": "firefox-gnome-theme",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"stylix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1767609335,
"narHash": "sha256-feveD98mQpptwrAEggBQKJTYbvwwglSbOv53uCfH9PY=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "250481aafeb741edfe23d29195671c19b36b6dca",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"fromYaml": {
"flake": false,
"locked": {
"lastModified": 1731966426,
"narHash": "sha256-lq95WydhbUTWig/JpqiB7oViTcHFP8Lv41IGtayokA8=",
"owner": "SenchoPens",
"repo": "fromYaml",
"rev": "106af9e2f715e2d828df706c386a685698f3223b",
"type": "github"
},
"original": {
"owner": "SenchoPens",
"repo": "fromYaml",
"type": "github"
}
},
"gnome-shell": {
"flake": false,
"locked": {
"host": "gitlab.gnome.org",
"lastModified": 1767737596,
"narHash": "sha256-eFujfIUQDgWnSJBablOuG+32hCai192yRdrNHTv0a+s=",
"owner": "GNOME",
"repo": "gnome-shell",
"rev": "ef02db02bf0ff342734d525b5767814770d85b49",
"type": "gitlab"
},
"original": {
"host": "gitlab.gnome.org",
"owner": "GNOME",
"ref": "gnome-49",
"repo": "gnome-shell",
"type": "gitlab"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
"nixpkgs-unstable"
]
},
"locked": {
"lastModified": 1774738535,
"narHash": "sha256-2jfBEZUC67IlnxO5KItFCAd7Oc+1TvyV/jQlR+2ykGQ=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "769e07ef8f4cf7b1ec3b96ef015abec9bc6b1e2a",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"nixos-hardware": {
"locked": {
"lastModified": 1766517411,
"narHash": "sha256-qilXvRpeyefgLRl1oyitQsacWGP5TrWf7f6V+KOUt38=",
"owner": "GammaKinematics",
"repo": "nixos-hardware",
"rev": "b1dfc15a77366c8f163af12b8b3c5f4d4f56812c",
"type": "github"
},
"original": {
"owner": "GammaKinematics",
"repo": "nixos-hardware",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1774388614,
"narHash": "sha256-tFwzTI0DdDzovdE9+Ras6CUss0yn8P9XV4Ja6RjA+nU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1073dad219cb244572b74da2b20c7fe39cb3fa9e",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-25.11",
"type": "indirect"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1774386573,
"narHash": "sha256-4hAV26quOxdC6iyG7kYaZcM3VOskcPUrdCQd/nx8obc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "46db2e09e1d3f113a13c0d7b81e2f221c63b8ce9",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"nur": {
"inputs": {
"flake-parts": [
"stylix",
"flake-parts"
],
"nixpkgs": [
"stylix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1767810917,
"narHash": "sha256-ZKqhk772+v/bujjhla9VABwcvz+hB2IaRyeLT6CFnT0=",
"owner": "nix-community",
"repo": "NUR",
"rev": "dead29c804adc928d3a69dfe7f9f12d0eec1f1a4",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "NUR",
"type": "github"
}
},
"root": {
"inputs": {
"disko": "disko",
"home-manager": "home-manager",
"nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable",
"stylix": "stylix",
"zen-browser": "zen-browser"
}
},
"stylix": {
"inputs": {
"base16": "base16",
"base16-fish": "base16-fish",
"base16-helix": "base16-helix",
"base16-vim": "base16-vim",
"firefox-gnome-theme": "firefox-gnome-theme",
"flake-parts": "flake-parts",
"gnome-shell": "gnome-shell",
"nixpkgs": [
"nixpkgs-unstable"
],
"nur": "nur",
"systems": "systems",
"tinted-foot": "tinted-foot",
"tinted-kitty": "tinted-kitty",
"tinted-schemes": "tinted-schemes",
"tinted-tmux": "tinted-tmux",
"tinted-zed": "tinted-zed"
},
"locked": {
"lastModified": 1774124764,
"narHash": "sha256-Poz9WTjiRlqZIf197CrMMJfTifZhrZpbHFv0eU1Nhtg=",
"owner": "nix-community",
"repo": "stylix",
"rev": "e31c79f571c5595a155f84b9d77ce53a84745494",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "stylix",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"tinted-foot": {
"flake": false,
"locked": {
"lastModified": 1726913040,
"narHash": "sha256-+eDZPkw7efMNUf3/Pv0EmsidqdwNJ1TaOum6k7lngDQ=",
"owner": "tinted-theming",
"repo": "tinted-foot",
"rev": "fd1b924b6c45c3e4465e8a849e67ea82933fcbe4",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "tinted-foot",
"rev": "fd1b924b6c45c3e4465e8a849e67ea82933fcbe4",
"type": "github"
}
},
"tinted-kitty": {
"flake": false,
"locked": {
"lastModified": 1735730497,
"narHash": "sha256-4KtB+FiUzIeK/4aHCKce3V9HwRvYaxX+F1edUrfgzb8=",
"owner": "tinted-theming",
"repo": "tinted-kitty",
"rev": "de6f888497f2c6b2279361bfc790f164bfd0f3fa",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "tinted-kitty",
"type": "github"
}
},
"tinted-schemes": {
"flake": false,
"locked": {
"lastModified": 1767710407,
"narHash": "sha256-+W1EB79Jl0/gm4JqmO0Nuc5C7hRdp4vfsV/VdzI+des=",
"owner": "tinted-theming",
"repo": "schemes",
"rev": "2800e2b8ac90f678d7e4acebe4fa253f602e05b2",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "schemes",
"type": "github"
}
},
"tinted-tmux": {
"flake": false,
"locked": {
"lastModified": 1767489635,
"narHash": "sha256-e6nnFnWXKBCJjCv4QG4bbcouJ6y3yeT70V9MofL32lU=",
"owner": "tinted-theming",
"repo": "tinted-tmux",
"rev": "3c32729ccae99be44fe8a125d20be06f8d7d8184",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "tinted-tmux",
"type": "github"
}
},
"tinted-zed": {
"flake": false,
"locked": {
"lastModified": 1767488740,
"narHash": "sha256-wVOj0qyil8m+ouSsVZcNjl5ZR+1GdOOAooAatQXHbuU=",
"owner": "tinted-theming",
"repo": "base16-zed",
"rev": "11abb0b282ad3786a2aae088d3a01c60916f2e40",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "base16-zed",
"type": "github"
}
},
"zen-browser": {
"inputs": {
"home-manager": [
"home-manager"
],
"nixpkgs": [
"nixpkgs-unstable"
]
},
"locked": {
"lastModified": 1774708879,
"narHash": "sha256-rTYvYkQL69/YkZB+MRA/IaX1qJ1lPx5KXoQS2/9+7Mw=",
"owner": "0xc000022070",
"repo": "zen-browser-flake",
"rev": "d01d23c798cceef42307d5789bfbce70515e8800",
"type": "github"
},
"original": {
"owner": "0xc000022070",
"repo": "zen-browser-flake",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

112
flake.nix Executable file
View file

@ -0,0 +1,112 @@
{
description = "NixOS - AL";
inputs = {
# Stable nixpkgs for core system (kernel, boot, virtualization)
nixpkgs.url = "nixpkgs/nixos-25.11";
# Unstable nixpkgs for desktop/dev tools
nixpkgs-unstable.url = "nixpkgs/nixos-unstable";
# Home Manager
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
# Zen Browser
zen-browser = {
url = "github:0xc000022070/zen-browser-flake";
inputs.nixpkgs.follows = "nixpkgs-unstable";
inputs.home-manager.follows = "home-manager";
};
# Hardware-specific optimizations (fork with Minisforum V3 SE support)
nixos-hardware.url = "github:GammaKinematics/nixos-hardware";
# Stylix - system-wide theming
stylix = {
url = "github:nix-community/stylix";
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
# Disko - declarative disk partitioning (for VPS)
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
self,
nixpkgs,
nixpkgs-unstable,
home-manager,
zen-browser,
...
}@inputs:
let
system = "x86_64-linux";
# Stable pkgs for CAD/manufacturing (more reliable builds)
pkgs-stable = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
# Unstable pkgs for home-manager / desktop / dev
pkgs-unstable = import nixpkgs-unstable {
inherit system;
config.allowUnfree = true;
};
in
{
# ── Hetzner VPS ──
nixosConfigurations.axiomania = nixpkgs.lib.nixosSystem {
inherit system;
modules = [
inputs.disko.nixosModules.disko
./Hetzner/axiomania.nix
];
};
# ── Hetzner ephemeral builder ──
nixosConfigurations.builder = nixpkgs.lib.nixosSystem {
inherit system;
modules = [
inputs.disko.nixosModules.disko
./Hetzner/builder.nix
];
};
# ── Local: Minisforum V3 SE ──
nixosConfigurations.v3 = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = { inherit inputs; };
modules = [
inputs.stylix.nixosModules.stylix
inputs.nixos-hardware.nixosModules.minisforum-v3-se
./configuration.nix
home-manager.nixosModules.home-manager
{
home-manager = {
useGlobalPkgs = false;
useUserPackages = true;
extraSpecialArgs = {
inherit inputs pkgs-unstable pkgs-stable;
};
users.lebowski = import ./home.nix;
backupFileExtension = "backup";
};
}
];
};
};
}

68
git.nix Normal file
View file

@ -0,0 +1,68 @@
{ pkgs-stable, ... }:
{
# ============================================================================
# Git Configuration
# ============================================================================
programs.git = {
enable = true;
package = pkgs-stable.git;
settings = {
user.name = "GammaKinematics";
user.email = "gamma.kinematics@gmail.com";
init.defaultBranch = "main";
pull.rebase = true;
push.autoSetupRemote = true;
# Use SSH for GitHub
url."ssh://git@github.com/".insteadOf = "https://github.com/";
};
# Delta - better diff viewer
# delta = {
# enable = true;
# options = {
# navigate = true;
# light = false;
# line-numbers = true;
# };
# };
# Aliases
# aliases = {
# co = "checkout";
# br = "branch";
# ci = "commit";
# st = "status";
# lg = "log --oneline --graph --decorate";
# };
# SSH signing (for verified commits)
# signing = {
# key = "~/.ssh/id_ed25519.pub";
# signByDefault = true;
# };
# extraConfig.gpg.format = "ssh";
};
# ============================================================================
# SSH Configuration
# ============================================================================
programs.ssh = {
enable = true;
enableDefaultConfig = false;
matchBlocks = {
"github.com" = {
host = "github.com";
identityFile = "~/.ssh/id_ed25519";
identitiesOnly = true;
};
# Add more hosts as needed
# "gitlab.com" = {
# host = "gitlab.com";
# identityFile = "~/.ssh/id_ed25519";
# identitiesOnly = true;
# };
};
};
}

73
hardware-configuration.nix Executable file
View file

@ -0,0 +1,73 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"nvme"
"xhci_pci"
"thunderbolt"
"usb_storage"
"usbhid"
"uas"
"sd_mod"
"rtsx_pci_sdmmc"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/ea496278-ae69-48b0-9edb-b351b9620829";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/C267-DCFD";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
fileSystems."/data" = {
device = "/dev/disk/by-label/DATA";
fsType = "ntfs";
options = [
"rw"
"uid=1000"
"gid=100"
"umask=022"
"nofail"
];
};
fileSystems."/data-bis" = {
device = "/dev/disk/by-label/DATA-bis";
fsType = "ntfs";
options = [
"rw"
"uid=1000"
"gid=100"
"umask=022"
"nofail"
];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

141
home.nix Normal file
View file

@ -0,0 +1,141 @@
{
config,
pkgs-unstable,
pkgs-stable,
inputs,
...
}:
let
flakeDir = "${config.home.homeDirectory}/NixOS";
in
{
# Fix home-manager xresources bug: pkgs.xrdb doesn't exist, should be xorg.xrdb
nixpkgs.overlays = [ (final: prev: { xrdb = prev.xorg.xrdb; }) ];
# Disable xresources - Stylix enables it by default but we don't need it (ST uses compile-time colors)
stylix.targets.xresources.enable = false;
imports = [
./git.nix
./zed.nix
./zen.nix
./Rofi/rofi.nix
./KiCad/kicad.nix
# ./Hyprsuck/home.nix
./Suckless/home.nix # Uncomment for dwm (and comment Hyprsuck)
];
home.username = "lebowski";
home.homeDirectory = "/home/lebowski";
# Let Home Manager manage itself
programs.home-manager.enable = true;
# ============================================================================
# User Packages (stable by default, unstable for bleeding-edge tools)
# ============================================================================
home.packages = with pkgs-stable; [
# Media & Creative
haruna
krita
xournalpp
# Office & Productivity
libreoffice-fresh
# CAD & Manufacturing
freecad
bambu-studio
# Dev tools (unstable for latest features)
pkgs-unstable.claude-code
pkgs-unstable.hcloud
];
# ============================================================================
# GTK Icon Theme
# ============================================================================
gtk.iconTheme = {
package = pkgs-stable.papirus-icon-theme;
name = "Papirus-Dark";
};
gtk.gtk4.theme = null;
# ============================================================================
# Notifications (shared between Hyprland and dwm)
# ============================================================================
services.dunst = {
enable = true;
package = pkgs-stable.dunst;
settings.global.timeout = 3;
};
# ============================================================================
# Shell Aliases
# ============================================================================
home.shellAliases = {
# NixOS rebuild shortcuts
nrs = "sudo nixos-rebuild switch --flake ${flakeDir}";
nrb = "sudo nixos-rebuild boot --flake ${flakeDir}";
nrt = "sudo nixos-rebuild test --flake ${flakeDir}";
# Nix utilities
nfu = "nix flake update --flake ${flakeDir}";
ncg = "sudo nix-collect-garbage -d";
nso = "nix store optimise";
};
# ============================================================================
# Optional: Additional program configurations
# ============================================================================
# --- Bash shell ---
programs.bash = {
enable = true;
package = pkgs-stable.bash;
};
# --- KeePassXC ---
programs.keepassxc = {
enable = true;
package = pkgs-stable.keepassxc;
};
programs.foliate = {
enable = true;
package = pkgs-stable.foliate;
};
# --- SSH ---
programs.ssh = {
enable = true;
package = pkgs-stable.openssh;
matchBlocks = {
"*" = {
addKeysToAgent = "yes";
serverAliveInterval = 60;
controlMaster = "auto";
controlPersist = "10m";
};
"git.axiomania.org" = {
port = 2222;
};
"vps" = {
hostname = "178.104.15.221";
user = "root";
identityFile = "~/.ssh/id_ed25519";
};
};
};
# ============================================================================
# Disable version check - we intentionally use unstable home-manager with stable nixpkgs
# Note: This may cause issues if home-manager uses features not yet in stable
home.enableNixpkgsReleaseCheck = false;
home.stateVersion = "25.11";
}

181
ryzenadj.nix Normal file
View file

@ -0,0 +1,181 @@
# RyzenAdj power tuning configuration
# Provides power-tuning script for Minisforum V3 SE power profile management
{ pkgs, ... }:
let
# Power profiles (values in mW for power, °C for temp)
profiles = {
saver = {
stapm = 15000;
fast = 18000;
slow = 16000;
temp = 80;
};
balanced = {
stapm = 22500;
fast = 30000;
slow = 25000;
temp = 90;
};
performance = {
stapm = 30000;
fast = 40000;
slow = 35000;
temp = 100;
};
compile = {
stapm = 40000;
fast = 50000;
slow = 45000;
temp = 100;
};
};
configFile = pkgs.writeText "power-tuning-config.json" (builtins.toJSON profiles);
power-tuning = pkgs.writeShellScriptBin "power-tuning" ''
CONFIG_FILE="${configFile}"
COMPILE_FLAG="/tmp/.power-tuning-compile-mode"
# Read a profile from config
get_profile() {
local profile="$1"
${pkgs.jq}/bin/jq -r ".$profile" "$CONFIG_FILE"
}
# Apply ryzenadj settings
apply_settings() {
local profile="$1"
local settings
settings=$(get_profile "$profile")
if [[ "$settings" == "null" ]]; then
echo "Error: Profile '$profile' not found in config"
return 1
fi
local stapm fast slow temp
stapm=$(echo "$settings" | ${pkgs.jq}/bin/jq -r '.stapm')
fast=$(echo "$settings" | ${pkgs.jq}/bin/jq -r '.fast')
slow=$(echo "$settings" | ${pkgs.jq}/bin/jq -r '.slow')
temp=$(echo "$settings" | ${pkgs.jq}/bin/jq -r '.temp')
echo "Applying $profile: STAPM=''${stapm}mW Fast=''${fast}mW Slow=''${slow}mW Temp=''${temp}°C"
${pkgs.ryzenadj}/bin/ryzenadj \
--stapm-limit="$stapm" \
--fast-limit="$fast" \
--slow-limit="$slow" \
--tctl-temp="$temp" \
2>&1
}
# Get current power profile from power-profiles-daemon
get_current_ppd_profile() {
local profile
profile=$(${pkgs.power-profiles-daemon}/bin/powerprofilesctl get 2>/dev/null)
case "$profile" in
"power-saver") echo "saver" ;;
"balanced") echo "balanced" ;;
"performance") echo "performance" ;;
*) echo "balanced" ;;
esac
}
# Check if compile mode is active
is_compile_mode() {
[[ -f "$COMPILE_FLAG" ]]
}
# Main command handling
case "''${1:-apply}" in
apply)
if is_compile_mode; then
echo "Compile mode active, using compile profile"
apply_settings "compile"
else
profile=$(get_current_ppd_profile)
echo "Current PPD profile: $profile"
apply_settings "$profile"
fi
;;
compile-on)
touch "$COMPILE_FLAG"
echo "Compile mode enabled"
apply_settings "compile"
;;
compile-off)
rm -f "$COMPILE_FLAG"
echo "Compile mode disabled"
profile=$(get_current_ppd_profile)
apply_settings "$profile"
;;
status)
echo "=== Power Tuning Status ==="
echo "Config file: $CONFIG_FILE"
echo "PPD Profile: $(${pkgs.power-profiles-daemon}/bin/powerprofilesctl get 2>/dev/null || echo 'unknown')"
echo "Compile mode: $(is_compile_mode && echo 'ON' || echo 'OFF')"
echo ""
echo "Current presets:"
${pkgs.jq}/bin/jq '.' "$CONFIG_FILE"
;;
*)
echo "Usage: power-tuning {apply|compile-on|compile-off|status}"
exit 1
;;
esac
'';
in
{
# Kernel module for SMU access
hardware.cpu.amd.ryzen-smu.enable = true;
# Permissions for ryzenadj to access SMU
systemd.tmpfiles.rules = [
"z /sys/kernel/ryzen_smu_drv/smn 0660 root wheel -"
"z /sys/kernel/ryzen_smu_drv/smu_args 0660 root wheel -"
"z /sys/kernel/ryzen_smu_drv/mp1_smu_cmd 0660 root wheel -"
"z /sys/kernel/ryzen_smu_drv/rsmu_cmd 0660 root wheel -"
];
environment.systemPackages = [
power-tuning
pkgs.ryzenadj
];
# Apply power tuning on boot and when power profile changes
systemd.services.power-tuning = {
description = "Apply RyzenAdj power tuning";
wantedBy = [ "multi-user.target" ];
after = [ "power-profiles-daemon.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${power-tuning}/bin/power-tuning apply";
};
};
# Re-apply when power profile changes
systemd.services.power-tuning-on-profile-change = {
description = "Apply RyzenAdj on power profile change";
after = [ "power-profiles-daemon.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${power-tuning}/bin/power-tuning apply";
};
};
# Path trigger to watch for profile changes
systemd.paths.power-tuning-watch = {
description = "Watch for power profile changes";
wantedBy = [ "multi-user.target" ];
pathConfig = {
PathChanged = "/sys/firmware/acpi/platform_profile";
Unit = "power-tuning-on-profile-change.service";
};
};
}

53
stylix.nix Normal file
View file

@ -0,0 +1,53 @@
{ pkgs, ... }:
{
stylix = {
enable = true;
enableReleaseChecks = false;
polarity = "dark";
# Catppuccin Mocha theme
base16Scheme = "${pkgs.base16-schemes}/share/themes/catppuccin-mocha.yaml";
# Wallpaper (used for color scheme generation)
image = ./Wallpapers/siege.png;
# Cursor
cursor = {
package = pkgs.bibata-cursors;
name = "Bibata-Modern-Ice";
size = 20;
};
# Fonts
fonts = {
monospace = {
package = pkgs.nerd-fonts.jetbrains-mono;
name = "JetBrainsMono Nerd Font";
};
sansSerif = {
package = pkgs.dejavu_fonts;
name = "DejaVu Sans";
};
serif = {
package = pkgs.dejavu_fonts;
name = "DejaVu Serif";
};
emoji = {
package = pkgs.noto-fonts-color-emoji;
name = "Noto Color Emoji";
};
sizes = {
terminal = 11;
applications = 11;
desktop = 11;
};
};
# Opacity settings
opacity = {
terminal = 0.9;
};
};
}

19
virtualisation.nix Normal file
View file

@ -0,0 +1,19 @@
{ config, pkgs, ... }:
{
# Libvirt/QEMU virtualisation
programs.virt-manager.enable = true;
users.groups.libvirtd.members = [ "lebowski" ];
virtualisation.libvirtd.enable = true;
virtualisation.spiceUSBRedirection.enable = true;
# Docker
virtualisation.docker = {
enable = true;
# Use the rootless mode - run Docker daemon as non-root user
rootless = {
enable = true;
setSocketVariable = true;
};
};
}

73
zed.nix Normal file
View file

@ -0,0 +1,73 @@
# Zed Editor configuration
{ pkgs-unstable, ... }:
{
programs.zed-editor = {
enable = true;
package = pkgs-unstable.zed-editor;
# This populates the userSettings "auto_install_extensions"
extensions = [
"nix"
"odin"
"astro"
"slint"
"dockerfile"
"docker-compose"
"latex"
"make"
"log"
"csv"
];
# Everything inside of these brackets are Zed options
userSettings = {
# Disable all AI features (including Claude Code ACP agents)
disable_ai = true;
# Disable collaboration/AI features
assistant = { enabled = false; };
collaboration_panel = { button = false; };
chat_panel = { button = false; };
notification_panel = { button = false; };
title_bar = { show_sign_in = false; };
# Hide UI panels
diagnostics = { button = false; };
debugger = { button = false; };
# Disable edit predictions completely
features = { edit_prediction_provider = "none"; };
show_edit_predictions = false;
hour_format = "hour24";
auto_update = false;
terminal = {
alternate_scroll = "off";
blinking = "off";
copy_on_select = true;
dock = "right";
line_height = "comfortable";
shell = "system";
toolbar = {
title = true; # Shows terminal title (e.g. "zsh" or running command) in panel header
};
working_directory = "current_project_directory";
};
vim_mode = false;
base_keymap = "VSCode";
show_whitespaces = "selection";
# Disable all language servers globally
enable_language_server = false;
# Disable Claude Code integration (prevents auto-launching claude-code-acp)
context_servers = { };
};
};
}

322
zen.nix Normal file
View file

@ -0,0 +1,322 @@
{ config, lib, pkgs-unstable, inputs, ... }:
{
# Workaround for https://github.com/0xc000022070/zen-browser-flake/issues/285
# The twilight module generates profiles.ini as a read-only nix store symlink,
# which Zen can't write to, so it ignores it and creates a random-prefix profile.
# This replaces the symlink with a mutable copy.
home.activation.fixZenProfiles = lib.hm.dag.entryAfter ["linkGeneration"] ''
ZEN_DIR="$HOME/.zen"
if [ -L "$ZEN_DIR/profiles.ini" ]; then
REAL=$(readlink -f "$ZEN_DIR/profiles.ini")
rm "$ZEN_DIR/profiles.ini"
cp "$REAL" "$ZEN_DIR/profiles.ini"
chmod u+w "$ZEN_DIR/profiles.ini"
fi
'';
# Tell Stylix which Zen profiles to theme
stylix.targets.zen-browser.profileNames = [ "default" ];
imports = [
inputs.zen-browser.homeModules.twilight
];
# Set Zen as default browser for all relevant MIME types
xdg.mimeApps = let
associations = builtins.listToAttrs (map (name: {
inherit name;
value = let
zen-browser = config.programs.zen-browser.package;
in
zen-browser.meta.desktopFileName;
}) [
"application/x-extension-shtml"
"application/x-extension-xhtml"
"application/x-extension-html"
"application/x-extension-xht"
"application/x-extension-htm"
"x-scheme-handler/unknown"
"x-scheme-handler/mailto"
"x-scheme-handler/chrome"
"x-scheme-handler/about"
"x-scheme-handler/https"
"x-scheme-handler/http"
"application/xhtml+xml"
"application/json"
"text/plain"
"text/html"
]);
in {
associations.added = associations;
defaultApplications = associations;
};
programs.zen-browser = {
enable = true;
# Native Messaging Hosts - required for KeePassXC browser integration
nativeMessagingHosts = [ pkgs-unstable.keepassxc ];
policies = let
# Helper to lock preferences
mkLockedAttrs = builtins.mapAttrs (_: value: {
Value = value;
Status = "locked";
});
# Helper to create extension install URL
mkPluginUrl = id: "https://addons.mozilla.org/firefox/downloads/latest/${id}/latest.xpi";
# Helper to create extension entry with options
mkExtensionEntry = {
id,
pinned ? false,
private_browsing ? true,
}: let
base = {
install_url = mkPluginUrl id;
installation_mode = "force_installed";
inherit private_browsing;
};
in
if pinned
then base // { default_area = "navbar"; }
else base;
# Helper to process extension settings
mkExtensionSettings = builtins.mapAttrs (_: entry:
if builtins.isAttrs entry
then entry
else mkExtensionEntry { id = entry; });
in {
# App behavior
DisableAppUpdate = true;
DontCheckDefaultBrowser = true;
DisableFeedbackCommands = true;
# Privacy & Telemetry
DisableTelemetry = true;
DisableFirefoxStudies = true;
DisablePocket = true;
# Autofill
AutofillAddressEnabled = true;
AutofillCreditCardEnabled = false;
OfferToSaveLogins = false;
# Tracking Protection
EnableTrackingProtection = {
Value = true;
Locked = true;
Cryptomining = true;
Fingerprinting = true;
};
# Clean up on shutdown (fresh instance)
SanitizeOnShutdown = {
Cache = true;
Cookies = false; # Keep logins
Downloads = true;
FormData = true;
History = false; # Keep history
Sessions = false; # Keep tabs on restart
SiteSettings = false;
OfflineApps = true;
Locked = true;
};
# Extensions
ExtensionSettings = mkExtensionSettings {
# uBlock Origin - ad blocker (pinned to navbar)
"uBlock0@raymondhill.net" = mkExtensionEntry {
id = "ublock-origin";
pinned = true;
private_browsing = true;
};
# KeePassXC - password manager (pinned to navbar)
"keepassxc-browser@keepassxc.org" = mkExtensionEntry {
id = "keepassxc-browser";
pinned = true;
private_browsing = true;
};
# --- Privacy & Security ---
"{74145f27-f039-47ce-a470-a662b129930a}" = "clearurls"; # Remove tracking from URLs
"jid1-BoFifL9Vbdl2zQ@jetpack" = "decentraleyes"; # Local CDN emulation
"{3579f63b-d8ee-424f-bbb6-6d0ce3285e6a}" = "chameleon-ext"; # Spoof browser profile
# --- GitHub ---
"{a4c4eda4-fb84-4a84-b4a1-f7c1cbf2a1ad}" = "refined-github-"; # GitHub enhancements
"{85860b32-02a8-431a-b2b1-40fbd64c9c69}" = "github-file-icons"; # File icons in GitHub
"github-repository-size@pranavmangal" = "gh-repo-size"; # Show repo size
# --- YouTube ---
"{762f9885-5a13-4abd-9c77-433dcd38b8fd}" = "return-youtube-dislikes";
};
# Locked preferences
Preferences = mkLockedAttrs {
"browser.aboutConfig.showWarning" = false;
"browser.tabs.warnOnClose" = false;
"media.videocontrols.picture-in-picture.video-toggle.enabled" = true;
# Translation (auto-translate all languages except en/fr)
"browser.translations.enable" = true;
"browser.translations.panelShown" = true;
"browser.translations.automaticallyPopup" = true;
"browser.translations.alwaysTranslateLanguages" = "ar,bg,cs,da,de,el,es,et,fi,hu,id,it,ja,ko,lt,lv,nl,no,pl,pt,ro,ru,sk,sl,sv,th,tr,uk,vi,zh";
"browser.translations.neverTranslateLanguages" = "en,fr";
# Privacy (balanced - removed slow options)
"privacy.spoof_english" = 1;
"network.cookie.cookieBehavior" = 5;
"dom.battery.enabled" = false;
# Performance & Speed
"gfx.webrender.all" = true;
"layers.acceleration.force-enabled" = true;
"network.http.http3.enabled" = true;
"network.dns.disablePrefetch" = false;
"network.prefetch-next" = true;
"browser.sessionstore.interval" = 60000; # Less frequent session saves (60s)
"browser.cache.memory.enable" = true;
"browser.cache.memory.capacity" = 524288; # 512MB memory cache
"content.notify.interval" = 100000;
"ui.submenuDelay" = 0; # Instant submenus
# Disable animations for snappiness
"toolkit.cosmeticAnimations.enabled" = false;
"browser.fullscreen.animate" = false;
};
};
profiles.default = {
# Zen-specific settings
settings = {
"zen.workspaces.continue-where-left-off" = true; # Restore tabs
"zen.workspaces.natural-scroll" = true;
"zen.view.compact.hide-tabbar" = true;
"zen.view.compact.hide-toolbar" = true;
"zen.view.compact.animate-sidebar" = false;
"zen.welcome-screen.seen" = true;
"zen.urlbar.behavior" = "float";
# Restore previous session
"browser.startup.page" = 3; # Restore previous session
"browser.sessionstore.resume_from_crash" = true;
};
# --- Bookmarks ---
bookmarks = {
force = true;
settings = [
{
name = "Dev";
toolbar = true;
bookmarks = [
{ name = "GitHub"; url = "https://github.com"; }
{ name = "NixOS Wiki"; url = "https://wiki.nixos.org/"; }
];
}
];
};
# --- Containers ---
# containersForce = true;
# containers = {
# Personal = { color = "blue"; icon = "fingerprint"; id = 1; };
# Work = { color = "orange"; icon = "briefcase"; id = 2; };
# Shopping = { color = "yellow"; icon = "dollar"; id = 3; };
# Banking = { color = "green"; icon = "dollar"; id = 4; };
# };
# --- Workspaces/Spaces ---
# spacesForce = true;
# spaces = {
# "Default" = {
# id = "generate-your-own-uuid"; # Use: uuidgen
# icon = "🏠";
# position = 1000;
# };
# "Work" = {
# id = "generate-your-own-uuid";
# icon = "💼";
# position = 1001;
# # container = containers."Work".id; # Link to container
# theme = {
# type = "gradient";
# colors = [
# {
# red = 100;
# green = 150;
# blue = 200;
# algorithm = "floating";
# type = "explicit-lightness";
# }
# ];
# opacity = 0.5;
# texture = 0.3;
# };
# };
# };
# --- Pinned tabs ---
# pinsForce = true;
# pins = {
# "GitHub" = {
# id = "generate-your-own-uuid";
# # workspace = spaces."Work".id;
# url = "https://github.com";
# position = 100;
# isEssential = false;
# };
# };
# --- Search engines ---
search = {
force = true;
default = "google";
engines = let
nixIcon = "${pkgs-unstable.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
in {
"Nix Packages" = {
urls = [{
template = "https://search.nixos.org/packages";
params = [
{ name = "type"; value = "packages"; }
{ name = "channel"; value = "unstable"; }
{ name = "query"; value = "{searchTerms}"; }
];
}];
icon = nixIcon;
definedAliases = [ "np" ];
};
"Nix Options" = {
urls = [{
template = "https://search.nixos.org/options";
params = [
{ name = "channel"; value = "unstable"; }
{ name = "query"; value = "{searchTerms}"; }
];
}];
icon = nixIcon;
definedAliases = [ "no" ];
};
"Home Manager Options" = {
urls = [{
template = "https://home-manager-options.extranix.com/";
params = [
{ name = "query"; value = "{searchTerms}"; }
{ name = "release"; value = "master"; }
];
}];
icon = nixIcon;
definedAliases = [ "hm" ];
};
# Hide unwanted default engines
"bing".metaData.hidden = true;
};
};
};
};
}