rework: more TUIs! (#62)

Signed-off-by: Hadi <[email protected]>
This commit is contained in:
Hadi
2026-09-28 20:18:12 +02:00
committed by GitHub
parent 1a3f23f248
commit 07a60e91d2
98 changed files with 1280 additions and 1093 deletions
Generated
+122 -7
View File
@@ -93,6 +93,21 @@
"type": "github" "type": "github"
} }
}, },
"crane": {
"locked": {
"lastModified": 1789760033,
"narHash": "sha256-jtT4yxZpR8seYnlCMMWSSPlFN92zO6ICuZ8pJrmi86k=",
"owner": "ipetkov",
"repo": "crane",
"rev": "73b980519cefc727a5f6cc8e5c0947a2f9be6edd",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"default-creds": { "default-creds": {
"flake": false, "flake": false,
"locked": { "locked": {
@@ -181,6 +196,22 @@
} }
}, },
"flake-compat_2": { "flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_3": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1777699697, "lastModified": 1777699697,
@@ -406,6 +437,29 @@
"type": "github" "type": "github"
} }
}, },
"lanzaboote": {
"inputs": {
"crane": "crane",
"nixpkgs": [
"nixpkgs"
],
"pre-commit": "pre-commit",
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1789982869,
"narHash": "sha256-o72O4Y6YZ2q6SkgqU8krWJomtVW7gsTAVt1eAv8bebU=",
"owner": "nix-community",
"repo": "lanzaboote",
"rev": "074c4938b829e745c878872fc165eea3a6b27399",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "lanzaboote",
"type": "github"
}
},
"mnw": { "mnw": {
"locked": { "locked": {
"lastModified": 1780772958, "lastModified": 1780772958,
@@ -513,11 +567,11 @@
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1787135253, "lastModified": 1790046670,
"narHash": "sha256-RD2kNWCG+Bjo6h+JVjWVNntZs2GtRoeY2xHjts/FNkA=", "narHash": "sha256-MYiI+CzL0tuWgRPjGsKCDHqYs2T3OzMlMQWOYWG0qso=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "ffb3c9b700e759be2ef13237c9d8f953b32a1e46", "rev": "6774f7bc253789b113a4f39285dc0fa100abeacc",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -649,7 +703,7 @@
}, },
"nvf": { "nvf": {
"inputs": { "inputs": {
"flake-compat": "flake-compat_2", "flake-compat": "flake-compat_3",
"mnw": "mnw", "mnw": "mnw",
"nixpkgs": "nixpkgs_6" "nixpkgs": "nixpkgs_6"
}, },
@@ -677,15 +731,37 @@
] ]
}, },
"locked": { "locked": {
"path": "./home/programs/tui/nvf", "path": "./home/nvf",
"type": "path" "type": "path"
}, },
"original": { "original": {
"path": "./home/programs/tui/nvf", "path": "./home/nvf",
"type": "path" "type": "path"
}, },
"parent": [] "parent": []
}, },
"pre-commit": {
"inputs": {
"flake-compat": "flake-compat_2",
"nixpkgs": [
"lanzaboote",
"nixpkgs"
]
},
"locked": {
"lastModified": 1789514889,
"narHash": "sha256-PFD1nxptCXJyJoiYO1gf/5Vv43yqGpHtMC6LUhl9/pQ=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "59f4ca0d063a1a3ec722c88b51a33004862e5379",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"default-creds": "default-creds", "default-creds": "default-creds",
@@ -695,6 +771,7 @@
"helium-browser": "helium-browser", "helium-browser": "helium-browser",
"home-manager": "home-manager", "home-manager": "home-manager",
"impermanence": "impermanence", "impermanence": "impermanence",
"lanzaboote": "lanzaboote",
"nix-index-database": "nix-index-database", "nix-index-database": "nix-index-database",
"nixarr": "nixarr", "nixarr": "nixarr",
"nixos-hardware": "nixos-hardware", "nixos-hardware": "nixos-hardware",
@@ -704,7 +781,29 @@
"nvf": "nvf", "nvf": "nvf",
"nvf-config": "nvf-config", "nvf-config": "nvf-config",
"sops-nix": "sops-nix", "sops-nix": "sops-nix",
"stylix": "stylix" "stylix": "stylix",
"tldr-pages-src": "tldr-pages-src"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"lanzaboote",
"nixpkgs"
]
},
"locked": {
"lastModified": 1789889921,
"narHash": "sha256-aL/ogiN7qZCGeNovS1f9hX73jwYIKb4Pcq6AZm57WtY=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "1fb104a12a8667045559b2575d6d448ae2fbd99b",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
} }
}, },
"sops-nix": { "sops-nix": {
@@ -855,6 +954,22 @@
"type": "github" "type": "github"
} }
}, },
"tldr-pages-src": {
"flake": false,
"locked": {
"lastModified": 1789901872,
"narHash": "sha256-UQoJuW4895ocBz/vMPBO7Vdr6X5AgtEurackYF1titY=",
"owner": "tldr-pages",
"repo": "tldr",
"rev": "615ccdb9d0323c13a2387693c8b0de8106042f4a",
"type": "github"
},
"original": {
"owner": "tldr-pages",
"repo": "tldr",
"type": "github"
}
},
"treefmt-nix": { "treefmt-nix": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
+11 -4
View File
@@ -1,8 +1,7 @@
{ {
# https://github.com/anotherhadi/nixy # https://github.com/anotherhadi/nixy
description = '' description = ''
Nixy simplifies and unifies the Hyprland ecosystem with a modular, easily customizable setup. Nixy simplifies and unifies the Hyprland ecosystem with a minimalist & easily customizable NixOS setup, heavily focused on TUIs.
It provides a structured way to manage your system configuration and dotfiles with minimal effort.
''; '';
inputs = { inputs = {
@@ -11,7 +10,7 @@
nixos-hardware.url = "github:NixOS/nixos-hardware"; nixos-hardware.url = "github:NixOS/nixos-hardware";
nvf.url = "github:notashelf/nvf"; nvf.url = "github:notashelf/nvf";
nvf-config = { nvf-config = {
url = "path:./home/programs/tui/nvf"; url = "path:./home/nvf";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
inputs.nvf.follows = "nvf"; inputs.nvf.follows = "nvf";
}; };
@@ -40,6 +39,10 @@
url = "github:nix-community/disko"; url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
lanzaboote = {
url = "github:nix-community/lanzaboote";
inputs.nixpkgs.follows = "nixpkgs";
};
helium-browser = { helium-browser = {
url = "github:oxcl/nix-flake-helium-browser"; url = "github:oxcl/nix-flake-helium-browser";
inputs.nixpkgs.follows = "nixpkgs-unstable"; inputs.nixpkgs.follows = "nixpkgs-unstable";
@@ -52,6 +55,10 @@
url = "github:anotherhadi/default-creds"; url = "github:anotherhadi/default-creds";
flake = false; flake = false;
}; };
tldr-pages-src = {
url = "github:tldr-pages/tldr";
flake = false;
};
# Server # Server
nixarr.url = "github:nix-media-server/nixarr"; nixarr.url = "github:nix-media-server/nixarr";
@@ -87,7 +94,7 @@
(system: f system (import nixpkgs {inherit system;})); (system: f system (import nixpkgs {inherit system;}));
in in
merge [ merge [
(import ./home/programs/tui/nixy/flake.nix args) (import ./home/tui/nixy/flake.nix args)
{ {
formatter.${system} = pkgs.alejandra; formatter.${system} = pkgs.alejandra;
packages.${system}.nvim = inputs.nvf-config.packages.${system}.nvim; packages.${system}.nvim = inputs.nvf-config.packages.${system}.nvim;
@@ -7,10 +7,6 @@
name = "Nix 4 Cyber"; name = "Nix 4 Cyber";
url = "https://n4c.hadi.icu"; url = "https://n4c.hadi.icu";
} }
{
name = "Infosec Notes - Anotherhadi";
url = "https://hadi.icu/notes";
}
{ {
name = "Cyberchef"; name = "Cyberchef";
url = "https://cyberchef.hadi.icu"; url = "https://cyberchef.hadi.icu";
@@ -27,10 +23,6 @@
name = "Hack The Box"; name = "Hack The Box";
url = "https://hackthebox.com"; url = "https://hackthebox.com";
} }
{
name = "Iknowyou";
url = "https://iknowyou-prod.hadi.icu";
}
{ {
name = "Exploit-DB"; name = "Exploit-DB";
url = "https://exploit-db.com"; url = "https://exploit-db.com";
@@ -15,14 +15,6 @@
name = "Gitea"; name = "Gitea";
url = "https://git.hadi.icu"; url = "https://git.hadi.icu";
} }
{
name = "Mealie";
url = "https://mealie.hadi.icu";
}
{
name = "Wallpapers";
url = "https://wallpapers.hadi.icu";
}
{ {
name = "Default Creds"; name = "Default Creds";
url = "https://default-creds.hadi.icu"; url = "https://default-creds.hadi.icu";
@@ -61,10 +53,6 @@
name = "AdGuard"; name = "AdGuard";
url = "https://adguard.hadi.icu"; url = "https://adguard.hadi.icu";
} }
{
name = "Umami";
url = "https://umami.hadi.icu";
}
{ {
name = "Cloudflare"; name = "Cloudflare";
url = "https://dash.cloudflare.com"; url = "https://dash.cloudflare.com";
@@ -39,11 +39,7 @@
} }
{ {
name = "Startpage Config"; name = "Startpage Config";
url = "https://www.startpage.com/do/mypage.pl?prfe=45d331deb05471d659dba933e7400df51d952bb103da6f6125c0e769a6be1d65610456a479f495ceeee7e97311cf227d7c1bb198de0ceeb193d8cddf9c455c19a409cc35c3e3f542ee27bd7cecd3"; url = "https://www.startpage.com/do/mypage.pl?prfe=2ca22738c4725515827ee9783be629ea1c26114819ab4a8c32573d96b3fc98540c50f251d1613da751834e14a9f50c7913eb528705c8ea7f067899ddd18e2d18f6f382cada947f7095cb9bead84dbb1aa455";
}
{
name = "Feedly";
url = "https://feedly.com";
} }
{ {
name = "MyNixOS"; name = "MyNixOS";
-36
View File
@@ -1,36 +0,0 @@
{
pkgs,
config,
lib,
...
}: {
home.packages = with pkgs; [
proton-pass
];
# Fix Proton Authenticator desktop entry
xdg.desktopEntries = {
"Proton Calendar" = {
name = "Proton Calendar";
exec = ''${config.programs.helium.package}/bin/helium "https://calendar.proton.me"'';
icon = "proton-calendar";
type = "Application";
categories = ["Utility"];
terminal = false;
};
"Proton Mail" = {
name = "Proton Mail";
exec = ''${config.programs.helium.package}/bin/helium "https://mail.proton.me/"'';
icon = "proton-mail";
type = "Application";
categories = ["Utility"];
terminal = false;
};
};
home.persistence."/persist" = lib.mkIf (config.var.impermanenceEnabled or false) {
directories = [
".config/Proton Pass"
];
};
}
-52
View File
@@ -1,52 +0,0 @@
# Git configuration
{config, ...}: let
username = config.var.git.username;
email = config.var.git.email;
in {
programs.git = {
enable = true;
ignores = [
".cache/"
".DS_Store"
".idea/"
"*.swp"
"*.elc"
"auto-save-list"
".direnv/"
"node_modules"
"result"
"result-*"
".superpowers/"
".claude/"
"docs/superpowers/"
];
settings = {
user.name = username;
user.email = email;
init.defaultBranch = "main";
pull.rebase = "true";
push.autoSetupRemote = true;
color.ui = "1";
alias = {
co = "checkout";
fuck = "commit --amend -m";
c = "commit -m";
ca = "commit -am";
forgor = "commit --amend --no-edit";
graph = "log --all --decorate --graph --oneline";
oops = "checkout --";
l = "log";
r = "rebase";
s = "status --short";
ss = "status";
d = "diff";
ps = "!git push origin $(git rev-parse --abbrev-ref HEAD)";
pl = "!git pull origin $(git rev-parse --abbrev-ref HEAD)";
st = "status";
br = "branch";
hist = ''log --pretty=format:"%Cgreen%h %Creset%cd %Cblue[%cn] %Creset%s%C(yellow)%d%C(reset)" --graph --date=relative --decorate --all'';
llog = ''log --graph --name-status --pretty=format:"%C(red)%h %C(reset)(%cd) %C(green)%an %Creset%s %C(yellow)%d%Creset" --date=relative'';
};
};
};
}
-49
View File
@@ -1,49 +0,0 @@
{
pkgs,
lib,
...
}: let
# wikiman ships without the ArchWiki/tldr-pages databases (too heavy to
# bundle). These are fetched declaratively from wikiman's own release
# snapshots and baked into a wrapped `wikiman` so `-S` shows them as
# installed offline, no manual `make source-*` step required.
# To refresh: bump the release tag/date below and update the hashes
# (`nix hash file --sri <downloaded file>`).
#
# Download size (compressed): ~17M ArchWiki + ~3.4M tldr-pages.
# Store size (unpacked): ~239M ArchWiki + ~147M tldr-pages (~385M total).
archWikiSrc = pkgs.fetchurl {
url = "https://github.com/filiparag/wikiman/releases/download/2.14.1/arch-wiki_20260810.source.tar.xz";
hash = "sha256-82aiDvwZ07m9CfBVYIDLSUpWds4QxUbCbCPmQ26j1XI=";
};
tldrPagesSrc = pkgs.fetchurl {
url = "https://github.com/filiparag/wikiman/releases/download/2.14.1/tldr-pages_20260810.source.tar.xz";
hash = "sha256-2vdszVMImSN1UF+OepKdj1v+lsG2jM68gcpnUJyqddw=";
};
# Real files (not symlinks): wikiman's source-detection does `find -type f`
# on this dir, which doesn't match symlinks, so `pkgs.symlinkJoin` alone
# won't work here.
data = pkgs.runCommand "wikiman-data" {nativeBuildInputs = [pkgs.xz];} ''
mkdir -p $out
cp -r ${pkgs.wikiman}/share $out/share
chmod -R u+w $out/share
tar xf ${archWikiSrc} -C $out --strip-components=1
tar xf ${tldrPagesSrc} -C $out --strip-components=1
'';
wikiman-full = pkgs.symlinkJoin {
name = "wikiman-full";
paths = [pkgs.wikiman];
nativeBuildInputs = [pkgs.makeWrapper];
postBuild = ''
rm $out/bin/wikiman
makeWrapper ${pkgs.wikiman}/bin/.wikiman-wrapped $out/bin/wikiman \
--prefix PATH : "${lib.makeBinPath [pkgs.fzf pkgs.ripgrep pkgs.gawk pkgs.w3m pkgs.coreutils pkgs.parallel]}" \
--set conf_sys_usr "${data}"
'';
meta.mainProgram = "wikiman";
};
in {
home.packages = [wikiman-full]; # Offline search engine for manual pages, ArchWiki and tldr
}
+13 -2
View File
@@ -3,8 +3,19 @@
pkgs, pkgs,
scripts, scripts,
... ...
}: { }: let
home.packages = [pkgs.wl-clipboard]; tofiMenu = "${pkgs.tofi}/bin/tofi --horizontal false --anchor center --width 700 --height 500 --margin-top 0 --margin-left 0 --margin-right 0 --num-results 10";
in {
scripts.clipboard-menu = pkgs.writeShellScriptBin "clipboard-menu" ''
list=$(${pkgs.cliphist}/bin/cliphist list)
selected=$(printf '%s\n' "$list" | cut -f2- | ${tofiMenu} --prompt-text "Clipboard: ")
[ -n "$selected" ] && printf '%s\n' "$list" \
| awk -F'\t' -v s="$selected" '$2 == s {print; exit}' \
| ${pkgs.cliphist}/bin/cliphist decode \
| ${pkgs.wl-clipboard}/bin/wl-copy
'';
home.packages = [pkgs.wl-clipboard scripts.clipboard-menu];
services.cliphist = { services.cliphist = {
enable = true; enable = true;
+32 -2
View File
@@ -1,6 +1,24 @@
# hypridle handles idle management: lock the screen, turn the display off, and # hypridle handles idle management: lock the screen, turn the display off, and
# suspend after periods of inactivity. The `caffeine-toggle` script pauses it. # suspend after periods of inactivity. The `caffeine-toggle` script pauses it.
{pkgs, ...}: { {
pkgs,
scripts,
...
}: {
scripts.caffeine-toggle = pkgs.writeShellScriptBin "caffeine-toggle" ''
# Pause hypridle (stay awake) or resume it.
if systemctl --user is-active --quiet hypridle; then
systemctl --user stop hypridle
OSD_TEXT="󰅶 Keep Awake On"
else
systemctl --user start hypridle
OSD_TEXT="󰾫 Keep Awake Off"
fi
${scripts.waybar-osd}/bin/waybar-osd "$OSD_TEXT"
'';
home.packages = [scripts.caffeine-toggle];
services.hypridle = { services.hypridle = {
enable = true; enable = true;
settings = { settings = {
@@ -17,7 +35,7 @@
on-timeout = "loginctl lock-session"; on-timeout = "loginctl lock-session";
} }
{ {
timeout = 360; # 6 min → screen off timeout = 720; # 12 min → screen off
on-timeout = "${pkgs.hyprland}/bin/hyprctl dispatch dpms off"; on-timeout = "${pkgs.hyprland}/bin/hyprctl dispatch dpms off";
on-resume = "${pkgs.hyprland}/bin/hyprctl dispatch dpms on"; on-resume = "${pkgs.hyprland}/bin/hyprctl dispatch dpms on";
} }
@@ -28,4 +46,16 @@
]; ];
}; };
}; };
xdg.desktopEntries = {
caffeine-toggle = {
name = "Keep Awake";
exec = "${scripts.caffeine-toggle}/bin/caffeine-toggle";
icon = "my-caffeine-on-symbolic";
comment = "Pause or resume idle locking and suspend";
categories = ["System"];
terminal = false;
settings.Keywords = "caffeine;idle;awake;inhibit;suspend;";
};
};
} }
+6 -63
View File
@@ -46,63 +46,6 @@ in {
bind = bind =
[ [
# Applications
(
"$shiftMod, A, exec, "
+ lib.getExe (mkMenu [
{
key = "a";
desc = "Proton Authenticator";
cmd = "env WEBKIT_DISABLE_COMPOSITING_MODE=1 ${pkgs.proton-authenticator}/bin/proton-authenticator";
}
{
key = "p";
desc = "Proton Pass";
cmd = "${pkgs.proton-pass}/bin/proton-pass";
}
{
key = "v";
desc = "Proton VPN";
cmd = "${pkgs.proton-vpn}/bin/protonvpn-app";
}
{
key = "c";
desc = "Proton Calendar";
cmd = "${config.programs.helium.package}/bin/helium 'https://calendar.proton.me/'";
}
{
key = "m";
desc = "Proton Mail";
cmd = "${config.programs.helium.package}/bin/helium 'https://mail.proton.me/'";
}
{
key = "o";
desc = "Obsidian";
cmd = "${pkgs.obsidian}/bin/obsidian";
}
{
key = "s";
desc = "Signal";
cmd = "${pkgs.signal-desktop}/bin/signal-desktop";
}
{
key = "t";
desc = "TickTick";
cmd = "${pkgs.ticktick}/bin/ticktick";
}
{
key = "b";
desc = "Helium";
cmd = "${config.programs.helium.package}/bin/helium";
}
{
key = "i";
desc = "Helium (Incognito)";
cmd = "${config.programs.helium.package}/bin/helium --incognito";
}
])
)
"$mod,B, exec, uwsm app -- ${config.programs.helium.package}/bin/helium" # Browser "$mod,B, exec, uwsm app -- ${config.programs.helium.package}/bin/helium" # Browser
# Power # Power
@@ -112,22 +55,22 @@ in {
{ {
key = "l"; key = "l";
desc = "Lock"; desc = "Lock";
cmd = "${pkgs.hyprlock}/bin/hyprlock"; cmd = config.xdg.desktopEntries.lock.exec;
} }
{ {
key = "s"; key = "s";
desc = "Suspend"; desc = "Suspend";
cmd = "systemctl suspend"; cmd = config.xdg.desktopEntries.suspend.exec;
} }
{ {
key = "r"; key = "r";
desc = "Reboot"; desc = "Reboot";
cmd = "systemctl reboot"; cmd = config.xdg.desktopEntries.reboot.exec;
} }
{ {
key = "p"; key = "p";
desc = "Power Off"; desc = "Power Off";
cmd = "systemctl poweroff"; cmd = config.xdg.desktopEntries.shutdown.exec;
} }
]) ])
) )
@@ -159,8 +102,8 @@ in {
"$shiftMod, S, movetoworkspace, special:scratch" # Move to scratch workspace "$shiftMod, S, movetoworkspace, special:scratch" # Move to scratch workspace
# Utilities # Utilities
", Print, exec, ${pkgs.hyprshot}/bin/hyprshot -m region" # Capture region ", Print, exec, ${config.xdg.desktopEntries.screenshot-region.exec}" # Capture region
"$shiftMod, Print, exec, ${pkgs.hyprshot}/bin/hyprshot -m output" # Capture screen "$shiftMod, Print, exec, ${config.xdg.desktopEntries.screenshot-screen.exec}" # Capture screen
] ]
++ (builtins.concatLists ( ++ (builtins.concatLists (
builtins.genList ( builtins.genList (
+1
View File
@@ -21,6 +21,7 @@ in {
./bindings.nix ./bindings.nix
./polkitagent.nix ./polkitagent.nix
./hyprpaper.nix ./hyprpaper.nix
./scripts.nix
]; ];
home.packages = home.packages =
+61
View File
@@ -0,0 +1,61 @@
# Compositor-level scripts: focus mode (temporarily flattens Hyprland's
# animations/gaps/decoration) and night shift (hyprsunset).
{
pkgs,
scripts,
...
}: {
scripts.focus-toggle = pkgs.writeShellScriptBin "focus-toggle" ''
if test -f "$XDG_RUNTIME_DIR/hypr-focus-mode"; then
rm "$XDG_RUNTIME_DIR/hypr-focus-mode"
OSD_TEXT="󰈈 Focus Off"
${scripts.waybar-osd}/bin/waybar-osd "$OSD_TEXT"
${pkgs.hyprland}/bin/hyprctl reload
${pkgs.hyprland}/bin/hyprctl dispatch exec waybar
else
touch "$XDG_RUNTIME_DIR/hypr-focus-mode"
OSD_TEXT="󰈈 Focus On"
${scripts.waybar-osd}/bin/waybar-osd "$OSD_TEXT"
${pkgs.procps}/bin/pkill waybar || true
${pkgs.hyprland}/bin/hyprctl keyword animations:enabled false
${pkgs.hyprland}/bin/hyprctl keyword general:gaps_in 0
${pkgs.hyprland}/bin/hyprctl keyword general:gaps_out 0
${pkgs.hyprland}/bin/hyprctl keyword decoration:active_opacity 1
${pkgs.hyprland}/bin/hyprctl keyword decoration:inactive_opacity 1
${pkgs.hyprland}/bin/hyprctl keyword decoration:rounding 0
fi
'';
scripts.nightshift-toggle = pkgs.writeShellScriptBin "nightshift-toggle" ''
if ${pkgs.procps}/bin/pidof "hyprsunset" > /dev/null; then
pkill hyprsunset
OSD_TEXT="󰖔 Night Shift Off"
else
${pkgs.hyprsunset}/bin/hyprsunset -t 4500 &
OSD_TEXT="󰖔 Night Shift On"
fi
${scripts.waybar-osd}/bin/waybar-osd "$OSD_TEXT"
'';
home.packages = [scripts.focus-toggle scripts.nightshift-toggle];
xdg.desktopEntries = {
focus-toggle = {
name = "Focus Mode";
exec = "${scripts.focus-toggle}/bin/focus-toggle";
icon = "do-not-disturb-symbolic";
comment = "Toggle focus mode";
categories = ["System"];
terminal = false;
};
nightshift-toggle = {
name = "Night Shift";
exec = "${scripts.nightshift-toggle}/bin/nightshift-toggle";
icon = "night-light-symbolic";
comment = "Toggle night shift";
categories = ["System"];
terminal = false;
};
};
}
+9
View File
@@ -63,6 +63,15 @@
in { in {
stylix.targets.hyprlock.enable = false; stylix.targets.hyprlock.enable = false;
xdg.desktopEntries.lock = {
name = "Lock";
exec = "${pkgs.hyprlock}/bin/hyprlock";
icon = "system-lock-screen-symbolic";
comment = "Lock the screen";
categories = ["System"];
terminal = false;
};
programs.hyprlock = { programs.hyprlock = {
enable = true; enable = true;
settings = { settings = {
+10 -5
View File
@@ -8,11 +8,11 @@
text = ["nvim-ghostty.desktop"]; text = ["nvim-ghostty.desktop"];
code = ["nvim-ghostty.desktop"]; code = ["nvim-ghostty.desktop"];
image = ["imv-dir.desktop"]; image = ["imv-dir.desktop"];
audio = ["vlc.desktop"]; audio = ["kew.desktop"];
video = ["vlc.desktop"]; video = ["vlc.desktop"];
directory = ["elio.desktop"]; directory = ["elio.desktop"];
office = ["onlyoffice-desktopeditors.desktop"]; office = ["onlyoffice-desktopeditors.desktop"];
pdf = ["onlyoffice-desktopeditors.desktop"]; pdf = ["helium.desktop"];
archive = ["elio.desktop"]; archive = ["elio.desktop"];
browser = ["helium.desktop"]; browser = ["helium.desktop"];
}; };
@@ -53,21 +53,24 @@
"image/bmp" "image/bmp"
"image/gif" "image/gif"
"image/jpeg" "image/jpeg"
"image/jpg"
"image/png" "image/png"
"image/svg+xml" "image/svg+xml"
"image/tiff" "image/tiff"
"image/vnd.microsoft.icon" "image/vnd.microsoft.icon"
"image/webp" "image/webp"
"image/heif"
"image/avif"
]; ];
audio = [ audio = [
"audio/aac" "audio/aac"
"audio/mpeg" "audio/mpeg"
"audio/ogg" "audio/ogg"
"audio/opus" "audio/opus"
"audio/wav" "audio/vnd.wave"
"audio/webm" "audio/webm"
"audio/x-matroska" "audio/x-matroska"
"audio/flac"
"audio/mp4"
]; ];
video = [ video = [
"video/mp2t" "video/mp2t"
@@ -78,6 +81,7 @@
"video/x-flv" "video/x-flv"
"video/x-matroska" "video/x-matroska"
"video/x-msvideo" "video/x-msvideo"
"video/quicktime"
]; ];
directory = ["inode/directory"]; directory = ["inode/directory"];
office = [ office = [
@@ -104,6 +108,8 @@
"application/x-xz" "application/x-xz"
"application/x-zstd" "application/x-zstd"
"application/x-compressed-tar" "application/x-compressed-tar"
"application/x-bzip2-compressed-tar"
"application/x-xz-compressed-tar"
]; ];
browser = [ browser = [
"text/html" "text/html"
@@ -145,7 +151,6 @@ in {
enable = true; enable = true;
createDirectories = true; createDirectories = true;
desktop = null; desktop = null;
music = null;
publicShare = null; publicShare = null;
templates = null; templates = null;
extraConfig = { extraConfig = {
+42
View File
@@ -0,0 +1,42 @@
{
pkgs,
scripts,
...
}: {
scripts.power-cycle = pkgs.writeShellScriptBin "power-cycle" ''
ppd=${pkgs.power-profiles-daemon}/bin/powerprofilesctl
cur=$("$ppd" get)
case "$cur" in
power-saver) next=balanced; icon="󰾅" ;;
balanced) next=performance; icon="󰓅" ;;
performance) next=power-saver; icon="󰌪" ;;
*) next=balanced; icon="󰾅" ;;
esac
"$ppd" set "$next"
OSD_TEXT="$icon ''${next^}"
${scripts.waybar-osd}/bin/waybar-osd "$OSD_TEXT"
'';
home.packages = [pkgs.poweralertd scripts.power-cycle];
xdg.desktopEntries.power-cycle = {
name = "Power Profile";
exec = "${scripts.power-cycle}/bin/power-cycle";
icon = "power-profile-balanced-symbolic";
comment = "Cycle power-saver / balanced / performance";
categories = ["System"];
terminal = false;
settings.Keywords = "power;profile;performance;battery;";
};
# Event-driven low-battery/critical/full notifications via UPower dbus signals
# (thresholds set in nixos/utils.nix, services.upower.percentage*).
systemd.user.services.poweralertd = {
Unit.Description = "UPower-powered battery notifications";
Service = {
ExecStart = "${pkgs.poweralertd}/bin/poweralertd";
Restart = "on-failure";
};
Install.WantedBy = ["graphical-session.target"];
};
}
+16
View File
@@ -0,0 +1,16 @@
# Shared script registry: any module that owns a script contributes it here
# (`scripts.<name> = ...`), and every module reads it back via the `scripts`
# module argument.
{
lib,
config,
...
}: {
options.scripts = lib.mkOption {
type = lib.types.attrsOf lib.types.package;
default = {};
internal = true;
};
config._module.args.scripts = config.scripts;
}
+22 -8
View File
@@ -18,6 +18,28 @@
in "${builtins.substring high 1 digits}${builtins.substring low 1 digits}"; in "${builtins.substring high 1 digits}${builtins.substring low 1 digits}";
alphaHex = opacityToHex config.theme.inactive-opacity; alphaHex = opacityToHex config.theme.inactive-opacity;
in { in {
scripts.dnd-toggle = pkgs.writeShellScriptBin "dnd-toggle" ''
state=$(${pkgs.swaynotificationcenter}/bin/swaync-client -d)
if [ "$state" = "true" ]; then
OSD_TEXT="󰂛 Do Not Disturb On"
else
OSD_TEXT="󰂚 Do Not Disturb Off"
fi
${scripts.waybar-osd}/bin/waybar-osd "$OSD_TEXT"
'';
home.packages = [scripts.dnd-toggle];
xdg.desktopEntries.dnd-toggle = {
name = "Do Not Disturb";
exec = "${scripts.dnd-toggle}/bin/dnd-toggle";
icon = "notifications-disabled-symbolic";
comment = "Toggle Do Not Disturb";
categories = ["System"];
terminal = false;
settings.Keywords = "dnd;notifications;disturb;swaync;silence;";
};
services.swaync = { services.swaync = {
enable = true; enable = true;
@@ -409,13 +431,5 @@ in {
stylix.targets.swaync.enable = false; stylix.targets.swaync.enable = false;
# GTK 4.22 defaults to the Vulkan renderer, which compiles its graphics
# pipelines lazily on the first frame. The control center has many distinct
# visual elements, so the first open triggers a big burst of pipeline
# compilation (a multi-second stall on a cold boot) while later opens reuse
# the in-process cache. The GL (ngl) renderer avoids this: it stays
# GPU-accelerated and benefits from Mesa's on-disk shader cache, so the
# first open is as fast as the rest. Scoped to swaync so other GTK4 apps
# keep the Vulkan renderer.
systemd.user.services.swaync.Service.Environment = ["GSK_RENDERER=ngl"]; systemd.user.services.swaync.Service.Environment = ["GSK_RENDERER=ngl"];
} }
+2 -6
View File
@@ -1,6 +1,5 @@
# Use elio as the file picker for portal dialogs (browser "select file(s)" and # Use elio as the file picker for portal dialogs (browser "select file(s)" and
# "save download to..."). The backend is xdg-desktop-portal-termfilechooser, # "save download to...").
# enabled system-wide in nixos/utils.nix (extraPortals + FileChooser routing).
{ {
config, config,
pkgs, pkgs,
@@ -48,9 +47,6 @@
fi fi
fi fi
''; '';
# ghostty must run in the foreground instead of handing off to the
# single-instance daemon, otherwise the wrapper returns before you've picked
# anything and the portal reads an empty selection.
termcmd = "ghostty --gtk-single-instance=false --title=termfilechooser -e"; termcmd = "ghostty --gtk-single-instance=false --title=termfilechooser -e";
in { in {
xdg.configFile."xdg-desktop-portal-termfilechooser/config".text = '' xdg.configFile."xdg-desktop-portal-termfilechooser/config".text = ''
@@ -60,6 +56,6 @@ in {
open_mode=suggested open_mode=suggested
save_mode=suggested save_mode=suggested
env=TERMCMD=${termcmd} env=TERMCMD=${termcmd}
PATH=${config.home.profileDirectory}/bin:/run/current-system/sw/bin:/run/wrappers/bin PATH=${config.home.profileDirectory}/bin:/run/current-system/sw/bin:/run/wrappers/bin
''; '';
} }
+1 -1
View File
@@ -12,7 +12,7 @@
then barHeight / 2 then barHeight / 2
else config.theme.rounding; else config.theme.rounding;
in { in {
imports = [./desktop-actions.nix]; imports = [./desktop-actions.nix ./scripts.nix];
programs.tofi = { programs.tofi = {
enable = true; enable = true;
settings = { settings = {
+9 -66
View File
@@ -4,24 +4,6 @@
... ...
}: { }: {
xdg.desktopEntries = { xdg.desktopEntries = {
focus-toggle = {
name = "Focus Mode";
exec = "${scripts.focus-toggle}/bin/focus-toggle";
icon = "do-not-disturb-symbolic";
comment = "Toggle focus mode";
categories = ["System"];
terminal = false;
};
nightshift-toggle = {
name = "Night Shift";
exec = "${scripts.nightshift-toggle}/bin/nightshift-toggle";
icon = "night-light-symbolic";
comment = "Toggle night shift";
categories = ["System"];
terminal = false;
};
mic-mute = { mic-mute = {
name = "Mute Microphone"; name = "Mute Microphone";
exec = "${scripts.mic-mute}/bin/mic-mute"; exec = "${scripts.mic-mute}/bin/mic-mute";
@@ -31,15 +13,6 @@
terminal = false; terminal = false;
}; };
lock = {
name = "Lock";
exec = "${pkgs.hyprlock}/bin/hyprlock";
icon = "system-lock-screen-symbolic";
comment = "Lock the screen";
categories = ["System"];
terminal = false;
};
reboot = { reboot = {
name = "Reboot"; name = "Reboot";
exec = "systemctl reboot"; exec = "systemctl reboot";
@@ -49,6 +22,15 @@
terminal = false; terminal = false;
}; };
suspend = {
name = "Suspend";
exec = "systemctl suspend";
icon = "system-suspend-symbolic";
comment = "Suspend the system";
categories = ["System"];
terminal = false;
};
shutdown = { shutdown = {
name = "Shutdown"; name = "Shutdown";
exec = "systemctl poweroff"; exec = "systemctl poweroff";
@@ -67,15 +49,6 @@
terminal = false; terminal = false;
}; };
waybar-toggle = {
name = "Toggle Waybar";
exec = "${scripts.waybar-toggle}/bin/waybar-toggle";
icon = "panel-applets-symbolic";
comment = "Show or hide the status bar";
categories = ["System"];
terminal = false;
};
wifi-toggle = { wifi-toggle = {
name = "Toggle Wi-Fi"; name = "Toggle Wi-Fi";
exec = "${scripts.wifi-toggle}/bin/wifi-toggle"; exec = "${scripts.wifi-toggle}/bin/wifi-toggle";
@@ -154,16 +127,6 @@
settings.Keywords = "record;recording;screencast;video;"; settings.Keywords = "record;recording;screencast;video;";
}; };
power-cycle = {
name = "Power Profile";
exec = "${scripts.power-cycle}/bin/power-cycle";
icon = "power-profile-balanced-symbolic";
comment = "Cycle power-saver / balanced / performance";
categories = ["System"];
terminal = false;
settings.Keywords = "power;profile;performance;battery;";
};
airplane-toggle = { airplane-toggle = {
name = "Airplane Mode"; name = "Airplane Mode";
exec = "${scripts.airplane-toggle}/bin/airplane-toggle"; exec = "${scripts.airplane-toggle}/bin/airplane-toggle";
@@ -194,16 +157,6 @@
settings.Keywords = "icon;nerdfont;glyph;symbol;"; settings.Keywords = "icon;nerdfont;glyph;symbol;";
}; };
caffeine-toggle = {
name = "Keep Awake";
exec = "${scripts.caffeine-toggle}/bin/caffeine-toggle";
icon = "my-caffeine-on-symbolic";
comment = "Pause or resume idle locking and suspend";
categories = ["System"];
terminal = false;
settings.Keywords = "caffeine;idle;awake;inhibit;suspend;";
};
logout = { logout = {
name = "Logout"; name = "Logout";
exec = "${pkgs.hyprland}/bin/hyprctl dispatch exit"; exec = "${pkgs.hyprland}/bin/hyprctl dispatch exit";
@@ -213,16 +166,6 @@
terminal = false; terminal = false;
}; };
dnd-toggle = {
name = "Do Not Disturb";
exec = "${scripts.dnd-toggle}/bin/dnd-toggle";
icon = "notifications-disabled-symbolic";
comment = "Toggle Do Not Disturb";
categories = ["System"];
terminal = false;
settings.Keywords = "dnd;notifications;disturb;swaync;silence;";
};
vol-mute = { vol-mute = {
name = "Mute Volume"; name = "Mute Volume";
exec = "${scripts.vol-mute}/bin/vol-mute"; exec = "${scripts.vol-mute}/bin/vol-mute";
+35
View File
@@ -0,0 +1,35 @@
# Picker scripts built on tofi's dmenu mode: emoji and Nerd Font icon input.
{
pkgs,
config,
scripts,
...
}: let
tofiMenu = "${pkgs.tofi}/bin/tofi --horizontal false --anchor center --width 700 --height 500 --margin-top 0 --margin-left 0 --margin-right 0 --num-results 10";
nerdFontGlyphnames = pkgs.fetchurl {
url = "https://raw.githubusercontent.com/ryanoasis/nerd-fonts/v3.4.0/glyphnames.json";
sha256 = "sha256-4tENI/W/8L1vBnbpsB2XifzcZW3ntJiilVwncW6kQ5w=";
};
nerdFontIconList = pkgs.runCommand "nerd-font-icons.txt" {nativeBuildInputs = [pkgs.jq];} ''
jq -r 'to_entries[] | select(.key != "METADATA") | "\(.value.char) \(.key)"' \
${nerdFontGlyphnames} > "$out"
'';
in {
scripts.emoji-picker = pkgs.writeShellScriptBin "emoji-picker" ''
export PATH="${pkgs.wtype}/bin:${pkgs.wl-clipboard}/bin:$PATH"
export BEMOJI_PICKER_CMD="${tofiMenu} --prompt-text 'emoji: '"
# -t types the emoji, -c also copies it.
exec ${pkgs.bemoji}/bin/bemoji -t -c
'';
scripts.icon-picker = pkgs.writeShellScriptBin "icon-picker" ''
selected=$(${tofiMenu} --font "${config.stylix.fonts.monospace.name}" --prompt-text 'icon: ' < ${nerdFontIconList})
[ -z "$selected" ] || icon=$(printf '%s\n' "$selected" | awk '{print $1}')
[ -n "''${icon:-}" ] || exit 0
${pkgs.wtype}/bin/wtype "$icon"
printf '%s' "$icon" | ${pkgs.wl-clipboard}/bin/wl-copy
'';
home.packages = [scripts.emoji-picker scripts.icon-picker];
}
+1 -1
View File
@@ -2,7 +2,7 @@
{ {
services.udiskie = { services.udiskie = {
enable = true; enable = true;
notify = true; notify = false;
automount = true; automount = true;
tray = "never"; tray = "never";
}; };
-103
View File
@@ -1,103 +0,0 @@
# battery-monitor: low-battery alerts for waybar/swaync.
# Runs main once (triggered by the battery-monitor systemd timer).
POWER_SUPPLY_DIR="${POWER_SUPPLY_DIR:-/sys/class/power_supply}"
STATE_FILE="${BATTERY_STATE_FILE:-/tmp/battery-alert-state}"
CRIT_ID_FILE="${BATTERY_CRIT_ID_FILE:-/tmp/battery-crit-id}"
WARN_THRESHOLD=10
CRIT_THRESHOLD=5
# Pure decision. Prints "<action>:<newstate>".
# action ∈ noop | osd_warn | crit_show | crit_clear ; state ∈ none | warn10 | crit5
battery_decide() {
status=$1
cap=$2
prev=$3
if [ "$status" = "Discharging" ]; then
if [ "$cap" -le "$CRIT_THRESHOLD" ]; then
echo "crit_show:crit5"
elif [ "$cap" -le "$WARN_THRESHOLD" ]; then
if [ "$prev" = "crit5" ]; then
echo "crit_clear:warn10"
elif [ "$prev" = "warn10" ]; then
echo "noop:warn10"
else
echo "osd_warn:warn10"
fi
else
if [ "$prev" = "crit5" ]; then
echo "crit_clear:none"
else
echo "noop:none"
fi
fi
else
if [ "$prev" = "crit5" ]; then
echo "crit_clear:none"
else
echo "noop:none"
fi
fi
}
# First real battery (type == Battery, with status + capacity).
resolve_battery() {
for d in "$POWER_SUPPLY_DIR"/*; do
[ -r "$d/type" ] || continue
[ "$(cat "$d/type")" = "Battery" ] || continue
[ -r "$d/status" ] && [ -r "$d/capacity" ] || continue
printf '%s' "$d"
return 0
done
return 1
}
show_warn_osd() {
waybar-osd "󰂃 Low battery · $1%"
}
show_crit_notification() {
cap=$1
id=$(cat "$CRIT_ID_FILE" 2>/dev/null || echo 0)
newid=$(notify-send --print-id --replace-id="$id" \
--urgency=critical --expire-time=0 \
"󰂃 Critical battery" "Battery at $cap%: plug in the charger")
printf '%s' "$newid" >"$CRIT_ID_FILE"
}
clear_crit_notification() {
id=$(cat "$CRIT_ID_FILE" 2>/dev/null || echo 0)
if [ "$id" != "0" ]; then
gdbus call --session \
--dest org.freedesktop.Notifications \
--object-path /org/freedesktop/Notifications \
--method org.freedesktop.Notifications.CloseNotification "$id" \
>/dev/null 2>&1 || true
fi
rm -f "$CRIT_ID_FILE"
}
main() {
dir=$(resolve_battery) || exit 0
status=$(cat "$dir/status")
cap=$(cat "$dir/capacity")
prev=$(cat "$STATE_FILE" 2>/dev/null || echo none)
result=$(battery_decide "$status" "$cap" "$prev")
action=${result%%:*}
newstate=${result##*:}
case "$action" in
osd_warn) show_warn_osd "$cap" ;;
crit_show) show_crit_notification "$cap" ;;
crit_clear) clear_crit_notification ;;
noop) : ;;
esac
printf '%s' "$newstate" >"$STATE_FILE"
}
main "$@"
+10 -36
View File
@@ -2,23 +2,13 @@
pkgs, pkgs,
config, config,
... ...
}: let }: {
scripts = import ./scripts.nix {inherit pkgs config;};
in {
imports = [ imports = [
./scripts
./settings.nix ./settings.nix
./style.nix ./style.nix
]; ];
_module.args.scripts = scripts;
_module.args.osdPath = scripts.osdPath;
_module.args.networkScript = scripts.networkScript;
_module.args.bluetoothScript = scripts.bluetoothScript;
_module.args.volMuteScript = scripts.vol-mute;
_module.args.caffeineToggleScript = scripts.caffeine-toggle;
_module.args.osdStatusScript = scripts.waybar-osd-status;
_module.args.nightshiftToggleScript = scripts.nightshift-toggle;
programs.waybar.enable = true; programs.waybar.enable = true;
stylix.targets.waybar.enable = false; stylix.targets.waybar.enable = false;
@@ -26,35 +16,19 @@ in {
[ [
playerctl playerctl
pavucontrol pavucontrol
blueman
hyprsunset hyprsunset
] ]
++ (with scripts; [waybar-osd waybar-osd-status battery-monitor vol-up vol-down vol-mute mic-mute bright-up bright-down nightshift-toggle focus-toggle waybar-toggle wifi-toggle bluetooth-toggle dnd-toggle output-cycle input-cycle color-pick screenshot-edit record-toggle power-cycle airplane-toggle clipboard-menu emoji-picker caffeine-toggle mic-status]); ++ (with config.scripts; [waybar-osd waybar-osd-status vol-up vol-down vol-mute mic-mute bright-up bright-down waybar-toggle wifi-toggle bluetooth-toggle output-cycle input-cycle color-pick screenshot-edit record-toggle airplane-toggle mic-status]);
# Poll battery level every 30s; battery-monitor no-ops when no battery exists. xdg.desktopEntries.waybar-toggle = {
systemd.user.services.battery-monitor = { name = "Toggle Waybar";
Unit.Description = "Low-battery OSD and critical notification monitor"; exec = "${config.scripts.waybar-toggle}/bin/waybar-toggle";
Service = { icon = "panel-applets-symbolic";
Type = "oneshot"; comment = "Show or hide the status bar";
ExecStart = "${scripts.battery-monitor}/bin/battery-monitor"; categories = ["System"];
}; terminal = false;
}; };
systemd.user.timers.battery-monitor = {
Unit.Description = "Poll battery level for low-battery alerts";
Timer = {
OnBootSec = "1min";
OnUnitActiveSec = "30s";
};
Install.WantedBy = ["timers.target"];
};
# Prevent blueman-applet from auto-starting via XDG autostart (we use custom/bluetooth module instead)
xdg.configFile."autostart/blueman.desktop".text = ''
[Desktop Entry]
Hidden=true
'';
wayland.windowManager.hyprland.settings.exec-once = [ wayland.windowManager.hyprland.settings.exec-once = [
"waybar" "waybar"
]; ];
-456
View File
@@ -1,456 +0,0 @@
{
pkgs,
config,
}: let
waybar-osd = pkgs.writeShellApplication {
name = "waybar-osd";
runtimeInputs = with pkgs; [procps coreutils];
text = ''
printf '%s' "$1" > "$XDG_RUNTIME_DIR/waybar-osd"
pkill -f -RTMIN+8 '^waybar$' 2>/dev/null || true
'';
};
waybar-osd-status = pkgs.writeShellApplication {
name = "waybar-osd-status";
runtimeInputs = with pkgs; [coreutils];
text = ''
file="$XDG_RUNTIME_DIR/waybar-osd"
[ -f "$file" ] || exit 1
mtime=$(stat -c %Y "$file" 2>/dev/null) || exit 1
age=$(( $(date +%s) - mtime ))
if [ "$age" -ge 3 ]; then
rm -f "$file"
exit 1
fi
'';
};
# battery-monitor: low-battery alerts, run by the systemd user timer.
battery-monitor = pkgs.writeShellApplication {
name = "battery-monitor";
runtimeInputs = with pkgs; [waybar-osd libnotify glib coreutils];
text = builtins.readFile ./battery-monitor.sh;
};
updateOsd = ''
${waybar-osd}/bin/waybar-osd "$OSD_TEXT"
'';
volGetText = ''
VOL_RAW=$(${pkgs.wireplumber}/bin/wpctl get-volume @DEFAULT_SINK@)
VOL=$(printf '%s' "$VOL_RAW" | awk '{printf "%d", $2*100}')
if printf '%s' "$VOL_RAW" | grep -q MUTED; then
OSD_TEXT="󰝟 $VOL%"
elif [ "$VOL" -lt 33 ]; then
OSD_TEXT="󰕿 $VOL%"
elif [ "$VOL" -lt 66 ]; then
OSD_TEXT="󰖀 $VOL%"
else
OSD_TEXT="󰕾 $VOL%"
fi
'';
brightGetText = ''
BRIGHT=$(${pkgs.brightnessctl}/bin/brightnessctl -m | awk -F, '{print int($5)}')
if [ "$BRIGHT" -lt 33 ]; then
OSD_TEXT="󰃞 $BRIGHT%"
elif [ "$BRIGHT" -lt 66 ]; then
OSD_TEXT="󰃟 $BRIGHT%"
else
OSD_TEXT="󰃠 $BRIGHT%"
fi
'';
# Resolve $src: the default source, otherwise the first available source.
# Some machines have no default audio source (@DEFAULT_AUDIO_SOURCE@ unresolved).
micSource = ''
src=$(${pkgs.wireplumber}/bin/wpctl inspect @DEFAULT_AUDIO_SOURCE@ 2>/dev/null | sed -n 's/^id \([0-9]*\),.*/\1/p')
if [ -z "$src" ]; then
src=$(${pkgs.pipewire}/bin/pw-dump | ${pkgs.jq}/bin/jq -r \
'[.[] | select(.info.props."media.class"=="Audio/Source") | .id] | .[0] // empty')
fi
'';
# tofi in vertical dmenu mode: the global config is a 36px-tall horizontal
# launcher, unusable for a list, so override the geometry here.
tofiMenu = "${pkgs.tofi}/bin/tofi --horizontal false --anchor center --width 700 --height 500 --margin-top 0 --margin-left 0 --margin-right 0 --num-results 10";
# Name -> glyph table maintained by the nerd-fonts project itself, pinned to
# the same release as the nerd-fonts packages in nixos/fonts.nix.
nerdFontGlyphnames = pkgs.fetchurl {
url = "https://raw.githubusercontent.com/ryanoasis/nerd-fonts/v3.4.0/glyphnames.json";
sha256 = "sha256-4tENI/W/8L1vBnbpsB2XifzcZW3ntJiilVwncW6kQ5w=";
};
nerdFontIconList = pkgs.runCommand "nerd-font-icons.txt" {nativeBuildInputs = [pkgs.jq];} ''
jq -r 'to_entries[] | select(.key != "METADATA") | "\(.value.char) \(.key)"' \
${nerdFontGlyphnames} > "$out"
'';
osdPath = "$XDG_RUNTIME_DIR/waybar-osd";
in {
inherit waybar-osd waybar-osd-status battery-monitor osdPath;
bluetoothScript = pkgs.writeShellScript "waybar-bluetooth" ''
jq=${pkgs.jq}/bin/jq
nl=$'\n'
# bluetoothctl hangs without a controller by default → always bounded by timeout.
bt() { timeout 3 ${pkgs.bluez}/bin/bluetoothctl "$@" 2>/dev/null; }
powered=$(bt show | awk '/Powered:/ { print $2; exit }')
if [ "$powered" != "yes" ]; then
"$jq" -cn '{ text: "󰂲", class: "off", tooltip: "Bluetooth off" }'
exit 0
fi
tip=""
add_tip() { tip="''${tip:+$tip$nl}$1"; }
count=0
for mac in $(bt devices Connected | awk '{ print $2 }'); do
count=$((count + 1))
info=$(bt info "$mac")
name=$(printf '%s' "$info" | sed -n 's/^[[:space:]]*Name: //p' | head -1)
[ -z "$name" ] && name="$mac"
batt=$(printf '%s' "$info" | sed -n 's/.*Battery Percentage:.*(\([0-9][0-9]*\)).*/\1/p' | head -1)
add_tip "󰂱 $name''${batt:+ · $batt%}"
done
if [ "$count" -gt 0 ]; then
"$jq" -cn --arg tooltip "$tip" '{ text: "󰂰", class: "connected", tooltip: $tooltip }'
else
"$jq" -cn '{ text: "󰂰", class: "on", tooltip: "Bluetooth on" }'
fi
'';
networkScript = pkgs.writeShellScript "waybar-network" ''
nmcli=${pkgs.networkmanager}/bin/nmcli
ip=${pkgs.iproute2}/bin/ip
jq=${pkgs.jq}/bin/jq
nl=$'\n'
text=""
tip=""
class="disconnected"
add_seg() { text="''${text:+$text }$1"; }
add_tip() { tip="''${tip:+$tip$nl}$1"; }
# ── Ethernet ────────────────────────────────────────────────
eth=$("$nmcli" -t -f DEVICE,TYPE,STATE device status 2>/dev/null \
| awk -F: '$2 == "ethernet" && $3 == "connected" { print $1; exit }')
if [ -n "$eth" ]; then
eth_ip=$("$ip" -4 -br addr show "$eth" 2>/dev/null | awk '{ print $3 }' | cut -d/ -f1)
add_seg "󰛳"
class="ethernet"
add_tip "󰛳 Ethernet''${eth_ip:+ · $eth_ip}"
fi
# ── Wi-Fi ───────────────────────────────────────────────────
wifi=$("$nmcli" -t -f ACTIVE,SSID,SIGNAL device wifi 2>/dev/null | grep -m1 '^yes:')
if [ -n "$wifi" ]; then
ssid=$(printf '%s' "$wifi" | cut -d: -f2)
signal=$(printf '%s' "$wifi" | cut -d: -f3)
if [ "''${signal:-0}" -ge 80 ]; then icon="󰤨"
elif [ "''${signal:-0}" -ge 60 ]; then icon="󰤥"
elif [ "''${signal:-0}" -ge 40 ]; then icon="󰤢"
elif [ "''${signal:-0}" -ge 20 ]; then icon="󰤟"
else icon="󰤯"
fi
add_seg "$icon"
[ "$class" = "disconnected" ] && class="wifi"
add_tip "$icon ''${ssid:-Wi-Fi}''${signal:+ · $signal%}"
fi
# ── VPN (nmcli, fallback interfaces) ────────────────────────
vpn=$("$nmcli" -t -f TYPE,NAME connection show --active 2>/dev/null \
| awk -F: '$1 == "vpn" || $1 == "wireguard" { print $2; exit }')
if [ -z "$vpn" ]; then
vpn=$("$ip" -br link show 2>/dev/null \
| awk '($2 == "UP" || $2 == "UNKNOWN") && $1 ~ /^(tun|wg|proton|ppp)/ { sub(/@.*/, "", $1); print $1; exit }')
fi
if [ -n "$vpn" ]; then
add_seg "󰦝"
add_tip "󰦝 VPN · $vpn"
fi
if [ -z "$text" ]; then
text="󰤭"
tip="Disconnected"
fi
"$jq" -cn --arg text "$text" --arg class "$class" --arg tooltip "$tip" \
'{ text: $text, class: $class, tooltip: $tooltip }'
'';
vol-up = pkgs.writeShellScriptBin "vol-up" ''
${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_SINK@ 5%+ --limit 1.0
${volGetText}
${updateOsd}
'';
vol-down = pkgs.writeShellScriptBin "vol-down" ''
${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_SINK@ 5%-
${volGetText}
${updateOsd}
'';
vol-mute = pkgs.writeShellScriptBin "vol-mute" ''
${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_SINK@ toggle
${volGetText}
${updateOsd}
'';
mic-mute = pkgs.writeShellScriptBin "mic-mute" ''
wpctl=${pkgs.wireplumber}/bin/wpctl
${micSource}
[ -z "$src" ] && exit 0
"$wpctl" set-mute "$src" toggle
if "$wpctl" get-volume "$src" | grep -q MUTED; then
OSD_TEXT="󰍭 Muted"
else
OSD_TEXT="󰍬 Live"
fi
${updateOsd}
'';
mic-status = pkgs.writeShellScriptBin "mic-status" ''
${micSource}
if [ -n "$src" ] && ${pkgs.wireplumber}/bin/wpctl get-volume "$src" | grep -q MUTED; then
echo true
else
echo false
fi
'';
bright-up = pkgs.writeShellScriptBin "bright-up" ''
${pkgs.brightnessctl}/bin/brightnessctl set 5%+
${brightGetText}
${updateOsd}
'';
bright-down = pkgs.writeShellScriptBin "bright-down" ''
${pkgs.brightnessctl}/bin/brightnessctl set 5%-
${brightGetText}
${updateOsd}
'';
wifi-toggle = pkgs.writeShellScriptBin "wifi-toggle" ''
if ${pkgs.networkmanager}/bin/nmcli radio wifi | grep -q enabled; then
${pkgs.networkmanager}/bin/nmcli radio wifi off
else
${pkgs.networkmanager}/bin/nmcli radio wifi on
fi
${updateOsd}
'';
bluetooth-toggle = pkgs.writeShellScriptBin "bluetooth-toggle" ''
if ${pkgs.bluez}/bin/bluetoothctl show | grep -q "Powered: yes"; then
${pkgs.bluez}/bin/bluetoothctl power off
else
${pkgs.bluez}/bin/bluetoothctl power on
fi
${updateOsd}
'';
waybar-toggle = pkgs.writeShellScriptBin "waybar-toggle" ''
if pidof waybar > /dev/null; then
pkill waybar
else
hyprctl dispatch exec waybar
fi
'';
nightshift-toggle = pkgs.writeShellScriptBin "nightshift-toggle" ''
if ${pkgs.procps}/bin/pidof "hyprsunset" > /dev/null; then
pkill hyprsunset
OSD_TEXT="󰖔 Night Shift Off"
else
${pkgs.hyprsunset}/bin/hyprsunset -t 4500 &
OSD_TEXT="󰖔 Night Shift On"
fi
${updateOsd}
'';
focus-toggle = pkgs.writeShellScriptBin "focus-toggle" ''
if test -f "$XDG_RUNTIME_DIR/hypr-focus-mode"; then
rm "$XDG_RUNTIME_DIR/hypr-focus-mode"
OSD_TEXT="󰈈 Focus Off"
${updateOsd}
${pkgs.hyprland}/bin/hyprctl reload
${pkgs.hyprland}/bin/hyprctl dispatch exec waybar
else
touch "$XDG_RUNTIME_DIR/hypr-focus-mode"
OSD_TEXT="󰈈 Focus On"
${updateOsd}
${pkgs.procps}/bin/pkill waybar || true
${pkgs.hyprland}/bin/hyprctl keyword animations:enabled false
${pkgs.hyprland}/bin/hyprctl keyword general:gaps_in 0
${pkgs.hyprland}/bin/hyprctl keyword general:gaps_out 0
${pkgs.hyprland}/bin/hyprctl keyword decoration:active_opacity 1
${pkgs.hyprland}/bin/hyprctl keyword decoration:inactive_opacity 1
${pkgs.hyprland}/bin/hyprctl keyword decoration:rounding 0
fi
'';
dnd-toggle = pkgs.writeShellScriptBin "dnd-toggle" ''
state=$(${pkgs.swaynotificationcenter}/bin/swaync-client -d)
if [ "$state" = "true" ]; then
OSD_TEXT="󰂛 Do Not Disturb On"
else
OSD_TEXT="󰂚 Do Not Disturb Off"
fi
${updateOsd}
'';
output-cycle = pkgs.writeShellScriptBin "output-cycle" ''
wpctl=${pkgs.wireplumber}/bin/wpctl
jq=${pkgs.jq}/bin/jq
pwdump=${pkgs.pipewire}/bin/pw-dump
# Sink IDs in pw-dump order.
ids=($("$pwdump" | "$jq" -r '.[] | select(.info.props."media.class"=="Audio/Sink") | .id'))
n=''${#ids[@]}
[ "$n" -eq 0 ] && exit 0
cur=$("$wpctl" inspect @DEFAULT_AUDIO_SINK@ 2>/dev/null | sed -n 's/^id \([0-9]*\),.*/\1/p')
next_index=0
for i in "''${!ids[@]}"; do
if [ "''${ids[$i]}" = "$cur" ]; then
next_index=$(( (i + 1) % n ))
break
fi
done
next=''${ids[$next_index]}
"$wpctl" set-default "$next"
desc=$("$pwdump" | "$jq" -r --argjson id "$next" '.[] | select(.id==$id) | .info.props."node.description"')
OSD_TEXT="󰓃 ''${desc:-Output}"
${updateOsd}
'';
input-cycle = pkgs.writeShellScriptBin "input-cycle" ''
wpctl=${pkgs.wireplumber}/bin/wpctl
jq=${pkgs.jq}/bin/jq
pwdump=${pkgs.pipewire}/bin/pw-dump
# Source IDs in pw-dump order.
ids=($("$pwdump" | "$jq" -r '.[] | select(.info.props."media.class"=="Audio/Source") | .id'))
n=''${#ids[@]}
[ "$n" -eq 0 ] && exit 0
cur=$("$wpctl" inspect @DEFAULT_AUDIO_SOURCE@ 2>/dev/null | sed -n 's/^id \([0-9]*\),.*/\1/p')
next_index=0
for i in "''${!ids[@]}"; do
if [ "''${ids[$i]}" = "$cur" ]; then
next_index=$(( (i + 1) % n ))
break
fi
done
next=''${ids[$next_index]}
"$wpctl" set-default "$next"
desc=$("$pwdump" | "$jq" -r --argjson id "$next" '.[] | select(.id==$id) | .info.props."node.description"')
OSD_TEXT="󰍬 ''${desc:-Input}"
${updateOsd}
'';
color-pick = pkgs.writeShellScriptBin "color-pick" ''
# hyprpicker -a already copies the color to the clipboard.
color=$(${pkgs.hyprpicker}/bin/hyprpicker -a 2>/dev/null)
if [ -n "$color" ]; then
OSD_TEXT="󰈊 $color"
else
OSD_TEXT="󰈊 Cancelled"
fi
${updateOsd}
'';
screenshot-edit = pkgs.writeShellScriptBin "screenshot-edit" ''
${pkgs.hyprshot}/bin/hyprshot -m region --raw 2>/dev/null \
| ${pkgs.satty}/bin/satty --filename -
'';
record-toggle = pkgs.writeShellScriptBin "record-toggle" ''
if pgrep -x wf-recorder >/dev/null; then
# -INT lets wf-recorder finalize the file cleanly.
pkill -INT -x wf-recorder
OSD_TEXT="󰕧 Recording saved"
else
dir="$HOME/Videos"
mkdir -p "$dir"
file="$dir/rec-$(date +%Y%m%d-%H%M%S).mp4"
${pkgs.wf-recorder}/bin/wf-recorder -f "$file" >/dev/null 2>&1 &
OSD_TEXT="󰑊 Recording…"
fi
${updateOsd}
'';
power-cycle = pkgs.writeShellScriptBin "power-cycle" ''
ppd=${pkgs.power-profiles-daemon}/bin/powerprofilesctl
cur=$("$ppd" get)
case "$cur" in
power-saver) next=balanced; icon="󰾅" ;;
balanced) next=performance; icon="󰓅" ;;
performance) next=power-saver; icon="󰌪" ;;
*) next=balanced; icon="󰾅" ;;
esac
"$ppd" set "$next"
OSD_TEXT="$icon ''${next^}"
${updateOsd}
'';
airplane-toggle = pkgs.writeShellScriptBin "airplane-toggle" ''
# Reuse nmcli + bluetoothctl (unprivileged) instead of rfkill.
on=false
nmcli radio wifi 2>/dev/null | grep -q enabled && on=true
bluetoothctl show 2>/dev/null | grep -q "Powered: yes" && on=true
if $on; then
nmcli radio wifi off 2>/dev/null
bluetoothctl power off >/dev/null 2>&1 || true
OSD_TEXT="󰀝 Airplane On"
else
nmcli radio wifi on 2>/dev/null
bluetoothctl power on >/dev/null 2>&1 || true
OSD_TEXT="󰀞 Airplane Off"
fi
${updateOsd}
'';
clipboard-menu = pkgs.writeShellScriptBin "clipboard-menu" ''
list=$(${pkgs.cliphist}/bin/cliphist list)
selected=$(printf '%s\n' "$list" | cut -f2- | ${tofiMenu} --prompt-text "Clipboard: ")
[ -n "$selected" ] && printf '%s\n' "$list" \
| awk -F'\t' -v s="$selected" '$2 == s {print; exit}' \
| ${pkgs.cliphist}/bin/cliphist decode \
| ${pkgs.wl-clipboard}/bin/wl-copy
'';
emoji-picker = pkgs.writeShellScriptBin "emoji-picker" ''
export PATH="${pkgs.wtype}/bin:${pkgs.wl-clipboard}/bin:$PATH"
export BEMOJI_PICKER_CMD="${tofiMenu} --prompt-text 'emoji: '"
# -t types the emoji (wtype), -c also copies it.
exec ${pkgs.bemoji}/bin/bemoji -t -c
'';
icon-picker = pkgs.writeShellScriptBin "icon-picker" ''
selected=$(${tofiMenu} --font "${config.stylix.fonts.monospace.name}" --prompt-text 'icon: ' < ${nerdFontIconList})
[ -z "$selected" ] || icon=$(printf '%s\n' "$selected" | awk '{print $1}')
[ -n "''${icon:-}" ] || exit 0
${pkgs.wtype}/bin/wtype "$icon"
printf '%s' "$icon" | ${pkgs.wl-clipboard}/bin/wl-copy
'';
caffeine-toggle = pkgs.writeShellScriptBin "caffeine-toggle" ''
# Pause hypridle (stay awake) or resume it.
if systemctl --user is-active --quiet hypridle; then
systemctl --user stop hypridle
OSD_TEXT="󰅶 Keep Awake On"
else
systemctl --user start hypridle
OSD_TEXT="󰾫 Keep Awake Off"
fi
${updateOsd}
'';
}
+74
View File
@@ -0,0 +1,74 @@
{
pkgs,
scripts,
...
}: let
volGetText = ''
VOL_RAW=$(${pkgs.wireplumber}/bin/wpctl get-volume @DEFAULT_SINK@)
VOL=$(printf '%s' "$VOL_RAW" | awk '{printf "%d", $2*100}')
if printf '%s' "$VOL_RAW" | grep -q MUTED; then
OSD_TEXT="󰝟 $VOL%"
elif [ "$VOL" -lt 33 ]; then
OSD_TEXT="󰕿 $VOL%"
elif [ "$VOL" -lt 66 ]; then
OSD_TEXT="󰖀 $VOL%"
else
OSD_TEXT="󰕾 $VOL%"
fi
'';
# Resolve $src: the default source, otherwise the first available source.
# Some machines have no default audio source (@DEFAULT_AUDIO_SOURCE@ unresolved).
micSource = ''
src=$(${pkgs.wireplumber}/bin/wpctl inspect @DEFAULT_AUDIO_SOURCE@ 2>/dev/null | sed -n 's/^id \([0-9]*\),.*/\1/p')
if [ -z "$src" ]; then
src=$(${pkgs.pipewire}/bin/pw-dump | ${pkgs.jq}/bin/jq -r \
'[.[] | select(.info.props."media.class"=="Audio/Source") | .id] | .[0] // empty')
fi
'';
updateOsd = ''
${scripts.waybar-osd}/bin/waybar-osd "$OSD_TEXT"
'';
in {
scripts.vol-up = pkgs.writeShellScriptBin "vol-up" ''
${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_SINK@ 5%+ --limit 1.0
${volGetText}
${updateOsd}
'';
scripts.vol-down = pkgs.writeShellScriptBin "vol-down" ''
${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_SINK@ 5%-
${volGetText}
${updateOsd}
'';
scripts.vol-mute = pkgs.writeShellScriptBin "vol-mute" ''
${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_SINK@ toggle
${volGetText}
${updateOsd}
'';
scripts.mic-mute = pkgs.writeShellScriptBin "mic-mute" ''
wpctl=${pkgs.wireplumber}/bin/wpctl
${micSource}
[ -z "$src" ] && exit 0
"$wpctl" set-mute "$src" toggle
if "$wpctl" get-volume "$src" | grep -q MUTED; then
OSD_TEXT="󰍭 Muted"
else
OSD_TEXT="󰍬 Live"
fi
${updateOsd}
'';
scripts.mic-status = pkgs.writeShellScriptBin "mic-status" ''
${micSource}
if [ -n "$src" ] && ${pkgs.wireplumber}/bin/wpctl get-volume "$src" | grep -q MUTED; then
echo true
else
echo false
fi
'';
}
+50
View File
@@ -0,0 +1,50 @@
{
pkgs,
scripts,
...
}: let
updateOsd = ''
${scripts.waybar-osd}/bin/waybar-osd "$OSD_TEXT"
'';
in {
scripts.bluetoothScript = pkgs.writeShellScript "waybar-bluetooth" ''
jq=${pkgs.jq}/bin/jq
nl=$'\n'
# bluetoothctl hangs without a controller by default → always bounded by timeout.
bt() { timeout 3 ${pkgs.bluez}/bin/bluetoothctl "$@" 2>/dev/null; }
powered=$(bt show | awk '/Powered:/ { print $2; exit }')
if [ "$powered" != "yes" ]; then
"$jq" -cn '{ text: "󰂲", class: "off", tooltip: "Bluetooth off" }'
exit 0
fi
tip=""
add_tip() { tip="''${tip:+$tip$nl}$1"; }
count=0
for mac in $(bt devices Connected | awk '{ print $2 }'); do
count=$((count + 1))
info=$(bt info "$mac")
name=$(printf '%s' "$info" | sed -n 's/^[[:space:]]*Name: //p' | head -1)
[ -z "$name" ] && name="$mac"
batt=$(printf '%s' "$info" | sed -n 's/.*Battery Percentage:.*(\([0-9][0-9]*\)).*/\1/p' | head -1)
add_tip "󰂱 $name''${batt:+ · $batt%}"
done
if [ "$count" -gt 0 ]; then
"$jq" -cn --arg tooltip "$tip" '{ text: "󰂰", class: "connected", tooltip: $tooltip }'
else
"$jq" -cn '{ text: "󰂰", class: "on", tooltip: "Bluetooth on" }'
fi
'';
scripts.bluetooth-toggle = pkgs.writeShellScriptBin "bluetooth-toggle" ''
if ${pkgs.bluez}/bin/bluetoothctl show | grep -q "Powered: yes"; then
${pkgs.bluez}/bin/bluetoothctl power off
else
${pkgs.bluez}/bin/bluetoothctl power on
fi
${updateOsd}
'';
}
+32
View File
@@ -0,0 +1,32 @@
{
pkgs,
scripts,
...
}: let
brightGetText = ''
BRIGHT=$(${pkgs.brightnessctl}/bin/brightnessctl -m | awk -F, '{print int($5)}')
if [ "$BRIGHT" -lt 33 ]; then
OSD_TEXT="󰃞 $BRIGHT%"
elif [ "$BRIGHT" -lt 66 ]; then
OSD_TEXT="󰃟 $BRIGHT%"
else
OSD_TEXT="󰃠 $BRIGHT%"
fi
'';
updateOsd = ''
${scripts.waybar-osd}/bin/waybar-osd "$OSD_TEXT"
'';
in {
scripts.bright-up = pkgs.writeShellScriptBin "bright-up" ''
${pkgs.brightnessctl}/bin/brightnessctl set 5%+
${brightGetText}
${updateOsd}
'';
scripts.bright-down = pkgs.writeShellScriptBin "bright-down" ''
${pkgs.brightnessctl}/bin/brightnessctl set 5%-
${brightGetText}
${updateOsd}
'';
}
+11
View File
@@ -0,0 +1,11 @@
{
imports = [
./osd.nix
./bluetooth.nix
./network.nix
./audio.nix
./brightness.nix
./devices.nix
./misc.nix
];
}
+63
View File
@@ -0,0 +1,63 @@
{
pkgs,
scripts,
...
}: let
updateOsd = ''
${scripts.waybar-osd}/bin/waybar-osd "$OSD_TEXT"
'';
in {
scripts.output-cycle = pkgs.writeShellScriptBin "output-cycle" ''
wpctl=${pkgs.wireplumber}/bin/wpctl
jq=${pkgs.jq}/bin/jq
pwdump=${pkgs.pipewire}/bin/pw-dump
# Sink IDs in pw-dump order.
ids=($("$pwdump" | "$jq" -r '.[] | select(.info.props."media.class"=="Audio/Sink") | .id'))
n=''${#ids[@]}
[ "$n" -eq 0 ] && exit 0
cur=$("$wpctl" inspect @DEFAULT_AUDIO_SINK@ 2>/dev/null | sed -n 's/^id \([0-9]*\),.*/\1/p')
next_index=0
for i in "''${!ids[@]}"; do
if [ "''${ids[$i]}" = "$cur" ]; then
next_index=$(( (i + 1) % n ))
break
fi
done
next=''${ids[$next_index]}
"$wpctl" set-default "$next"
desc=$("$pwdump" | "$jq" -r --argjson id "$next" '.[] | select(.id==$id) | .info.props."node.description"')
OSD_TEXT="󰓃 ''${desc:-Output}"
${updateOsd}
'';
scripts.input-cycle = pkgs.writeShellScriptBin "input-cycle" ''
wpctl=${pkgs.wireplumber}/bin/wpctl
jq=${pkgs.jq}/bin/jq
pwdump=${pkgs.pipewire}/bin/pw-dump
# Source IDs in pw-dump order.
ids=($("$pwdump" | "$jq" -r '.[] | select(.info.props."media.class"=="Audio/Source") | .id'))
n=''${#ids[@]}
[ "$n" -eq 0 ] && exit 0
cur=$("$wpctl" inspect @DEFAULT_AUDIO_SOURCE@ 2>/dev/null | sed -n 's/^id \([0-9]*\),.*/\1/p')
next_index=0
for i in "''${!ids[@]}"; do
if [ "''${ids[$i]}" = "$cur" ]; then
next_index=$(( (i + 1) % n ))
break
fi
done
next=''${ids[$next_index]}
"$wpctl" set-default "$next"
desc=$("$pwdump" | "$jq" -r --argjson id "$next" '.[] | select(.id==$id) | .info.props."node.description"')
OSD_TEXT="󰍬 ''${desc:-Input}"
${updateOsd}
'';
}
+65
View File
@@ -0,0 +1,65 @@
{
pkgs,
scripts,
...
}: let
updateOsd = ''
${scripts.waybar-osd}/bin/waybar-osd "$OSD_TEXT"
'';
in {
scripts.waybar-toggle = pkgs.writeShellScriptBin "waybar-toggle" ''
if pidof waybar > /dev/null; then
pkill waybar
else
hyprctl dispatch exec waybar
fi
'';
scripts.color-pick = pkgs.writeShellScriptBin "color-pick" ''
# hyprpicker -a already copies the color to the clipboard.
color=$(${pkgs.hyprpicker}/bin/hyprpicker -a 2>/dev/null)
if [ -n "$color" ]; then
OSD_TEXT="󰈊 $color"
else
OSD_TEXT="󰈊 Cancelled"
fi
${updateOsd}
'';
scripts.screenshot-edit = pkgs.writeShellScriptBin "screenshot-edit" ''
${pkgs.hyprshot}/bin/hyprshot -m region --raw 2>/dev/null \
| ${pkgs.satty}/bin/satty --filename -
'';
scripts.record-toggle = pkgs.writeShellScriptBin "record-toggle" ''
if pgrep -x wf-recorder >/dev/null; then
# -INT lets wf-recorder finalize the file cleanly.
pkill -INT -x wf-recorder
OSD_TEXT="󰕧 Recording saved"
else
dir="$HOME/Videos"
mkdir -p "$dir"
file="$dir/rec-$(date +%Y%m%d-%H%M%S).mp4"
${pkgs.wf-recorder}/bin/wf-recorder -f "$file" >/dev/null 2>&1 &
OSD_TEXT="󰑊 Recording…"
fi
${updateOsd}
'';
scripts.airplane-toggle = pkgs.writeShellScriptBin "airplane-toggle" ''
# Reuse nmcli + bluetoothctl (unprivileged) instead of rfkill.
on=false
nmcli radio wifi 2>/dev/null | grep -q enabled && on=true
bluetoothctl show 2>/dev/null | grep -q "Powered: yes" && on=true
if $on; then
nmcli radio wifi off 2>/dev/null
bluetoothctl power off >/dev/null 2>&1 || true
OSD_TEXT="󰀝 Airplane On"
else
nmcli radio wifi on 2>/dev/null
bluetoothctl power on >/dev/null 2>&1 || true
OSD_TEXT="󰀞 Airplane Off"
fi
${updateOsd}
'';
}
+77
View File
@@ -0,0 +1,77 @@
{
pkgs,
scripts,
...
}: let
updateOsd = ''
${scripts.waybar-osd}/bin/waybar-osd "$OSD_TEXT"
'';
in {
scripts.networkScript = pkgs.writeShellScript "waybar-network" ''
nmcli=${pkgs.networkmanager}/bin/nmcli
ip=${pkgs.iproute2}/bin/ip
jq=${pkgs.jq}/bin/jq
nl=$'\n'
text=""
tip=""
class="disconnected"
add_seg() { text="''${text:+$text }$1"; }
add_tip() { tip="''${tip:+$tip$nl}$1"; }
# ── Ethernet ────────────────────────────────────────────────
eth=$("$nmcli" -t -f DEVICE,TYPE,STATE device status 2>/dev/null \
| awk -F: '$2 == "ethernet" && $3 == "connected" { print $1; exit }')
if [ -n "$eth" ]; then
eth_ip=$("$ip" -4 -br addr show "$eth" 2>/dev/null | awk '{ print $3 }' | cut -d/ -f1)
add_seg "󰛳"
class="ethernet"
add_tip "󰛳 Ethernet''${eth_ip:+ · $eth_ip}"
fi
# ── Wi-Fi ───────────────────────────────────────────────────
wifi=$("$nmcli" -t -f ACTIVE,SSID,SIGNAL device wifi 2>/dev/null | grep -m1 '^yes:')
if [ -n "$wifi" ]; then
ssid=$(printf '%s' "$wifi" | cut -d: -f2)
signal=$(printf '%s' "$wifi" | cut -d: -f3)
if [ "''${signal:-0}" -ge 80 ]; then icon="󰤨"
elif [ "''${signal:-0}" -ge 60 ]; then icon="󰤥"
elif [ "''${signal:-0}" -ge 40 ]; then icon="󰤢"
elif [ "''${signal:-0}" -ge 20 ]; then icon="󰤟"
else icon="󰤯"
fi
add_seg "$icon"
[ "$class" = "disconnected" ] && class="wifi"
add_tip "$icon ''${ssid:-Wi-Fi}''${signal:+ · $signal%}"
fi
# ── VPN (nmcli, fallback interfaces) ────────────────────────
vpn=$("$nmcli" -t -f TYPE,NAME connection show --active 2>/dev/null \
| awk -F: '$1 == "vpn" || $1 == "wireguard" { print $2; exit }')
if [ -z "$vpn" ]; then
vpn=$("$ip" -br link show 2>/dev/null \
| awk '($2 == "UP" || $2 == "UNKNOWN") && $1 ~ /^(tun|wg|proton|ppp)/ { sub(/@.*/, "", $1); print $1; exit }')
fi
if [ -n "$vpn" ]; then
add_seg "󰦝"
add_tip "󰦝 VPN · $vpn"
fi
if [ -z "$text" ]; then
text="󰤭"
tip="Disconnected"
fi
"$jq" -cn --arg text "$text" --arg class "$class" --arg tooltip "$tip" \
'{ text: $text, class: $class, tooltip: $tooltip }'
'';
scripts.wifi-toggle = pkgs.writeShellScriptBin "wifi-toggle" ''
if ${pkgs.networkmanager}/bin/nmcli radio wifi | grep -q enabled; then
${pkgs.networkmanager}/bin/nmcli radio wifi off
else
${pkgs.networkmanager}/bin/nmcli radio wifi on
fi
${updateOsd}
'';
}
+27
View File
@@ -0,0 +1,27 @@
{pkgs, ...}: {
scripts.waybar-osd = pkgs.writeShellApplication {
name = "waybar-osd";
runtimeInputs = with pkgs; [procps coreutils];
text = ''
printf '%s' "$1" > "$XDG_RUNTIME_DIR/waybar-osd"
pkill -f -RTMIN+8 '^waybar$' 2>/dev/null || true
'';
};
scripts.waybar-osd-status = pkgs.writeShellApplication {
name = "waybar-osd-status";
runtimeInputs = with pkgs; [coreutils];
text = ''
file="$XDG_RUNTIME_DIR/waybar-osd"
[ -f "$file" ] || exit 1
mtime=$(stat -c %Y "$file" 2>/dev/null) || exit 1
age=$(( $(date +%s) - mtime ))
if [ "$age" -ge 3 ]; then
rm -f "$file"
exit 1
fi
'';
};
_module.args.osdPath = "$XDG_RUNTIME_DIR/waybar-osd";
}
+7 -14
View File
@@ -2,11 +2,6 @@
config, config,
pkgs, pkgs,
osdPath, osdPath,
networkScript,
bluetoothScript,
caffeineToggleScript,
osdStatusScript,
nightshiftToggleScript,
... ...
}: let }: let
gaps-out = config.theme.gaps-out; gaps-out = config.theme.gaps-out;
@@ -18,9 +13,7 @@ in {
position = "top"; position = "top";
height = config.theme.bar-height; height = config.theme.bar-height;
margin = "${toString gaps-out} ${toString gaps-out} 0"; margin = "${toString gaps-out} ${toString gaps-out} 0";
modules-center = ["custom/osd" "custom/osd-sep" "clock" "tray" "hyprland/workspaces" "custom/network" "custom/bluetooth" "battery" "group/drawer"]; modules-center = ["custom/osd" "custom/osd-sep" "clock" "tray" "hyprland/workspaces" "custom/network" "custom/bluetooth" "battery" "pulseaudio" "custom/nightshift" "custom/caffeine"];
# ── Modules ─────────────────────────────────────────────────────────
"hyprland/workspaces" = { "hyprland/workspaces" = {
format = "{id}"; format = "{id}";
@@ -55,7 +48,7 @@ in {
exec-if = "! systemctl --user is-active --quiet hypridle"; exec-if = "! systemctl --user is-active --quiet hypridle";
format = "{}"; format = "{}";
interval = 5; interval = 5;
on-click = "${caffeineToggleScript}/bin/caffeine-toggle"; on-click = "${config.scripts.caffeine-toggle}/bin/caffeine-toggle";
tooltip = false; tooltip = false;
}; };
@@ -64,12 +57,12 @@ in {
exec-if = "${pkgs.procps}/bin/pgrep -x hyprsunset"; exec-if = "${pkgs.procps}/bin/pgrep -x hyprsunset";
format = "{}"; format = "{}";
interval = 5; interval = 5;
on-click = "${nightshiftToggleScript}/bin/nightshift-toggle"; on-click = "${config.scripts.nightshift-toggle}/bin/nightshift-toggle";
tooltip = false; tooltip = false;
}; };
"custom/bluetooth" = { "custom/bluetooth" = {
exec = "${bluetoothScript}"; exec = "${config.scripts.bluetoothScript}";
exec-if = "${pkgs.bluez}/bin/bluetoothctl list 2>/dev/null | grep -q Controller"; exec-if = "${pkgs.bluez}/bin/bluetoothctl list 2>/dev/null | grep -q Controller";
return-type = "json"; return-type = "json";
interval = 5; interval = 5;
@@ -78,7 +71,7 @@ in {
"custom/osd" = { "custom/osd" = {
exec = "cat ${osdPath}"; exec = "cat ${osdPath}";
exec-if = "${osdStatusScript}/bin/waybar-osd-status"; exec-if = "${config.scripts.waybar-osd-status}/bin/waybar-osd-status";
signal = 8; signal = 8;
interval = 1; interval = 1;
format = "{}"; format = "{}";
@@ -86,7 +79,7 @@ in {
"custom/osd-sep" = { "custom/osd-sep" = {
exec = "echo '|'"; exec = "echo '|'";
exec-if = "${osdStatusScript}/bin/waybar-osd-status"; exec-if = "${config.scripts.waybar-osd-status}/bin/waybar-osd-status";
signal = 8; signal = 8;
interval = 1; interval = 1;
format = "{}"; format = "{}";
@@ -122,7 +115,7 @@ in {
}; };
"custom/network" = { "custom/network" = {
exec = "${networkScript}"; exec = "${config.scripts.networkScript}";
return-type = "json"; return-type = "json";
interval = 10; interval = 10;
on-click = "${pkgs.ghostty}/bin/ghostty +new-window -e ${pkgs.nur.repos.anotherhadi.settuings}/bin/settuings --page network"; on-click = "${pkgs.ghostty}/bin/ghostty +new-window -e ${pkgs.nur.repos.anotherhadi.settuings}/bin/settuings --page network";
@@ -1,3 +1,4 @@
# Elio is a TUI file explorer
{ {
pkgs, pkgs,
config, config,
@@ -5,16 +6,12 @@
}: let }: let
c = config.lib.stylix.colors; c = config.lib.stylix.colors;
# TODO: Waiting for a version in nixpkgs # TODO: Waiting for a version in nixpkgs
version = "1.11.2"; version = "1.12.0";
sources = { sources = {
"x86_64-linux" = { "x86_64-linux" = {
url = "https://github.com/elio-fm/elio/releases/download/v${version}/elio-${version}-x86_64-unknown-linux-gnu.tar.gz"; url = "https://github.com/elio-fm/elio/releases/download/v${version}/elio-${version}-x86_64-unknown-linux-gnu.tar.gz";
hash = "sha256-9cW/xUA9p0LEYI3/j4MGPVZvcyJpV1JYKYBuHur4sTM="; hash = "sha256-9cW/xUA9p0LEYI3/j4MGPVZvcyJpV1JYKYBuHur4sTM=";
}; };
"aarch64-linux" = {
url = "https://github.com/elio-fm/elio/releases/download/v${version}/elio-${version}-aarch64-unknown-linux-gnu.tar.gz";
hash = "sha256-zg141Dp+jHwAqzoaCNvOoC7ZFeAX+cO8HZF5W7wz1Qs=";
};
}; };
source = source =
sources.${pkgs.stdenv.hostPlatform.system} sources.${pkgs.stdenv.hostPlatform.system}
@@ -55,7 +52,12 @@
}; };
}; };
in { in {
home.packages = [elio]; home.packages = [
elio
pkgs.poppler-utils # PDF previews
pkgs.ffmpeg # media metadata and thumbnails
pkgs.resvg # SVG previews
];
xdg.desktopEntries.elio = { xdg.desktopEntries.elio = {
name = "elio"; name = "elio";
+64
View File
@@ -0,0 +1,64 @@
# Git configuration
{
config,
lib,
...
}: let
username = config.var.git.username;
email = config.var.git.email;
in {
imports = [./lazygit.nix];
programs = {
gh.enable = true;
git = {
enable = true;
ignores = [
".cache/"
".DS_Store"
".idea/"
"*.swp"
"*.elc"
"auto-save-list"
".direnv/"
"node_modules"
"result"
"result-*"
".superpowers/"
".claude/"
"docs/superpowers/"
];
settings = {
user.name = username;
user.email = email;
init.defaultBranch = "main";
pull.rebase = "true";
push.autoSetupRemote = true;
color.ui = "1";
alias = {
co = "checkout";
fuck = "commit --amend -m";
c = "commit -m";
ca = "commit -am";
forgor = "commit --amend --no-edit";
graph = "log --all --decorate --graph --oneline";
oops = "checkout --";
l = "log";
r = "rebase";
s = "status --short";
ss = "status";
d = "diff";
ps = "!git push origin $(git rev-parse --abbrev-ref HEAD)";
pl = "!git pull origin $(git rev-parse --abbrev-ref HEAD)";
st = "status";
br = "branch";
hist = ''log --pretty=format:"%Cgreen%h %Creset%cd %Cblue[%cn] %Creset%s%C(yellow)%d%C(reset)" --graph --date=relative --decorate --all'';
llog = ''log --graph --name-status --pretty=format:"%C(red)%h %C(reset)(%cd) %C(green)%an %Creset%s %C(yellow)%d%Creset" --date=relative'';
};
};
};
};
home.persistence."/persist" = lib.mkIf (config.var.impermanenceEnabled or false) {
directories = [".config/gh"];
};
}
@@ -1,10 +1,10 @@
# This file is used to sign git commits using an SSH key. # This file is used to sign git commits using an SSH key.
{ {
# CHANGEME: change this to your own SSH key. # CHANGEME: change this to your own SSH key.
# ~/.ssh/key.pub itself is managed by sops (see hosts/laptop/secrets/default.nix: signing-pub-key).
home.file.".ssh/allowed_signers".text = "* ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIINhWby7lUUXQNKbRu9/UOrGjWDf3fvoAwGHomWv/+lL"; home.file.".ssh/allowed_signers".text = "* ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIINhWby7lUUXQNKbRu9/UOrGjWDf3fvoAwGHomWv/+lL";
programs.git = { programs.git = {
signing.format = "openpgp";
settings = { settings = {
commit.gpgsign = true; commit.gpgsign = true;
gpg.ssh.allowedSignersFile = "~/.ssh/allowed_signers"; gpg.ssh.allowedSignersFile = "~/.ssh/allowed_signers";
+45
View File
@@ -0,0 +1,45 @@
# Kew is a TUI audio player
{
pkgs,
lib,
config,
...
}: let
kewLauncher = pkgs.writeShellScript "kew-launcher" ''
if [ -n "$1" ]; then
exec ${pkgs.kew}/bin/kew play "$1"
else
exec ${pkgs.kew}/bin/kew
fi
'';
in {
home.packages = with pkgs; [
kew
];
home.persistence."/persist" = lib.mkIf (config.var.impermanenceEnabled or false) {
directories = [
".config/kew"
];
};
xdg.desktopEntries.kew = {
name = "Music Player";
exec = "${pkgs.ghostty}/bin/ghostty --window-padding-x=0 --window-padding-y=0 -e ${kewLauncher} %f";
comment = "Play music from the terminal";
categories = ["Audio" "Music"];
terminal = false;
mimeType = [
"audio/aac"
"audio/mpeg"
"audio/ogg"
"audio/opus"
"audio/vnd.wave"
"audio/webm"
"audio/x-matroska"
"audio/flac"
"audio/mp4"
];
settings.Keywords = "lofi;player;music;";
};
}
+21
View File
@@ -0,0 +1,21 @@
{pkgs, ...}: let
btopPackage = pkgs.btop.override {cudaSupport = true;};
in {
programs.btop = {
enable = true;
package = btopPackage;
settings = {
vim_keys = true;
shown_boxes = "cpu mem net proc gpu0";
};
};
xdg.desktopEntries.btop = {
name = "btop++";
exec = "${pkgs.ghostty}/bin/ghostty +new-window -e ${btopPackage}/bin/btop";
comment = "Ressources monitor (btop)";
categories = ["Settings"];
terminal = false;
settings.Keywords = "ressource;ressources;btop;usage;monitoring;cpu;gpu;memory";
};
}
+5
View File
@@ -0,0 +1,5 @@
{
programs = {
cava.enable = true;
};
}
@@ -5,16 +5,10 @@
inputs, inputs,
... ...
}: { }: {
home.packages = import ./cybersecurity-packages.nix { home.packages = import ./pkgs.nix {
inherit pkgs; inherit pkgs;
}; };
systemd.user.tmpfiles.rules = [
"d %h/Cyber/tmp 0755 - - -"
"d %h/Cyber/wordlists 0755 - - -"
"d %h/Cyber/wordlists/rules 0755 - - -"
];
home.file = { home.file = {
"Cyber/wordlists/seclists".source = pkgs.fetchFromGitHub { "Cyber/wordlists/seclists".source = pkgs.fetchFromGitHub {
owner = "danielmiessler"; owner = "danielmiessler";
@@ -43,6 +37,7 @@
hash = "sha256-T0XkMHJZQiy63/j25nGTkaiDFjc+blmgEVGTAFgVylU="; hash = "sha256-T0XkMHJZQiy63/j25nGTkaiDFjc+blmgEVGTAFgVylU=";
}; };
# Open source list of default credentials
".cache/default-creds".source = inputs.default-creds; ".cache/default-creds".source = inputs.default-creds;
}; };
+6
View File
@@ -0,0 +1,6 @@
{lib, ...}: let
utils = import ../../lib/utils.nix {inherit lib;};
in {
imports =
utils.importAll ./.;
}
+25
View File
@@ -0,0 +1,25 @@
# Feature-rich TUI RSS reader based on the news-flash library
{
config,
lib,
pkgs,
pkgs-unstable,
...
}: {
home.packages = [
pkgs-unstable.eilmeldung
];
home.persistence."/persist" = lib.mkIf (config.var.impermanenceEnabled or false) {
directories = [".config/eilmeldung"];
};
xdg.desktopEntries.eilmeldung = {
name = "eilmeldung";
exec = "${pkgs.ghostty}/bin/ghostty +new-window -e ${pkgs-unstable.eilmeldung}/bin/eilmeldung";
comment = "Feature-rich TUI RSS reader";
categories = ["Network" "ConsoleOnly"];
terminal = false;
settings.Keywords = "rss;feed;news;reader";
};
}
+15
View File
@@ -0,0 +1,15 @@
# Monitui allows you to edit hyprland's monitor configuration
{pkgs, ...}: {
home.packages = [
pkgs.nur.repos.anotherhadi.monitui
];
xdg.desktopEntries.monitui = {
name = "monitui";
exec = "${pkgs.ghostty}/bin/ghostty +new-window -e ${pkgs.nur.repos.anotherhadi.monitui}/bin/monitui";
comment = "Delightfully minimal TUI for wrangling Hyprland monitors";
categories = ["Settings" "HardwareSettings"];
terminal = false;
settings.Keywords = "hyprland;monitor;display;screen;resolution";
};
}
+15
View File
@@ -0,0 +1,15 @@
# TUI for managing USBGuard rules
{pkgs, ...}: {
home.packages = [
pkgs.nur.repos.anotherhadi.usbguard-tui
];
xdg.desktopEntries.usbguard-tui = {
name = "USBGuard TUI";
exec = "${pkgs.ghostty}/bin/ghostty +new-window -e ${pkgs.polkit}/bin/pkexec ${pkgs.nur.repos.anotherhadi.usbguard-tui}/bin/usbguard-tui";
comment = "Manage USBGuard rules";
categories = ["Settings"];
terminal = false;
settings.Keywords = "usb;usbguard;security;devices;rules";
};
}
@@ -8,31 +8,24 @@
home.packages = with pkgs; [ home.packages = with pkgs; [
# I love TUIs # I love TUIs
caligula # User-friendly, lightweight TUI for disk imaging (ISO, USB BOOT) caligula # User-friendly, lightweight TUI for disk imaging (ISO, USB BOOT)
tealdeer # Fast tldr client
sttr # A minimalist CyberChef-like, cross-platform CLI app for performing various operations on strings sttr # A minimalist CyberChef-like, cross-platform CLI app for performing various operations on strings
slides # A terminal-based presentation tool that allows you to create and deliver presentations directly from the command line slides # A terminal-based presentation tool that allows you to create and deliver presentations directly from the command line
pom # Pomodoro timer in your terminal
tabiew # A terminal-based CSV viewer and manipulator tabiew # A terminal-based CSV viewer and manipulator
jless # A pager for JSON jless # A pager for JSON
pkgs.nur.repos.anotherhadi.usbguard-tui # TUI for managing USBGuard rules
pkgs.nur.repos.anotherhadi.settuings # Terminal based settings (wifi, bluetooth, audio, ...)
pkgs.nur.repos.anotherhadi.fztea # Flipper Zero from the terminal
pkgs.nur.repos.anotherhadi.monitui # Edit hyprland's monitor configuration
pkgs.nur.repos.anotherhadi.proton-vpn-tui
dua # Tool to conveniently learn about the disk usage of directories dua # Tool to conveniently learn about the disk usage of directories
mcat # Cat command for documents / images / videos and more mcat # Cat command for documents / images / videos and more
lazyjournal lazyjournal
vhs # Create GIF from the command-line vhs # Create GIF from the command-line
andcli # 2FA for your shell, waiting for the support of Proton AUTH in the andcli nixpkgs unstable version pkgs.nur.repos.anotherhadi.settuings # Terminal based settings (wifi, bluetooth, audio, ...)
pkgs.nur.repos.anotherhadi.fztea # Flipper Zero from the terminal
pkgs-unstable.pomo # Pomodoro timer in your terminal
httpie # Command-line HTTP client, a user-friendly cURL replacement httpie # Command-line HTTP client, a user-friendly cURL replacement
gh # GitHub
figlet # Transform text into ASCII art figlet # Transform text into ASCII art
pastel # Command-line tool to generate, analyze, convert and manipulate colors pastel # Command-line tool to generate, analyze, convert and manipulate colors
imagemagick # Image manipulation tool imagemagick # Image manipulation tool
chafa # Image to ANSI/Unicode converter and more. chafa # Image to ANSI/Unicode converter and more.
proton-vpn-cli pkgs-unstable.yt-dlp
pkgs-unstable.eilmeldung
# Just cool # Just cool
tty-solitaire tty-solitaire
@@ -43,10 +36,5 @@
cmatrix cmatrix
fastfetch fastfetch
smassh # typing test, 10fastfinger like smassh # typing test, 10fastfinger like
minesweep-rs # Mine sweeper
]; ];
home.persistence."/persist" = lib.mkIf (config.var.impermanenceEnabled or false) {
directories = [".config/gh" ".config/gh-dash"];
};
} }
+8
View File
@@ -0,0 +1,8 @@
{
# TODO: proton-pass-agent ?
imports = [
./proton-auth.nix
./proton-calendar.nix
./proton-vpn.nix
];
}
+26
View File
@@ -0,0 +1,26 @@
{
config,
lib,
pkgs,
pkgs-unstable,
...
}: {
home.packages = [
pkgs-unstable.andcli # 2FA TUI for your shell
];
home.persistence."/persist" = lib.mkIf (config.var.impermanenceEnabled or false) {
directories = [".config/andcli"];
};
programs.zsh.shellAliases."andcli" = "${pkgs-unstable.andcli}/bin/andcli ~/.cache/protonauth.json.txt --type protonauth";
xdg.desktopEntries.andcli = {
name = "andcli";
exec = "${pkgs.ghostty}/bin/ghostty +new-window -e ${pkgs-unstable.andcli}/bin/andcli ${config.home.homeDirectory}/.cache/protonauth.json.txt --type protonauth";
comment = "2FA TUI for your shell";
categories = ["Utility" "Security" "ConsoleOnly"];
terminal = false;
settings.Keywords = "2fa;otp;totp;authenticator;security";
};
}
+7
View File
@@ -0,0 +1,7 @@
{pkgs, ...}: {
# TODO: config + desktop file
home.packages = [
pkgs.nur.repos.anotherhadi.proton-vpn-tui
pkgs.proton-vpn-cli
];
}
@@ -24,6 +24,11 @@ in {
"bg" = "-1"; "bg" = "-1";
"prompt" = muted; "prompt" = muted;
"pointer" = accent; "pointer" = accent;
"border" = muted;
"preview-border" = muted;
"scrollbar" = muted;
"preview-scrollbar" = muted;
"gutter" = muted;
}; };
defaultOptions = [ defaultOptions = [
@@ -15,9 +15,6 @@
COLORTERM = "truecolor"; COLORTERM = "truecolor";
MANPAGER = "bat -l man -p"; MANPAGER = "bat -l man -p";
}; };
persistence."/persist" = lib.mkIf (config.var.impermanenceEnabled or false) {
files = [".zsh_history"];
};
}; };
programs.zsh = { programs.zsh = {
@@ -37,6 +34,7 @@
]; ];
history = { history = {
path = lib.mkIf (config.var.impermanenceEnabled or false) "/persist${config.home.homeDirectory}/.zsh_history";
ignoreDups = true; ignoreDups = true;
findNoDups = true; findNoDups = true;
expireDuplicatesFirst = true; expireDuplicatesFirst = true;
@@ -63,7 +61,6 @@
cd = "z"; cd = "z";
ls = "eza --icons=always --no-quotes"; ls = "eza --icons=always --no-quotes";
tree = "eza --icons=always --tree --no-quotes"; tree = "eza --icons=always --tree --no-quotes";
cat = "bat --theme=base16 --color=always --paging=never --tabs=2 --wrap=never --plain";
mkdir = "mkdir -p"; mkdir = "mkdir -p";
nix-shell = "nix-shell --command zsh"; nix-shell = "nix-shell --command zsh";
grep = "rg --color=auto"; grep = "rg --color=auto";
@@ -118,6 +115,33 @@
'') '')
# bash # bash
'' ''
# cat: preview images inline (kitty graphics protocol, works in Ghostty)
# instead of dumping bat's binary garbage; falls back to bat otherwise.
cat() {
local -a imgs rest
local f
for f in "$@"; do
case "''${f:l}" in
*.png|*.jpg|*.jpeg|*.gif|*.bmp|*.webp|*.tiff|*.ico)
imgs+=("$f")
;;
*)
rest+=("$f")
;;
esac
done
if (( ''${#imgs} )); then
for f in "''${imgs[@]}"; do
chafa --format=kitty "$f"
done
fi
if (( ''${#rest} || ! ''${#imgs} )); then
bat --theme=base16 --color=always --paging=never --tabs=2 --wrap=never --plain "''${rest[@]}"
fi
}
# Suffix Aliases # Suffix Aliases
alias -s {nix,md,txt,yml,yaml,go}=nvim alias -s {nix,md,txt,yml,yaml,go}=nvim
alias -s {json,jsonl}=jless alias -s {json,jsonl}=jless
@@ -145,6 +169,7 @@
bindkey '^[[1;5C' forward-word bindkey '^[[1;5C' forward-word
bindkey '^[[1;5D' backward-word bindkey '^[[1;5D' backward-word
bindkey '^F' _fzf_file_no_hidden bindkey '^F' _fzf_file_no_hidden
bindkey '^R' fzf-history-widget
bindkey '^[[A' history-substring-search-up bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down bindkey '^[[B' history-substring-search-down
} }
@@ -2,14 +2,12 @@
# theming, a live visualizer and synced lyrics. # theming, a live visualizer and synced lyrics.
{ {
config, config,
pkgs-unstable,
pkgs, pkgs,
lib, lib,
... ...
}: let }: {
# https://github.com/NixOS/nixpkgs/pull/555072 home.packages = [pkgs-unstable.myx];
myx = pkgs.nur.repos.anotherhadi.myx;
in {
home.packages = [myx];
home.persistence."/persist" = lib.mkIf (config.var.impermanenceEnabled or false) { home.persistence."/persist" = lib.mkIf (config.var.impermanenceEnabled or false) {
directories = [".config/myx"]; directories = [".config/myx"];
@@ -17,7 +15,7 @@ in {
xdg.desktopEntries.myx = { xdg.desktopEntries.myx = {
name = "Spotify"; name = "Spotify";
exec = "${pkgs.ghostty}/bin/ghostty --window-padding-x=0 --window-padding-y=0 -e ${myx}/bin/myx"; exec = "${pkgs.ghostty}/bin/ghostty --window-padding-x=0 --window-padding-y=0 -e ${pkgs-unstable.myx}/bin/myx";
icon = "spotify"; icon = "spotify";
comment = "Control Spotify from the terminal"; comment = "Control Spotify from the terminal";
categories = ["Audio" "Music"]; categories = ["Audio" "Music"];
+26
View File
@@ -0,0 +1,26 @@
# TLDR provides simplified, example-based help pages for shell commands
{
pkgs,
inputs,
...
}: let
tealdeerCache = pkgs.runCommand "tealdeer-tldr-cache" {} ''
mkdir -p $out/tldr-pages
for d in ${inputs.tldr-pages-src}/pages ${inputs.tldr-pages-src}/pages.*; do
[ -d "$d" ] || continue
cp -r "$d" "$out/tldr-pages/$(basename "$d")"
done
if [ -d "$out/tldr-pages/pages" ] && [ ! -d "$out/tldr-pages/pages.en" ]; then
mv "$out/tldr-pages/pages" "$out/tldr-pages/pages.en"
fi
'';
in {
programs = {
tealdeer = {
enable = true; # Fast tldr client
enableAutoUpdates = false;
settings.directories.cache_dir = "${tealdeerCache}";
};
zsh.shellAliases."tldr" = "tldr --quiet";
};
}
+65 -7
View File
@@ -1,13 +1,17 @@
{config, ...}: { {
config,
lib,
...
}: {
imports = [ imports = [
# Mostly system related configuration
../../nixos/nvidia.nix # CHANGEME: Remove this line if you don't have an Nvidia GPU ../../nixos/nvidia.nix # CHANGEME: Remove this line if you don't have an Nvidia GPU
../../nixos/audio.nix ../../nixos/audio.nix
../../nixos/bluetooth.nix ../../nixos/bluetooth.nix
../../nixos/fonts.nix ../../nixos/fonts.nix
../../nixos/home-manager.nix ../../nixos/home-manager.nix
../../nixos/nix.nix ../../nixos/nix.nix
../../nixos/systemd-boot.nix ../../nixos/lanzaboot.nix # CHANGEME: Remove this line and uncomment the next one if you don't want to setup secure boot
# ../../nixos/systemd-boot.nix
../../nixos/tuigreet.nix ../../nixos/tuigreet.nix
../../nixos/autologin.nix # Skip first TUIGreet login, use LUKS password to unlock the keyring ../../nixos/autologin.nix # Skip first TUIGreet login, use LUKS password to unlock the keyring
../../nixos/users.nix ../../nixos/users.nix
@@ -15,13 +19,10 @@
../../nixos/hyprland.nix ../../nixos/hyprland.nix
../../nixos/steam.nix ../../nixos/steam.nix
../../nixos/kernel-hardening.nix ../../nixos/kernel-hardening.nix
../../home/programs/gui/helium/system.nix # I hate browser's configuration.. ../../home/gui/helium/system.nix # I hate browser's configuration..
# CHANGEME: You should probably remove those things: # CHANGEME: You should probably remove those things:
./wireguard.nix ./wireguard.nix
./persistence.nix # impermanence: what to keep once "/" is wiped on boot
./usbguard.nix
./disko.nix
./secrets ./secrets
# You should let those lines as is # You should let those lines as is
@@ -31,8 +32,65 @@
home-manager.users."${config.var.username}" = import ./home.nix; home-manager.users."${config.var.username}" = import ./home.nix;
# User password
users.users.${config.var.username}.hashedPassword = "$y$j9T$A7gH534UczuBxulj9IfEu1$ImRy3lpYpemRWNVIkA7efKPWXneFiqhZnEF1aMkWcD8"; # CHANGEME: This is my password users.users.${config.var.username}.hashedPassword = "$y$j9T$A7gH534UczuBxulj9IfEu1$ImRy3lpYpemRWNVIkA7efKPWXneFiqhZnEF1aMkWcD8"; # CHANGEME: This is my password
# Impermanence: declares what should survive a wipe of "/".
environment.persistence."/persist" = {
hideMounts = true;
directories = [
"/etc/NetworkManager/system-connections" # Wifi connections, VPN
"/var/lib/bluetooth" # Bluetooth connections
"/var/lib/nixos" # keeps uid/gid stable across boots
"/var/lib/systemd/coredump"
"/var/lib/upower" # battery calibration state
"/var/lib/systemd/backlight" # remembers screen brightness
"/var/lib/systemd/timers" # last-run timestamps (e.g. nix gc weekly)
"/var/log"
"/var/db/sudo/lectured" # remembers that the sudo lecture was already shown
];
files = [
"/etc/machine-id"
"/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_ed25519_key.pub"
"/etc/ssh/ssh_host_rsa_key"
"/etc/ssh/ssh_host_rsa_key.pub"
"/var/lib/systemd/random-seed" # avoid a weak entropy pool on first boot
];
};
# USBGuard:
# The following line allow all USB devices until a proper policy is configured.
# Run `sudo usbguard generate-policy` with your devices plugged in,
# then set rules = "<output>" and switch implicitPolicyTarget to "block".
# services.usbguard.implicitPolicyTarget = lib.mkForce "allow";
services.usbguard = {
enable = true;
implicitPolicyTarget = "block";
IPCAllowedUsers = [
"root"
];
rules = ''
allow id 13fd:5900 name "External"
allow id 1d6b:0003 name "xHCI Host Controller"
allow id 1d6b:0002 name "xHCI Host Controller"
allow id 0bda:c85c name "Bluetooth Radio"
allow id 30c9:009f name "HP True Vision FHD Camera"
allow id 03f0:036b name "HP USB-C Dock G5"
allow id 03f0:066b name "HP USB-C Dock G5"
allow id 03f0:056b name "USB Audio"
allow id 0bda:8153 name "USB 10/100/1000 LAN"
allow id 046d:0ab7 name "Blue Microphones"
allow id 03f0:076b name "USB5734"
allow id 1532:02a1 name "Razer Ornata V3"
allow id 03f0:046b name "HP USB-C Dock G5"
allow id 03f0:086b name "USB2734"
allow id 1b1c:1b75 name "CORSAIR HARPOON RGB PRO Gaming Mouse"
'';
};
# Don't touch this # Don't touch this
system.stateVersion = "26.05"; system.stateVersion = "26.05";
} }
+1
View File
@@ -20,6 +20,7 @@ nixpkgs.lib.nixosSystem {
inputs.helium-browser.nixosModules.default inputs.helium-browser.nixosModules.default
inputs.impermanence.nixosModules.impermanence inputs.impermanence.nixosModules.impermanence
inputs.disko.nixosModules.disko inputs.disko.nixosModules.disko
inputs.lanzaboote.nixosModules.lanzaboote
./disko.nix ./disko.nix
./configuration.nix ./configuration.nix
]; ];
+13 -43
View File
@@ -3,49 +3,18 @@
inputs, inputs,
lib, lib,
... ...
}: { }: let
imports = [ utils = import ../../home/lib/utils.nix {inherit lib;};
# Programs in {
imports =
## GUI utils.importAll ../../home/tui
../../home/programs/gui/proton ++ utils.importAll ../../home/gui
../../home/programs/gui/helium ++ utils.importAll ../../home/system # System (Desktop environment like stuff)
../../home/programs/gui/pkgs.nix ++ [
inputs.nvf-config.homeManagerModules.default # My vim config
## TUI ../../home/tui/git/signing.nix # CHANGEME: Change the key or remove this file
inputs.nvf-config.homeManagerModules.default ./variables.nix # Mostly user-specific configuration
../../home/programs/tui/ghostty ];
../../home/programs/tui/ilovetui
../../home/programs/tui/shell
../../home/programs/tui/git
../../home/programs/tui/git/lazygit.nix
../../home/programs/tui/git/signing.nix # CHANGEME: Change the key or remove this file
../../home/programs/tui/nixy
../../home/programs/tui/nix-utils
../../home/programs/tui/myx
../../home/programs/tui/elio
../../home/programs/tui/wikiman
../../home/programs/tui/navi
../../home/programs/tui/pkgs.nix
## GROUPS
../../home/programs/group/cybersecurity.nix
../../home/programs/group/dev.nix
# System (Desktop environment like stuff)
../../home/system/hyprlock
../../home/system/hyprland
../../home/system/waybar
../../home/system/swaync
../../home/system/tofi
../../home/system/mime
../../home/system/udiskie
../../home/system/termfilechooser
../../home/system/clipboard
../../home/system/hypridle
./variables.nix # Mostly user-specific configuration
];
home = { home = {
inherit (config.var) username; inherit (config.var) username;
@@ -59,6 +28,7 @@
".cache" ".cache"
".steam" ".steam"
"Notes" "Notes"
"Music"
"Projects" "Projects"
"Documents" "Documents"
"Downloads" "Downloads"
-29
View File
@@ -1,29 +0,0 @@
# Impermanence: declares what should survive a wipe of "/".
{
environment.persistence."/persist" = {
hideMounts = true;
directories = [
"/etc/NetworkManager/system-connections" # Wifi connections, VPN
"/var/lib/bluetooth" # Bluetooth connections
"/var/lib/nixos" # keeps uid/gid stable across boots
"/var/lib/systemd/coredump"
"/var/lib/upower" # battery calibration state
"/var/lib/systemd/backlight" # remembers screen brightness
"/var/lib/systemd/timers" # last-run timestamps (e.g. nix gc weekly)
"/var/log"
"/var/cache/tuigreet"
"/var/cache/vulnix"
"/var/db/sudo/lectured" # remembers that the sudo lecture was already shown
];
files = [
"/etc/machine-id"
"/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_ed25519_key.pub"
"/etc/ssh/ssh_host_rsa_key"
"/etc/ssh/ssh_host_rsa_key.pub"
"/var/lib/systemd/random-seed" # avoid a weak entropy pool on first boot
];
};
}
File diff suppressed because one or more lines are too long
-31
View File
@@ -1,31 +0,0 @@
# USBGuard:
# The following line allow all USB devices until a proper policy is configured.
# Run `sudo usbguard generate-policy` with your devices plugged in,
# then set rules = "<output>" and switch implicitPolicyTarget to "block".
# services.usbguard.implicitPolicyTarget = lib.mkForce "allow";
{
services.usbguard = {
enable = true;
implicitPolicyTarget = "block";
IPCAllowedUsers = [
"root"
];
rules = ''
allow id 13fd:5900 name "External"
allow id 1d6b:0003 name "xHCI Host Controller"
allow id 1d6b:0002 name "xHCI Host Controller"
allow id 0bda:c85c name "Bluetooth Radio"
allow id 30c9:009f name "HP True Vision FHD Camera"
allow id 03f0:036b name "HP USB-C Dock G5"
allow id 03f0:066b name "HP USB-C Dock G5"
allow id 03f0:056b name "USB Audio"
allow id 0bda:8153 name "USB 10/100/1000 LAN"
allow id 046d:0ab7 name "Blue Microphones"
allow id 03f0:076b name "USB5734"
allow id 1532:02a1 name "Razer Ornata V3"
allow id 03f0:046b name "HP USB-C Dock G5"
allow id 03f0:086b name "USB2734"
allow id 1b1c:1b75 name "CORSAIR HARPOON RGB PRO Gaming Mouse"
'';
};
}
+1 -1
View File
@@ -14,7 +14,7 @@
../../home/programs/tui/nixy ../../home/programs/tui/nixy
../../home/programs/tui/nix-utils ../../home/programs/tui/nix-utils
../../home/programs/tui/elio ../../home/programs/tui/elio
../../home/programs/tui/wikiman ../../home/programs/tui/tealdeer
../../home/programs/tui/pkgs.nix ../../home/programs/tui/pkgs.nix
../../home/programs/group/dev.nix ../../home/programs/group/dev.nix
+1 -1
View File
@@ -23,7 +23,7 @@
../../home/programs/tui/nix-utils ../../home/programs/tui/nix-utils
../../home/programs/tui/myx ../../home/programs/tui/myx
../../home/programs/tui/elio ../../home/programs/tui/elio
../../home/programs/tui/wikiman ../../home/programs/tui/tealdeer
../../home/programs/tui/navi ../../home/programs/tui/navi
../../home/programs/tui/pkgs.nix ../../home/programs/tui/pkgs.nix
+55
View File
@@ -0,0 +1,55 @@
# Secure Boot via lanzaboote (signed systemd-boot + unified kernel images)
#
# One-time setup on the machine (not handled by Nix):
# 1. sudo sbctl create-keys
# 2. Reboot into firmware setup, put Secure Boot in "Setup Mode"
# 3. nixos-rebuild switch (this signs the current generation with the new keys)
# 4. sudo sbctl enroll-keys --microsoft
# 5. Reboot, re-enable Secure Boot in firmware
{
pkgs,
lib,
...
}: {
boot = {
loader = {
efi.canTouchEfiVariables = true;
systemd-boot = {
enable = lib.mkForce false;
consoleMode = "auto";
configurationLimit = 8;
};
};
lanzaboote = {
enable = true;
pkiBundle = "/var/lib/sbctl";
};
tmp.cleanOnBoot = true;
kernelPackages = pkgs.linuxPackages; # _latest, _zen, _hardened, _rt, _rt_latest, etc.
# Silent boot
kernelParams = [
"quiet"
"splash"
"rd.systemd.show_status=false"
"rd.udev.log_level=3"
"udev.log_priority=3"
"boot.shell_on_fail"
];
consoleLogLevel = 0;
initrd.verbose = false;
};
environment.systemPackages = [pkgs.sbctl];
# To avoid systemd services hanging on shutdown
systemd.settings.Manager = {
DefaultTimeoutStopSec = "10s";
};
environment.persistence."/persist".directories = [
"/var/lib/sbctl"
];
}
+4 -2
View File
@@ -48,8 +48,6 @@ in {
# literally no documentation about this anywhere. # literally no documentation about this anywhere.
# might be good to write about this... # might be good to write about this...
# https://www.reddit.com/r/NixOS/comments/u0cdpi/tuigreet_with_xmonad_how/ # https://www.reddit.com/r/NixOS/comments/u0cdpi/tuigreet_with_xmonad_how/
# Unlock the gnome-keyring with the login password so apps (browser
# secrets via libsecret, ProtonVPN credentials, …) don't reprompt.
systemd.services.greetd.serviceConfig = { systemd.services.greetd.serviceConfig = {
Type = "idle"; Type = "idle";
StandardInput = "tty"; StandardInput = "tty";
@@ -60,4 +58,8 @@ in {
TTYVHangup = true; TTYVHangup = true;
TTYVTDisallocate = true; TTYVTDisallocate = true;
}; };
environment.persistence."/persist".directories = [
"/var/cache/tuigreet"
];
} }
+6 -1
View File
@@ -68,7 +68,12 @@ in {
]; ];
}; };
gvfs.enable = true; gvfs.enable = true;
upower.enable = true; upower = {
enable = true;
percentageLow = 10;
percentageCritical = 5;
percentageAction = 3;
};
power-profiles-daemon.enable = true; power-profiles-daemon.enable = true;
udisks2.enable = true; udisks2.enable = true;
}; };
+4
View File
@@ -24,4 +24,8 @@
RandomizedDelaySec = "1h"; RandomizedDelaySec = "1h";
}; };
}; };
environment.persistence."/persist".directories = [
"/var/cache/vulnix"
];
} }