mirror of
https://github.com/anotherhadi/nixy.git
synced 2026-10-05 11:18:24 +02:00
Generated
+122
-7
@@ -93,6 +93,21 @@
|
||||
"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": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -181,6 +196,22 @@
|
||||
}
|
||||
},
|
||||
"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,
|
||||
"locked": {
|
||||
"lastModified": 1777699697,
|
||||
@@ -406,6 +437,29 @@
|
||||
"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": {
|
||||
"locked": {
|
||||
"lastModified": 1780772958,
|
||||
@@ -513,11 +567,11 @@
|
||||
},
|
||||
"nixpkgs-unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1787135253,
|
||||
"narHash": "sha256-RD2kNWCG+Bjo6h+JVjWVNntZs2GtRoeY2xHjts/FNkA=",
|
||||
"lastModified": 1790046670,
|
||||
"narHash": "sha256-MYiI+CzL0tuWgRPjGsKCDHqYs2T3OzMlMQWOYWG0qso=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "ffb3c9b700e759be2ef13237c9d8f953b32a1e46",
|
||||
"rev": "6774f7bc253789b113a4f39285dc0fa100abeacc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -649,7 +703,7 @@
|
||||
},
|
||||
"nvf": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"flake-compat": "flake-compat_3",
|
||||
"mnw": "mnw",
|
||||
"nixpkgs": "nixpkgs_6"
|
||||
},
|
||||
@@ -677,15 +731,37 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"path": "./home/programs/tui/nvf",
|
||||
"path": "./home/nvf",
|
||||
"type": "path"
|
||||
},
|
||||
"original": {
|
||||
"path": "./home/programs/tui/nvf",
|
||||
"path": "./home/nvf",
|
||||
"type": "path"
|
||||
},
|
||||
"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": {
|
||||
"inputs": {
|
||||
"default-creds": "default-creds",
|
||||
@@ -695,6 +771,7 @@
|
||||
"helium-browser": "helium-browser",
|
||||
"home-manager": "home-manager",
|
||||
"impermanence": "impermanence",
|
||||
"lanzaboote": "lanzaboote",
|
||||
"nix-index-database": "nix-index-database",
|
||||
"nixarr": "nixarr",
|
||||
"nixos-hardware": "nixos-hardware",
|
||||
@@ -704,7 +781,29 @@
|
||||
"nvf": "nvf",
|
||||
"nvf-config": "nvf-config",
|
||||
"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": {
|
||||
@@ -855,6 +954,22 @@
|
||||
"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": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
{
|
||||
# https://github.com/anotherhadi/nixy
|
||||
description = ''
|
||||
Nixy simplifies and unifies the Hyprland ecosystem with a modular, easily customizable setup.
|
||||
It provides a structured way to manage your system configuration and dotfiles with minimal effort.
|
||||
Nixy simplifies and unifies the Hyprland ecosystem with a minimalist & easily customizable NixOS setup, heavily focused on TUIs.
|
||||
'';
|
||||
|
||||
inputs = {
|
||||
@@ -11,7 +10,7 @@
|
||||
nixos-hardware.url = "github:NixOS/nixos-hardware";
|
||||
nvf.url = "github:notashelf/nvf";
|
||||
nvf-config = {
|
||||
url = "path:./home/programs/tui/nvf";
|
||||
url = "path:./home/nvf";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.nvf.follows = "nvf";
|
||||
};
|
||||
@@ -40,6 +39,10 @@
|
||||
url = "github:nix-community/disko";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
lanzaboote = {
|
||||
url = "github:nix-community/lanzaboote";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
helium-browser = {
|
||||
url = "github:oxcl/nix-flake-helium-browser";
|
||||
inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||
@@ -52,6 +55,10 @@
|
||||
url = "github:anotherhadi/default-creds";
|
||||
flake = false;
|
||||
};
|
||||
tldr-pages-src = {
|
||||
url = "github:tldr-pages/tldr";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
# Server
|
||||
nixarr.url = "github:nix-media-server/nixarr";
|
||||
@@ -87,7 +94,7 @@
|
||||
(system: f system (import nixpkgs {inherit system;}));
|
||||
in
|
||||
merge [
|
||||
(import ./home/programs/tui/nixy/flake.nix args)
|
||||
(import ./home/tui/nixy/flake.nix args)
|
||||
{
|
||||
formatter.${system} = pkgs.alejandra;
|
||||
packages.${system}.nvim = inputs.nvf-config.packages.${system}.nvim;
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
name = "Nix 4 Cyber";
|
||||
url = "https://n4c.hadi.icu";
|
||||
}
|
||||
{
|
||||
name = "Infosec Notes - Anotherhadi";
|
||||
url = "https://hadi.icu/notes";
|
||||
}
|
||||
{
|
||||
name = "Cyberchef";
|
||||
url = "https://cyberchef.hadi.icu";
|
||||
@@ -27,10 +23,6 @@
|
||||
name = "Hack The Box";
|
||||
url = "https://hackthebox.com";
|
||||
}
|
||||
{
|
||||
name = "Iknowyou";
|
||||
url = "https://iknowyou-prod.hadi.icu";
|
||||
}
|
||||
{
|
||||
name = "Exploit-DB";
|
||||
url = "https://exploit-db.com";
|
||||
@@ -15,14 +15,6 @@
|
||||
name = "Gitea";
|
||||
url = "https://git.hadi.icu";
|
||||
}
|
||||
{
|
||||
name = "Mealie";
|
||||
url = "https://mealie.hadi.icu";
|
||||
}
|
||||
{
|
||||
name = "Wallpapers";
|
||||
url = "https://wallpapers.hadi.icu";
|
||||
}
|
||||
{
|
||||
name = "Default Creds";
|
||||
url = "https://default-creds.hadi.icu";
|
||||
@@ -61,10 +53,6 @@
|
||||
name = "AdGuard";
|
||||
url = "https://adguard.hadi.icu";
|
||||
}
|
||||
{
|
||||
name = "Umami";
|
||||
url = "https://umami.hadi.icu";
|
||||
}
|
||||
{
|
||||
name = "Cloudflare";
|
||||
url = "https://dash.cloudflare.com";
|
||||
@@ -39,11 +39,7 @@
|
||||
}
|
||||
{
|
||||
name = "Startpage Config";
|
||||
url = "https://www.startpage.com/do/mypage.pl?prfe=45d331deb05471d659dba933e7400df51d952bb103da6f6125c0e769a6be1d65610456a479f495ceeee7e97311cf227d7c1bb198de0ceeb193d8cddf9c455c19a409cc35c3e3f542ee27bd7cecd3";
|
||||
}
|
||||
{
|
||||
name = "Feedly";
|
||||
url = "https://feedly.com";
|
||||
url = "https://www.startpage.com/do/mypage.pl?prfe=2ca22738c4725515827ee9783be629ea1c26114819ab4a8c32573d96b3fc98540c50f251d1613da751834e14a9f50c7913eb528705c8ea7f067899ddd18e2d18f6f382cada947f7095cb9bead84dbb1aa455";
|
||||
}
|
||||
{
|
||||
name = "MyNixOS";
|
||||
@@ -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"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -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'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -3,8 +3,19 @@
|
||||
pkgs,
|
||||
scripts,
|
||||
...
|
||||
}: {
|
||||
home.packages = [pkgs.wl-clipboard];
|
||||
}: 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";
|
||||
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 = {
|
||||
enable = true;
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
# hypridle handles idle management: lock the screen, turn the display off, and
|
||||
# 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 = {
|
||||
enable = true;
|
||||
settings = {
|
||||
@@ -17,7 +35,7 @@
|
||||
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-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;";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,63 +46,6 @@ in {
|
||||
|
||||
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
|
||||
|
||||
# Power
|
||||
@@ -112,22 +55,22 @@ in {
|
||||
{
|
||||
key = "l";
|
||||
desc = "Lock";
|
||||
cmd = "${pkgs.hyprlock}/bin/hyprlock";
|
||||
cmd = config.xdg.desktopEntries.lock.exec;
|
||||
}
|
||||
{
|
||||
key = "s";
|
||||
desc = "Suspend";
|
||||
cmd = "systemctl suspend";
|
||||
cmd = config.xdg.desktopEntries.suspend.exec;
|
||||
}
|
||||
{
|
||||
key = "r";
|
||||
desc = "Reboot";
|
||||
cmd = "systemctl reboot";
|
||||
cmd = config.xdg.desktopEntries.reboot.exec;
|
||||
}
|
||||
{
|
||||
key = "p";
|
||||
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
|
||||
|
||||
# Utilities
|
||||
", Print, exec, ${pkgs.hyprshot}/bin/hyprshot -m region" # Capture region
|
||||
"$shiftMod, Print, exec, ${pkgs.hyprshot}/bin/hyprshot -m output" # Capture screen
|
||||
", Print, exec, ${config.xdg.desktopEntries.screenshot-region.exec}" # Capture region
|
||||
"$shiftMod, Print, exec, ${config.xdg.desktopEntries.screenshot-screen.exec}" # Capture screen
|
||||
]
|
||||
++ (builtins.concatLists (
|
||||
builtins.genList (
|
||||
|
||||
@@ -21,6 +21,7 @@ in {
|
||||
./bindings.nix
|
||||
./polkitagent.nix
|
||||
./hyprpaper.nix
|
||||
./scripts.nix
|
||||
];
|
||||
|
||||
home.packages =
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -63,6 +63,15 @@
|
||||
in {
|
||||
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 = {
|
||||
enable = true;
|
||||
settings = {
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
text = ["nvim-ghostty.desktop"];
|
||||
code = ["nvim-ghostty.desktop"];
|
||||
image = ["imv-dir.desktop"];
|
||||
audio = ["vlc.desktop"];
|
||||
audio = ["kew.desktop"];
|
||||
video = ["vlc.desktop"];
|
||||
directory = ["elio.desktop"];
|
||||
office = ["onlyoffice-desktopeditors.desktop"];
|
||||
pdf = ["onlyoffice-desktopeditors.desktop"];
|
||||
pdf = ["helium.desktop"];
|
||||
archive = ["elio.desktop"];
|
||||
browser = ["helium.desktop"];
|
||||
};
|
||||
@@ -53,21 +53,24 @@
|
||||
"image/bmp"
|
||||
"image/gif"
|
||||
"image/jpeg"
|
||||
"image/jpg"
|
||||
"image/png"
|
||||
"image/svg+xml"
|
||||
"image/tiff"
|
||||
"image/vnd.microsoft.icon"
|
||||
"image/webp"
|
||||
"image/heif"
|
||||
"image/avif"
|
||||
];
|
||||
audio = [
|
||||
"audio/aac"
|
||||
"audio/mpeg"
|
||||
"audio/ogg"
|
||||
"audio/opus"
|
||||
"audio/wav"
|
||||
"audio/vnd.wave"
|
||||
"audio/webm"
|
||||
"audio/x-matroska"
|
||||
"audio/flac"
|
||||
"audio/mp4"
|
||||
];
|
||||
video = [
|
||||
"video/mp2t"
|
||||
@@ -78,6 +81,7 @@
|
||||
"video/x-flv"
|
||||
"video/x-matroska"
|
||||
"video/x-msvideo"
|
||||
"video/quicktime"
|
||||
];
|
||||
directory = ["inode/directory"];
|
||||
office = [
|
||||
@@ -104,6 +108,8 @@
|
||||
"application/x-xz"
|
||||
"application/x-zstd"
|
||||
"application/x-compressed-tar"
|
||||
"application/x-bzip2-compressed-tar"
|
||||
"application/x-xz-compressed-tar"
|
||||
];
|
||||
browser = [
|
||||
"text/html"
|
||||
@@ -145,7 +151,6 @@ in {
|
||||
enable = true;
|
||||
createDirectories = true;
|
||||
desktop = null;
|
||||
music = null;
|
||||
publicShare = null;
|
||||
templates = null;
|
||||
extraConfig = {
|
||||
|
||||
@@ -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"];
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -18,6 +18,28 @@
|
||||
in "${builtins.substring high 1 digits}${builtins.substring low 1 digits}";
|
||||
alphaHex = opacityToHex config.theme.inactive-opacity;
|
||||
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 = {
|
||||
enable = true;
|
||||
|
||||
@@ -409,13 +431,5 @@ in {
|
||||
|
||||
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"];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Use elio as the file picker for portal dialogs (browser "select file(s)" and
|
||||
# "save download to..."). The backend is xdg-desktop-portal-termfilechooser,
|
||||
# enabled system-wide in nixos/utils.nix (extraPortals + FileChooser routing).
|
||||
# "save download to...").
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
@@ -48,9 +47,6 @@
|
||||
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";
|
||||
in {
|
||||
xdg.configFile."xdg-desktop-portal-termfilechooser/config".text = ''
|
||||
@@ -60,6 +56,6 @@ in {
|
||||
open_mode=suggested
|
||||
save_mode=suggested
|
||||
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
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
then barHeight / 2
|
||||
else config.theme.rounding;
|
||||
in {
|
||||
imports = [./desktop-actions.nix];
|
||||
imports = [./desktop-actions.nix ./scripts.nix];
|
||||
programs.tofi = {
|
||||
enable = true;
|
||||
settings = {
|
||||
|
||||
@@ -4,24 +4,6 @@
|
||||
...
|
||||
}: {
|
||||
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 = {
|
||||
name = "Mute Microphone";
|
||||
exec = "${scripts.mic-mute}/bin/mic-mute";
|
||||
@@ -31,15 +13,6 @@
|
||||
terminal = false;
|
||||
};
|
||||
|
||||
lock = {
|
||||
name = "Lock";
|
||||
exec = "${pkgs.hyprlock}/bin/hyprlock";
|
||||
icon = "system-lock-screen-symbolic";
|
||||
comment = "Lock the screen";
|
||||
categories = ["System"];
|
||||
terminal = false;
|
||||
};
|
||||
|
||||
reboot = {
|
||||
name = "Reboot";
|
||||
exec = "systemctl reboot";
|
||||
@@ -49,6 +22,15 @@
|
||||
terminal = false;
|
||||
};
|
||||
|
||||
suspend = {
|
||||
name = "Suspend";
|
||||
exec = "systemctl suspend";
|
||||
icon = "system-suspend-symbolic";
|
||||
comment = "Suspend the system";
|
||||
categories = ["System"];
|
||||
terminal = false;
|
||||
};
|
||||
|
||||
shutdown = {
|
||||
name = "Shutdown";
|
||||
exec = "systemctl poweroff";
|
||||
@@ -67,15 +49,6 @@
|
||||
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 = {
|
||||
name = "Toggle Wi-Fi";
|
||||
exec = "${scripts.wifi-toggle}/bin/wifi-toggle";
|
||||
@@ -154,16 +127,6 @@
|
||||
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 = {
|
||||
name = "Airplane Mode";
|
||||
exec = "${scripts.airplane-toggle}/bin/airplane-toggle";
|
||||
@@ -194,16 +157,6 @@
|
||||
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 = {
|
||||
name = "Logout";
|
||||
exec = "${pkgs.hyprland}/bin/hyprctl dispatch exit";
|
||||
@@ -213,16 +166,6 @@
|
||||
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 = {
|
||||
name = "Mute Volume";
|
||||
exec = "${scripts.vol-mute}/bin/vol-mute";
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
{
|
||||
services.udiskie = {
|
||||
enable = true;
|
||||
notify = true;
|
||||
notify = false;
|
||||
automount = true;
|
||||
tray = "never";
|
||||
};
|
||||
|
||||
@@ -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 "$@"
|
||||
@@ -2,23 +2,13 @@
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
scripts = import ./scripts.nix {inherit pkgs config;};
|
||||
in {
|
||||
}: {
|
||||
imports = [
|
||||
./scripts
|
||||
./settings.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;
|
||||
stylix.targets.waybar.enable = false;
|
||||
|
||||
@@ -26,35 +16,19 @@ in {
|
||||
[
|
||||
playerctl
|
||||
pavucontrol
|
||||
blueman
|
||||
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.
|
||||
systemd.user.services.battery-monitor = {
|
||||
Unit.Description = "Low-battery OSD and critical notification monitor";
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${scripts.battery-monitor}/bin/battery-monitor";
|
||||
};
|
||||
xdg.desktopEntries.waybar-toggle = {
|
||||
name = "Toggle Waybar";
|
||||
exec = "${config.scripts.waybar-toggle}/bin/waybar-toggle";
|
||||
icon = "panel-applets-symbolic";
|
||||
comment = "Show or hide the status bar";
|
||||
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 = [
|
||||
"waybar"
|
||||
];
|
||||
|
||||
@@ -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}
|
||||
'';
|
||||
}
|
||||
@@ -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
|
||||
'';
|
||||
}
|
||||
@@ -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}
|
||||
'';
|
||||
}
|
||||
@@ -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}
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
imports = [
|
||||
./osd.nix
|
||||
./bluetooth.nix
|
||||
./network.nix
|
||||
./audio.nix
|
||||
./brightness.nix
|
||||
./devices.nix
|
||||
./misc.nix
|
||||
];
|
||||
}
|
||||
@@ -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}
|
||||
'';
|
||||
}
|
||||
@@ -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}
|
||||
'';
|
||||
}
|
||||
@@ -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}
|
||||
'';
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -2,11 +2,6 @@
|
||||
config,
|
||||
pkgs,
|
||||
osdPath,
|
||||
networkScript,
|
||||
bluetoothScript,
|
||||
caffeineToggleScript,
|
||||
osdStatusScript,
|
||||
nightshiftToggleScript,
|
||||
...
|
||||
}: let
|
||||
gaps-out = config.theme.gaps-out;
|
||||
@@ -18,9 +13,7 @@ in {
|
||||
position = "top";
|
||||
height = config.theme.bar-height;
|
||||
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 ─────────────────────────────────────────────────────────
|
||||
modules-center = ["custom/osd" "custom/osd-sep" "clock" "tray" "hyprland/workspaces" "custom/network" "custom/bluetooth" "battery" "pulseaudio" "custom/nightshift" "custom/caffeine"];
|
||||
|
||||
"hyprland/workspaces" = {
|
||||
format = "{id}";
|
||||
@@ -55,7 +48,7 @@ in {
|
||||
exec-if = "! systemctl --user is-active --quiet hypridle";
|
||||
format = "{}";
|
||||
interval = 5;
|
||||
on-click = "${caffeineToggleScript}/bin/caffeine-toggle";
|
||||
on-click = "${config.scripts.caffeine-toggle}/bin/caffeine-toggle";
|
||||
tooltip = false;
|
||||
};
|
||||
|
||||
@@ -64,12 +57,12 @@ in {
|
||||
exec-if = "${pkgs.procps}/bin/pgrep -x hyprsunset";
|
||||
format = "{}";
|
||||
interval = 5;
|
||||
on-click = "${nightshiftToggleScript}/bin/nightshift-toggle";
|
||||
on-click = "${config.scripts.nightshift-toggle}/bin/nightshift-toggle";
|
||||
tooltip = false;
|
||||
};
|
||||
|
||||
"custom/bluetooth" = {
|
||||
exec = "${bluetoothScript}";
|
||||
exec = "${config.scripts.bluetoothScript}";
|
||||
exec-if = "${pkgs.bluez}/bin/bluetoothctl list 2>/dev/null | grep -q Controller";
|
||||
return-type = "json";
|
||||
interval = 5;
|
||||
@@ -78,7 +71,7 @@ in {
|
||||
|
||||
"custom/osd" = {
|
||||
exec = "cat ${osdPath}";
|
||||
exec-if = "${osdStatusScript}/bin/waybar-osd-status";
|
||||
exec-if = "${config.scripts.waybar-osd-status}/bin/waybar-osd-status";
|
||||
signal = 8;
|
||||
interval = 1;
|
||||
format = "{}";
|
||||
@@ -86,7 +79,7 @@ in {
|
||||
|
||||
"custom/osd-sep" = {
|
||||
exec = "echo '|'";
|
||||
exec-if = "${osdStatusScript}/bin/waybar-osd-status";
|
||||
exec-if = "${config.scripts.waybar-osd-status}/bin/waybar-osd-status";
|
||||
signal = 8;
|
||||
interval = 1;
|
||||
format = "{}";
|
||||
@@ -122,7 +115,7 @@ in {
|
||||
};
|
||||
|
||||
"custom/network" = {
|
||||
exec = "${networkScript}";
|
||||
exec = "${config.scripts.networkScript}";
|
||||
return-type = "json";
|
||||
interval = 10;
|
||||
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,
|
||||
config,
|
||||
@@ -5,16 +6,12 @@
|
||||
}: let
|
||||
c = config.lib.stylix.colors;
|
||||
# TODO: Waiting for a version in nixpkgs
|
||||
version = "1.11.2";
|
||||
version = "1.12.0";
|
||||
sources = {
|
||||
"x86_64-linux" = {
|
||||
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=";
|
||||
};
|
||||
"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 =
|
||||
sources.${pkgs.stdenv.hostPlatform.system}
|
||||
@@ -55,7 +52,12 @@
|
||||
};
|
||||
};
|
||||
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 = {
|
||||
name = "elio";
|
||||
@@ -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.
|
||||
{
|
||||
# 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";
|
||||
|
||||
programs.git = {
|
||||
signing.format = "openpgp";
|
||||
settings = {
|
||||
commit.gpgsign = true;
|
||||
gpg.ssh.allowedSignersFile = "~/.ssh/allowed_signers";
|
||||
@@ -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;";
|
||||
};
|
||||
}
|
||||
@@ -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";
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
programs = {
|
||||
cava.enable = true;
|
||||
};
|
||||
}
|
||||
@@ -5,16 +5,10 @@
|
||||
inputs,
|
||||
...
|
||||
}: {
|
||||
home.packages = import ./cybersecurity-packages.nix {
|
||||
home.packages = import ./pkgs.nix {
|
||||
inherit pkgs;
|
||||
};
|
||||
|
||||
systemd.user.tmpfiles.rules = [
|
||||
"d %h/Cyber/tmp 0755 - - -"
|
||||
"d %h/Cyber/wordlists 0755 - - -"
|
||||
"d %h/Cyber/wordlists/rules 0755 - - -"
|
||||
];
|
||||
|
||||
home.file = {
|
||||
"Cyber/wordlists/seclists".source = pkgs.fetchFromGitHub {
|
||||
owner = "danielmiessler";
|
||||
@@ -43,6 +37,7 @@
|
||||
hash = "sha256-T0XkMHJZQiy63/j25nGTkaiDFjc+blmgEVGTAFgVylU=";
|
||||
};
|
||||
|
||||
# Open source list of default credentials
|
||||
".cache/default-creds".source = inputs.default-creds;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{lib, ...}: let
|
||||
utils = import ../../lib/utils.nix {inherit lib;};
|
||||
in {
|
||||
imports =
|
||||
utils.importAll ./.;
|
||||
}
|
||||
@@ -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";
|
||||
};
|
||||
}
|
||||
@@ -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";
|
||||
};
|
||||
}
|
||||
@@ -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; [
|
||||
# I love TUIs
|
||||
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
|
||||
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
|
||||
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
|
||||
mcat # Cat command for documents / images / videos and more
|
||||
lazyjournal
|
||||
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
|
||||
gh # GitHub
|
||||
figlet # Transform text into ASCII art
|
||||
pastel # Command-line tool to generate, analyze, convert and manipulate colors
|
||||
imagemagick # Image manipulation tool
|
||||
chafa # Image to ANSI/Unicode converter and more.
|
||||
proton-vpn-cli
|
||||
pkgs-unstable.eilmeldung
|
||||
pkgs-unstable.yt-dlp
|
||||
|
||||
# Just cool
|
||||
tty-solitaire
|
||||
@@ -43,10 +36,5 @@
|
||||
cmatrix
|
||||
fastfetch
|
||||
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"];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
# TODO: proton-pass-agent ?
|
||||
imports = [
|
||||
./proton-auth.nix
|
||||
./proton-calendar.nix
|
||||
./proton-vpn.nix
|
||||
];
|
||||
}
|
||||
@@ -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";
|
||||
};
|
||||
}
|
||||
@@ -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";
|
||||
"prompt" = muted;
|
||||
"pointer" = accent;
|
||||
"border" = muted;
|
||||
"preview-border" = muted;
|
||||
"scrollbar" = muted;
|
||||
"preview-scrollbar" = muted;
|
||||
"gutter" = muted;
|
||||
};
|
||||
|
||||
defaultOptions = [
|
||||
@@ -15,9 +15,6 @@
|
||||
COLORTERM = "truecolor";
|
||||
MANPAGER = "bat -l man -p";
|
||||
};
|
||||
persistence."/persist" = lib.mkIf (config.var.impermanenceEnabled or false) {
|
||||
files = [".zsh_history"];
|
||||
};
|
||||
};
|
||||
|
||||
programs.zsh = {
|
||||
@@ -37,6 +34,7 @@
|
||||
];
|
||||
|
||||
history = {
|
||||
path = lib.mkIf (config.var.impermanenceEnabled or false) "/persist${config.home.homeDirectory}/.zsh_history";
|
||||
ignoreDups = true;
|
||||
findNoDups = true;
|
||||
expireDuplicatesFirst = true;
|
||||
@@ -63,7 +61,6 @@
|
||||
cd = "z";
|
||||
ls = "eza --icons=always --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";
|
||||
nix-shell = "nix-shell --command zsh";
|
||||
grep = "rg --color=auto";
|
||||
@@ -118,6 +115,33 @@
|
||||
'')
|
||||
# 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
|
||||
alias -s {nix,md,txt,yml,yaml,go}=nvim
|
||||
alias -s {json,jsonl}=jless
|
||||
@@ -145,6 +169,7 @@
|
||||
bindkey '^[[1;5C' forward-word
|
||||
bindkey '^[[1;5D' backward-word
|
||||
bindkey '^F' _fzf_file_no_hidden
|
||||
bindkey '^R' fzf-history-widget
|
||||
bindkey '^[[A' history-substring-search-up
|
||||
bindkey '^[[B' history-substring-search-down
|
||||
}
|
||||
@@ -2,14 +2,12 @@
|
||||
# theming, a live visualizer and synced lyrics.
|
||||
{
|
||||
config,
|
||||
pkgs-unstable,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
# https://github.com/NixOS/nixpkgs/pull/555072
|
||||
myx = pkgs.nur.repos.anotherhadi.myx;
|
||||
in {
|
||||
home.packages = [myx];
|
||||
}: {
|
||||
home.packages = [pkgs-unstable.myx];
|
||||
|
||||
home.persistence."/persist" = lib.mkIf (config.var.impermanenceEnabled or false) {
|
||||
directories = [".config/myx"];
|
||||
@@ -17,7 +15,7 @@ in {
|
||||
|
||||
xdg.desktopEntries.myx = {
|
||||
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";
|
||||
comment = "Control Spotify from the terminal";
|
||||
categories = ["Audio" "Music"];
|
||||
@@ -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";
|
||||
};
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
{config, ...}: {
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
# Mostly system related configuration
|
||||
../../nixos/nvidia.nix # CHANGEME: Remove this line if you don't have an Nvidia GPU
|
||||
../../nixos/audio.nix
|
||||
../../nixos/bluetooth.nix
|
||||
../../nixos/fonts.nix
|
||||
../../nixos/home-manager.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/autologin.nix # Skip first TUIGreet login, use LUKS password to unlock the keyring
|
||||
../../nixos/users.nix
|
||||
@@ -15,13 +19,10 @@
|
||||
../../nixos/hyprland.nix
|
||||
../../nixos/steam.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:
|
||||
./wireguard.nix
|
||||
./persistence.nix # impermanence: what to keep once "/" is wiped on boot
|
||||
./usbguard.nix
|
||||
./disko.nix
|
||||
./secrets
|
||||
|
||||
# You should let those lines as is
|
||||
@@ -31,8 +32,65 @@
|
||||
|
||||
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
|
||||
|
||||
# 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
|
||||
system.stateVersion = "26.05";
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ nixpkgs.lib.nixosSystem {
|
||||
inputs.helium-browser.nixosModules.default
|
||||
inputs.impermanence.nixosModules.impermanence
|
||||
inputs.disko.nixosModules.disko
|
||||
inputs.lanzaboote.nixosModules.lanzaboote
|
||||
./disko.nix
|
||||
./configuration.nix
|
||||
];
|
||||
|
||||
+13
-43
@@ -3,49 +3,18 @@
|
||||
inputs,
|
||||
lib,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
# Programs
|
||||
|
||||
## GUI
|
||||
../../home/programs/gui/proton
|
||||
../../home/programs/gui/helium
|
||||
../../home/programs/gui/pkgs.nix
|
||||
|
||||
## TUI
|
||||
inputs.nvf-config.homeManagerModules.default
|
||||
../../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
|
||||
];
|
||||
}: let
|
||||
utils = import ../../home/lib/utils.nix {inherit lib;};
|
||||
in {
|
||||
imports =
|
||||
utils.importAll ../../home/tui
|
||||
++ utils.importAll ../../home/gui
|
||||
++ utils.importAll ../../home/system # System (Desktop environment like stuff)
|
||||
++ [
|
||||
inputs.nvf-config.homeManagerModules.default # My vim config
|
||||
../../home/tui/git/signing.nix # CHANGEME: Change the key or remove this file
|
||||
./variables.nix # Mostly user-specific configuration
|
||||
];
|
||||
|
||||
home = {
|
||||
inherit (config.var) username;
|
||||
@@ -59,6 +28,7 @@
|
||||
".cache"
|
||||
".steam"
|
||||
"Notes"
|
||||
"Music"
|
||||
"Projects"
|
||||
"Documents"
|
||||
"Downloads"
|
||||
|
||||
@@ -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
@@ -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"
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
../../home/programs/tui/nixy
|
||||
../../home/programs/tui/nix-utils
|
||||
../../home/programs/tui/elio
|
||||
../../home/programs/tui/wikiman
|
||||
../../home/programs/tui/tealdeer
|
||||
../../home/programs/tui/pkgs.nix
|
||||
|
||||
../../home/programs/group/dev.nix
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
../../home/programs/tui/nix-utils
|
||||
../../home/programs/tui/myx
|
||||
../../home/programs/tui/elio
|
||||
../../home/programs/tui/wikiman
|
||||
../../home/programs/tui/tealdeer
|
||||
../../home/programs/tui/navi
|
||||
../../home/programs/tui/pkgs.nix
|
||||
|
||||
|
||||
@@ -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
@@ -48,8 +48,6 @@ in {
|
||||
# literally no documentation about this anywhere.
|
||||
# might be good to write about this...
|
||||
# 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 = {
|
||||
Type = "idle";
|
||||
StandardInput = "tty";
|
||||
@@ -60,4 +58,8 @@ in {
|
||||
TTYVHangup = true;
|
||||
TTYVTDisallocate = true;
|
||||
};
|
||||
|
||||
environment.persistence."/persist".directories = [
|
||||
"/var/cache/tuigreet"
|
||||
];
|
||||
}
|
||||
|
||||
+6
-1
@@ -68,7 +68,12 @@ in {
|
||||
];
|
||||
};
|
||||
gvfs.enable = true;
|
||||
upower.enable = true;
|
||||
upower = {
|
||||
enable = true;
|
||||
percentageLow = 10;
|
||||
percentageCritical = 5;
|
||||
percentageAction = 3;
|
||||
};
|
||||
power-profiles-daemon.enable = true;
|
||||
udisks2.enable = true;
|
||||
};
|
||||
|
||||
@@ -24,4 +24,8 @@
|
||||
RandomizedDelaySec = "1h";
|
||||
};
|
||||
};
|
||||
|
||||
environment.persistence."/persist".directories = [
|
||||
"/var/cache/vulnix"
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user