summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/gkleen@sif')
-rw-r--r--accounts/gkleen@sif/default.nix241
-rw-r--r--accounts/gkleen@sif/emacs.el2
-rw-r--r--accounts/gkleen@sif/niri/default.nix440
-rw-r--r--accounts/gkleen@sif/niri/mako.nix53
-rw-r--r--accounts/gkleen@sif/niri/swayosd.nix7
-rw-r--r--accounts/gkleen@sif/niri/waybar.nix21
-rw-r--r--accounts/gkleen@sif/synadm/default.nix9
-rw-r--r--accounts/gkleen@sif/synadm/synadm_yaml15
-rw-r--r--accounts/gkleen@sif/systemd.nix34
-rw-r--r--accounts/gkleen@sif/utils/async-yt-dlp.nix57
-rw-r--r--accounts/gkleen@sif/utils/pdf2pdf.nix8
-rw-r--r--accounts/gkleen@sif/zshrc140
12 files changed, 726 insertions, 301 deletions
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix
index 6574af9d..64434bb8 100644
--- a/accounts/gkleen@sif/default.nix
+++ b/accounts/gkleen@sif/default.nix
@@ -50,9 +50,20 @@ let
50 }; 50 };
51 51
52 lockCommand = "${lib.getExe' config.systemd.package "systemctl"} --user start gtklock.service"; 52 lockCommand = "${lib.getExe' config.systemd.package "systemctl"} --user start gtklock.service";
53
54 editor = pkgs.symlinkJoin {
55 inherit (cfg.services.emacs.package) name;
56 buildInputs = with pkgs; [ makeWrapper ];
57 paths = [ cfg.services.emacs.package ];
58 postBuild = ''
59 wrapProgram $out/bin/emacsclient \
60 --inherit-argv0 \
61 --add-flags ${lib.escapeShellArg (lib.escapeShellArgs cfg.services.emacs.client.arguments)}
62 '';
63 };
53in { 64in {
54 imports = with flake.nixosModules.userProfiles.${userName}; [ 65 imports = with flake.nixosModules.userProfiles.${userName}; [
55 mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) 66 zsh tmux mpv yt-dlp (args: import ./xcompose.nix (inputs // args))
56 ]; 67 ];
57 68
58 config = { 69 config = {
@@ -60,16 +71,17 @@ in {
60 imports = [ 71 imports = [
61 ./libvirt 72 ./libvirt
62 ./niri 73 ./niri
63 flakeInputs.nix-index-database.hmModules.nix-index 74 ./synadm
75 flakeInputs.nix-index-database.homeModules.nix-index
64 flakeInputs.impermanence.nixosModules.home-manager.impermanence 76 flakeInputs.impermanence.nixosModules.home-manager.impermanence
65 ]; 77 ];
66 78
67 home.stateVersion = "20.09"; 79 home.stateVersion = "20.09";
68 80
69 nixpkgs.config = { 81 # nixpkgs.config = {
70 allowUnfree = true; 82 # allowUnfree = true;
71 zathura.useMupdf = false; 83 # zathura.useMupdf = false;
72 }; 84 # };
73 85
74 nix.registry = { 86 nix.registry = {
75 "flk" = { 87 "flk" = {
@@ -160,6 +172,7 @@ in {
160 }; 172 };
161 }; 173 };
162 }; 174 };
175 chromium.enable = true;
163 176
164 zathura = { 177 zathura = {
165 enable = true; 178 enable = true;
@@ -175,13 +188,93 @@ in {
175 gpu-api = "vulkan"; 188 gpu-api = "vulkan";
176 }; 189 };
177 190
178 zsh.initExtra = '' 191 zsh.initContent = let
179 source ${./zshrc} 192 zshrc = pkgs.resholve.mkDerivation {
193 pname = "zshrc";
194 version = "0.0.0";
195
196 src = ./zshrc;
197
198 dontUnpack = true;
199 dontConfigure = true;
200 dontBuild = true;
201
202 installPhase = ''
203 mkdir -p $out/share
204 install "$src" $out/share/zshrc
205 '';
206
207 solutions = {
208 default = {
209 scripts = [ "share/zshrc" ];
210 interpreter = "none";
211 inputs = with pkgs; [
212 coreutils
213 rpm
214 binutils
215 squashfsTools
216 unzip
217 cfg.programs.git.package
218 magickWrapped
219 curl
220 file
221 gnutar
222 cpio
223 magic-wormhole
224 cfg.programs.zsh.package
225 fuse
226 util-linux
227 findutils
228 qrencode
229 tty-clock
230 cfg.programs.jq.package
231 eza
232 less
233 config.systemd.package
234 config.programs.ssh.package
235 gnused
236 miniserve
237 p7zip
238 ];
239 execer = with pkgs; [
240 "cannot:${lib.getExe' rpm "rpm2cpio"}"
241 "cannot:${lib.getExe' squashfsTools "unsquashfs"}"
242 "cannot:${lib.getExe' unzip "unzip"}"
243 "cannot:${lib.getExe cfg.programs.git.package}"
244 "cannot:${lib.getExe cpio}"
245 "cannot:${lib.getExe' magic-wormhole "wormhole"}"
246 "cannot:${lib.getExe' fuse "fusermount"}"
247 "cannot:${lib.getExe less}"
248 "cannot:${lib.getExe' config.systemd.package "systemctl"}"
249 "cannot:${lib.getExe config.programs.ssh.package}"
250 "cannot:${lib.getExe' p7zip "7z"}"
251 ];
252 wrapper = with pkgs; [
253 "${lib.getExe' magickWrapped "magick"}:${lib.getExe' imagemagick "magick"}"
254 ];
255 fake = {
256 builtin = ["print"];
257 external = ["sudo" "umount"];
258 };
259 };
260 };
261 };
262 magickWrapped = pkgs.symlinkJoin {
263 inherit (pkgs.imagemagick) name;
264 paths = [ pkgs.imagemagick ];
265
266 buildInputs = with pkgs; [ makeWrapper ];
267 postBuild = ''
268 wrapProgram $out/bin/magick \
269 --prefix PATH : ${lib.makeBinPath (with pkgs; [ ghostscript ])}
270 '';
271 };
272 in ''
273 source ${zshrc}/share/zshrc
180 ''; 274 '';
181 zsh.dirHashes = let 275 zsh.dirHashes = let
182 flakeHashes = mapAttrs' (n: v: nameValuePair (inputNames.${n} or n) (toString v)) flakeInputs; 276 flakeHashes = mapAttrs' (n: v: nameValuePair (inputNames.${n} or n) (toString v)) flakeInputs;
183 inputNames = { 277 inputNames = {
184 "nixpkgs" = "nixos";
185 }; 278 };
186 in flakeHashes // { 279 in flakeHashes // {
187 u2w = "$HOME/projects/uni2work"; 280 u2w = "$HOME/projects/uni2work";
@@ -193,6 +286,16 @@ in {
193 pro = "$HOME/projects/pro"; 286 pro = "$HOME/projects/pro";
194 media = "$HOME/media"; 287 media = "$HOME/media";
195 }; 288 };
289 jq.colors = {
290 arrays = "1;37";
291 "false" = "0;37";
292 "null" = "2;37";
293 numbers = "0;37";
294 objectKeys = "1;34";
295 objects = "1;37";
296 strings = "0;32";
297 "true" = "0;37";
298 };
196 299
197 obs-studio = { 300 obs-studio = {
198 enable = true; 301 enable = true;
@@ -202,7 +305,7 @@ in {
202 gh = { 305 gh = {
203 enable = true; 306 enable = true;
204 settings = { 307 settings = {
205 editor = lib.getExe' config.home-manager.users.${userName}.programs.emacs.package "emacsclient"; 308 editor = lib.getExe' editor "emacsclient";
206 gitProtocol = "ssh"; 309 gitProtocol = "ssh";
207 }; 310 };
208 }; 311 };
@@ -228,16 +331,10 @@ in {
228 # notify_on_cmd_finish = "invisible 120"; 331 # notify_on_cmd_finish = "invisible 120";
229 }; 332 };
230 keybindings = { 333 keybindings = {
231 "kitty_mod+n" = "detach_window"; 334 "kitty_mod+n" = "new_os_window_with_cwd";
232 "kitty_mod+m" = "detach_window ask"; 335 "kitty_mod+m" = "detach_window ask";
233 }; 336 "kitty_mod+enter" = "new_window_with_cwd";
234 }; 337 "kitty_mod+t" = "new_tab_with_cwd";
235 wpaperd = {
236 enable = true;
237 settings.default = {
238 path = "~/.wallpapers";
239 duration = "15m";
240 mode = "center";
241 }; 338 };
242 }; 339 };
243 fuzzel = { 340 fuzzel = {
@@ -250,7 +347,7 @@ in {
250 font = "Fira Sans"; 347 font = "Fira Sans";
251 }; 348 };
252 colors = { 349 colors = {
253 background = "000000aa"; 350 background = "000000cc";
254 text = "cdd6f4ff"; 351 text = "cdd6f4ff";
255 match = "94e2d5ff"; 352 match = "94e2d5ff";
256 selection = "585b70ff"; 353 selection = "585b70ff";
@@ -267,15 +364,28 @@ in {
267 enable = true; 364 enable = true;
268 extraAbbreviations = ["i.A." "d.h." "D.h." "gdw."]; 365 extraAbbreviations = ["i.A." "d.h." "D.h." "gdw."];
269 }; 366 };
367 nushell = {
368 enable = true;
369 settings.show_banner = false;
370 };
371 fd.enable = true;
270 }; 372 };
271 373
272 services = { 374 services = {
375 wpaperd = {
376 enable = true;
377 settings.default = {
378 path = "~/.wallpapers";
379 duration = "15m";
380 mode = "center";
381 };
382 };
273 emacs = { 383 emacs = {
274 enable = true; 384 enable = true;
275 socketActivation.enable = true; 385 socketActivation.enable = true;
276 client = { 386 client = {
277 enable = true; 387 enable = true;
278 arguments = mkForce ["--reuse-frame" "--alternate-editor" "\"\""]; 388 arguments = mkForce ["--create-frame" "--alternate-editor" (lib.getExe cfg.services.emacs.package)];
279 }; 389 };
280 }; 390 };
281 gpg-agent = { 391 gpg-agent = {
@@ -384,6 +494,13 @@ in {
384 }; 494 };
385 }; 495 };
386 496
497 qt.kde.settings = {
498 kwalletrc = {
499 KSecretD.Enabled = false;
500 Wallet."Default Wallet" = "store";
501 };
502 };
503
387 xsession.preferStatusNotifierItems = true; 504 xsession.preferStatusNotifierItems = true;
388 505
389 xresources.properties = import ./xresources.nix; 506 xresources.properties = import ./xresources.nix;
@@ -395,15 +512,15 @@ in {
395 wrappedYTMDesktop libsForQt5.qt5ct playerctl evince papers 512 wrappedYTMDesktop libsForQt5.qt5ct playerctl evince papers
396 thunderbird zoom-us xdg-desktop-portal steam steam-run 513 thunderbird zoom-us xdg-desktop-portal steam steam-run
397 wireshark virt-manager rclone cached-nix-shell worktime 514 wireshark virt-manager rclone cached-nix-shell worktime
398 fira-code-symbols libreoffice xournalpp google-chrome 515 fira-code-symbols libreoffice xournalpp
399 nixos-shell virt-viewer freerdp gnome-icon-theme 516 nixos-shell virt-viewer freerdp gnome-icon-theme
400 paper-icon-theme sshpassSecret weechat element-desktop 517 paper-icon-theme sshpassSecret weechat element-desktop
401 sieve-connect gimp3 inkscape udiskie glab nitrokey-app 518 sieve-connect gimp3 inkscape udiskie glab nitrokey-app
402 pynitrokey gtklock wlrctl remmina openscad spice-record 519 pynitrokey gtklock wlrctl remmina openscad spice-record
403 libguestfs-with-appliance nerd-fonts.fira-mono 520 nerd-fonts.fira-mono
404 nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts 521 nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts
405 swtpm (hunspellWithDicts (with hunspellDicts; [en_GB-large de_DE])) 522 swtpm (hunspell.withDicts (dicts: with dicts; [en_GB-large de_DE]))
406 # synadm 523 libation libqalculate
407 ] ++ mapAttrsToList (_name: pkg: pkgs.callPackage pkg {}) (customUtils.nixImport { dir = ./utils; }); 524 ] ++ mapAttrsToList (_name: pkg: pkgs.callPackage pkg {}) (customUtils.nixImport { dir = ./utils; });
408 525
409 file = { 526 file = {
@@ -424,12 +541,9 @@ in {
424 QT_QPA_PLATFORMTHEME = "qt5ct"; 541 QT_QPA_PLATFORMTHEME = "qt5ct";
425 LIBVIRT_DEFAULT_URI = "qemu:///system"; 542 LIBVIRT_DEFAULT_URI = "qemu:///system";
426 STACK_XDG = 1; 543 STACK_XDG = 1;
427 EDITOR = pkgs.writeShellScript "editor" '' 544 EDITOR = lib.getExe' editor "emacsclient";
428 args=("--reuse-frame" "--alternate-editor" "")
429 args+=("$@")
430 exec -a emacsclient ${lib.getExe' cfg.services.emacs.package "emacsclient"} "''${args[@]}"
431 '';
432 RCLONE_PASSWORD_COMMAND = "${lib.getExe' pkgs.libsecret "secret-tool"} lookup service rclone"; 545 RCLONE_PASSWORD_COMMAND = "${lib.getExe' pkgs.libsecret "secret-tool"} lookup service rclone";
546 SYSTEMD_TINT_BACKGROUND = "false";
433 }; 547 };
434 548
435 extraProfileCommands = '' 549 extraProfileCommands = ''
@@ -466,9 +580,17 @@ in {
466 General = { 580 General = {
467 dot_as_separator = 0; 581 dot_as_separator = 0;
468 }; 582 };
583 Mode = {
584 calculate_as_you_type = 1;
585 };
469 }; 586 };
470 }; 587 };
471 "emacs/init.el".source = ./emacs.el; 588 "emacs/init.el".source = pkgs.substitute {
589 src = ./emacs.el;
590 substitutions = [
591 "--subst-var-by" "ksshaskpass" (lib.getExe pkgs.kdePackages.ksshaskpass)
592 ];
593 };
472 "systemd/user/xdg-desktop-portal.service.d/after-graphical-session.conf".text = '' 594 "systemd/user/xdg-desktop-portal.service.d/after-graphical-session.conf".text = ''
473 [Unit] 595 [Unit]
474 After=graphical-session.target 596 After=graphical-session.target
@@ -486,6 +608,8 @@ in {
486 xdg.dataFile = { 608 xdg.dataFile = {
487 "dbus-1/services/org.keepassxc.KeePassXC.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.keepassxc.KeePassXC.service"; 609 "dbus-1/services/org.keepassxc.KeePassXC.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.keepassxc.KeePassXC.service";
488 "dbus-1/services/org.freedesktop.secrets.service.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.freedesktop.secrets.service.service"; 610 "dbus-1/services/org.freedesktop.secrets.service.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.freedesktop.secrets.service.service";
611 "dbus-1/services/org.kde.kwalletd6.service".source = "${pkgs.kdePackages.kwallet}/share/dbus-1/services/org.kde.kwalletd6.service";
612 "dbus-1/services/org.kde.kwalletd5.service".source = "${pkgs.kdePackages.kwallet}/share/dbus-1/services/org.kde.kwalletd5.service";
489 "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation { 613 "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation {
490 inherit (sources.emoji-data) pname src; 614 inherit (sources.emoji-data) pname src;
491 version = lib.removePrefix "v" sources.emoji-data.version; 615 version = lib.removePrefix "v" sources.emoji-data.version;
@@ -573,11 +697,11 @@ in {
573 exec -- \ 697 exec -- \
574 ${lib.getExe' config.systemd.package "systemd-run"} --wait --user --slice-inherit \ 698 ${lib.getExe' config.systemd.package "systemd-run"} --wait --user --slice-inherit \
575 --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ 699 --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \
576 --property 'Environment=DSCP=46' \ 700 -E DSCP=46 -E NIXOS_OZONE_WL \
577 -- ${lib.getExe pkgs.dscp} ${lib.getExe' pkgs.google-chrome "google-chrome-stable"} \ 701 -- ${lib.getExe pkgs.dscp} ${lib.getExe cfg.programs.chromium.package} \
578 --class=Rainbow \ 702 --class=Rainbow \
579 --kiosk "https://web.openrainbow.com" \ 703 --app="https://web.openrainbow.com" \
580 --user-data-dir=''${HOME}/.config/google-chrome-rainbow 704 --user-data-dir=''${HOME}/.config/chromium-rainbow
581 ''); 705 '');
582 icon = pkgs.fetchurl { 706 icon = pkgs.fetchurl {
583 url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg"; 707 url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg";
@@ -587,6 +711,53 @@ in {
587 StartupWMClass = "Rainbow"; 711 StartupWMClass = "Rainbow";
588 }; 712 };
589 }; 713 };
714 kimai = {
715 name = "Kimai";
716 exec = toString (pkgs.writeShellScript "kimai" ''
717 exec -- \
718 ${lib.getExe cfg.programs.chromium.package} \
719 --class=Kimai \
720 --app="https://kimai.yggdrasil.li" \
721 --user-data-dir=''${HOME}/.config/chromium-kimai
722 '');
723 icon = pkgs.fetchurl {
724 url = "https://www.kimai.org/images/kimai_logo.png";
725 hash = "sha256-lnlOttzR2SwXA70R+egJUkeKr4U5V0avqTk8uX4bqfs=";
726 };
727 settings = {
728 StartupWMClass = "Kimai";
729 StartupNotify = "true";
730 };
731 };
732 audiobookshelf = {
733 name = "Audiobookshelf";
734 exec = toString (pkgs.writeShellScript "audiobookshelf" ''
735 exec -- \
736 ${lib.getExe cfg.programs.chromium.package} \
737 --class=Audiobookshelf \
738 --app="https://audiobookshelf.yggdrasil.li" \
739 --user-data-dir=''${HOME}/.config/chromium-audiobookshelf
740 '');
741 icon = pkgs.fetchurl {
742 url = "https://www.audiobookshelf.org/Logo.png";
743 hash = "sha256-JGPk+WNT1C4DC4lSMb0K0YmAMT5LvmSOeO0QRzkc7Lk=";
744 };
745 settings = {
746 StartupWMClass = "Audiobookshelf";
747 StartupNotify = "true";
748 };
749 };
750 thunderbird-lmu = {
751 name = "Thunderbird (LMU)";
752 exec = "thunderbird --name thunderbird -P lmu %U";
753 icon = "thunderbird";
754 genericName = "Email Client";
755 categories = [ "Network" "Chat" "Email" "Feed" "GTK" "News" ];
756 settings = {
757 StartupWMClass = "thunderbird";
758 StartupNotify = "true";
759 };
760 };
590 }; 761 };
591 762
592 fonts = { 763 fonts = {
diff --git a/accounts/gkleen@sif/emacs.el b/accounts/gkleen@sif/emacs.el
index 563c5d0b..3beefba6 100644
--- a/accounts/gkleen@sif/emacs.el
+++ b/accounts/gkleen@sif/emacs.el
@@ -254,3 +254,5 @@ necessarily running."
254(bind-key "C-x C-m" #'move-file) 254(bind-key "C-x C-m" #'move-file)
255 255
256(let ((ssh_auth_sock (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))) (setenv "SSH_AUTH_SOCK" ssh_auth_sock)) 256(let ((ssh_auth_sock (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))) (setenv "SSH_AUTH_SOCK" ssh_auth_sock))
257(setenv "SSH_ASKPASS_REQUIRE" "prefer")
258(setenv "SSH_ASKPASS" "@ksshaskpass@")
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix
index dc993d66..35a3d799 100644
--- a/accounts/gkleen@sif/niri/default.nix
+++ b/accounts/gkleen@sif/niri/default.nix
@@ -3,6 +3,7 @@ let
3 cfg = config.programs.niri; 3 cfg = config.programs.niri;
4 4
5 kdl = flakeInputs.niri-flake.lib.kdl; 5 kdl = flakeInputs.niri-flake.lib.kdl;
6 sleaf = name: arg: kdl.node name [arg] [];
6 7
7 niri = cfg.package; 8 niri = cfg.package;
8 terminal = lib.getExe config.programs.kitty.package; 9 terminal = lib.getExe config.programs.kitty.package;
@@ -35,7 +36,11 @@ let
35 if jq -e '.is_focused' <<<"$window_json" >/dev/null; then 36 if jq -e '.is_focused' <<<"$window_json" >/dev/null; then
36 niri msg action focus-workspace-previous 37 niri msg action focus-workspace-previous
37 else 38 else
38 niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")" 39 if [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].is_focused' <<<"$workspaces_json") != "true" ]] && [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].id' <<<"$workspaces_json") = $(jq -r '.workspace_id' <<<"$window_json") ]]; then
40 niri msg action focus-workspace "$workspace_name"
41 else
42 niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")"
43 fi
39 fi 44 fi
40 exit 0 45 exit 0
41 fi 46 fi
@@ -45,7 +50,6 @@ let
45 ''; 50 '';
46 }; 51 };
47 focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn); 52 focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn);
48 focus-or-spawn-action-app_id = app_id: focus-or-spawn-action ''select(.app_id == "${app_id}")'';
49 53
50 with_adjacent_workspace = pkgs.writeShellApplication { 54 with_adjacent_workspace = pkgs.writeShellApplication {
51 name = "with-adjacent-workspace"; 55 name = "with-adjacent-workspace";
@@ -84,7 +88,7 @@ let
84 }; 88 };
85 with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$"; 89 with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$";
86 focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; 90 focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}'';
87 move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}''; 91 move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}'';
88 92
89 with_unnamed_workspace = pkgs.writeShellApplication { 93 with_unnamed_workspace = pkgs.writeShellApplication {
90 name = "with-unnamed-workspace"; 94 name = "with-unnamed-workspace";
@@ -131,7 +135,7 @@ let
131 135
132 windows_json="$(niri msg -j windows)" 136 windows_json="$(niri msg -j windows)"
133 active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")" 137 active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")"
134 window_ix="$(gojq -r --arg active_workspace "$active_workspace" '.[] | select('"$window_select"') | "\(.title)\u0000icon\u001f\(.app_id)"' <<<"$windows_json" | fuzzel --log-level=warning --dmenu --index)" 138 window_ix="$(gojq -r --arg active_workspace "$active_workspace" '.[] | select('"$window_select"') | "\(.title)\u0000icon\u001f\(.app_id)"' <<<"$windows_json" | fuzzel --width=60 --log-level=warning --dmenu --index)"
135 # shellcheck disable=SC2016 139 # shellcheck disable=SC2016
136 window_json="$(gojq -rc --arg active_workspace "$active_workspace" --arg window_ix "$window_ix" 'map(select('"$window_select"')) | .[($window_ix | tonumber)]' <<<"$windows_json")" 140 window_json="$(gojq -rc --arg active_workspace "$active_workspace" --arg window_ix "$window_ix" 'map(select('"$window_select"')) | .[($window_ix | tonumber)]' <<<"$windows_json")"
137 141
@@ -141,6 +145,25 @@ let
141 ''; 145 '';
142 }; 146 };
143 with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window); 147 with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window);
148
149 with_predicate_window = pred: pkgs.writeShellApplication {
150 name = "with-predicate-window";
151 runtimeInputs = [ niri pkgs.gojq pkgs.socat ];
152 text = ''
153 action="$1"
154 shift
155
156 windows_json="$(niri msg -j windows)"
157 window_json="$(gojq -rc 'map(select(${pred})) | .[0]' <<<"$windows_json")"
158
159 [[ -z "$window_json" || $window_json = "null" ]] && exit 1
160
161 jq -c "$action" <<<"$window_json" | socat STDIO "$NIRI_SOCKET"
162 '';
163 };
164
165 with-urgent-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_urgent"));
166 with-focused-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_focused"));
144in { 167in {
145 imports = [ 168 imports = [
146 ./waybar.nix 169 ./waybar.nix
@@ -171,6 +194,17 @@ in {
171 type = lib.types.nullOr lib.types.str; 194 type = lib.types.nullOr lib.types.str;
172 default = null; 195 default = null;
173 }; 196 };
197 moveKey = lib.mkOption {
198 type = lib.types.nullOr lib.types.str;
199 default = let
200 keys = lib.splitString "+" config.key;
201 defMoveKey = lib.concatStringsSep "+" (lib.flatten [
202 (lib.take (lib.length keys - 1) keys)
203 ["Shift"]
204 (lib.takeEnd 1 keys)
205 ]);
206 in if config.key == null then null else defMoveKey;
207 };
174 spawn = lib.mkOption { 208 spawn = lib.mkOption {
175 type = lib.types.nullOr (lib.types.listOf lib.types.str); 209 type = lib.types.nullOr (lib.types.listOf lib.types.str);
176 default = null; 210 default = null;
@@ -245,11 +279,11 @@ in {
245 Service = { 279 Service = {
246 Type = "simple"; 280 Type = "simple";
247 Sockets = [ "niri-workspace-history.socket" ]; 281 Sockets = [ "niri-workspace-history.socket" ];
248 ExecStart = pkgs.writers.writePython3 "niri-workspace-history" {} '' 282 ExecStart = pkgs.writers.writePython3 "niri-workspace-history" { flakeIgnore = ["E501"]; } ''
249 import os 283 import os
250 import socket 284 import socket
251 import json 285 import json
252 import sys 286 # import sys
253 from collections import defaultdict 287 from collections import defaultdict
254 from threading import Thread, Lock 288 from threading import Thread, Lock
255 from socketserver import StreamRequestHandler, ThreadingTCPServer 289 from socketserver import StreamRequestHandler, ThreadingTCPServer
@@ -274,8 +308,8 @@ in {
274 308
275 def focus_workspace(output, workspace): 309 def focus_workspace(output, workspace):
276 with history_lock: 310 with history_lock:
277 workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace] # noqa: E501 311 workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace]
278 print(json.dumps(workspace_history), file=sys.stderr) 312 # print(json.dumps(workspace_history), file=sys.stderr)
279 313
280 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 314 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
281 sock.connect(os.environ["NIRI_SOCKET"]) 315 sock.connect(os.environ["NIRI_SOCKET"])
@@ -297,14 +331,14 @@ in {
297 331
298 class RequestHandler(StreamRequestHandler): 332 class RequestHandler(StreamRequestHandler):
299 def handle(self): 333 def handle(self):
300 with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out: # noqa: E501 334 with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out:
301 with history_lock: 335 with history_lock:
302 json.dump(workspace_history, out) 336 json.dump(workspace_history, out)
303 337
304 338
305 class Server(ThreadingTCPServer): 339 class Server(ThreadingTCPServer):
306 def __init__(self): 340 def __init__(self):
307 ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False) # noqa: E501 341 ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False)
308 self.socket = socket.fromfd(3, self.address_family, self.socket_type) 342 self.socket = socket.fromfd(3, self.address_family, self.socket_type)
309 343
310 344
@@ -330,6 +364,79 @@ in {
330 ''; 364 '';
331 }; 365 };
332 }; 366 };
367 systemd.user.services.niri-workspace-sort = {
368 Unit = {
369 BindsTo = [ "niri.service" ];
370 After = [ "niri.service" ];
371 };
372 Install = {
373 WantedBy = [ "niri.service" ];
374 };
375 Service = {
376 Type = "simple";
377 ExecStart = pkgs.writers.writePython3 "niri-workspace-sort" { flakeIgnore = ["E501"]; } ''
378 import os
379 import sys
380 import socket
381 import json
382
383 outputs = None
384 only = {'HDMI-A-1': {'bmr'}, 'eDP-1': {'vid'}}
385
386
387 class Niri(socket.socket):
388 def __init__(self):
389 super().__init__(socket.AF_UNIX, socket.SOCK_STREAM)
390 super().connect(os.environ["NIRI_SOCKET"])
391 self.fh = super().makefile(mode='rw', buffering=1, encoding='utf-8')
392
393 def cmd(self, obj):
394 print(json.dumps(obj, separators=(',', ':')), flush=True, file=self.fh)
395
396 def event_stream(self):
397 self.cmd("EventStream")
398 return self.fh
399
400
401 with Niri() as niri, Niri().event_stream() as niri_stream:
402 for line in niri_stream:
403 workspaces = None
404 if line_json := json.loads(line):
405 if "WorkspacesChanged" in line_json:
406 workspaces = line_json["WorkspacesChanged"]["workspaces"]
407
408 if workspaces is None:
409 continue
410
411 old_outputs = outputs
412 outputs = {ws["output"] for ws in workspaces}
413 if old_outputs is None:
414 print("Initial outputs: {}".format(outputs), file=sys.stderr)
415 continue
416
417 new_outputs = outputs - old_outputs
418 if not new_outputs:
419 continue
420 print("New outputs: {}".format(new_outputs), file=sys.stderr)
421
422 relevant_workspaces = list(filter(lambda ws: (ws["name"] is not None) or (ws["active_window_id"] is not None), workspaces))
423 target_output = next(iter(outputs - set(only.keys())))
424 if not target_output:
425 continue
426 for ws in relevant_workspaces:
427 ws_ident = ws["name"] if ws["name"] is not None else (ws["output"], ws["idx"])
428 if ws["output"] not in set(only.keys()):
429 continue
430 if ws_ident in only[ws["output"]]:
431 continue
432
433 print("{} -> {}".format(ws_ident, target_output), file=sys.stderr)
434 niri.cmd({"Action": {"MoveWorkspaceToMonitor": {"reference": {"Id": ws["id"]}, "output": target_output}}})
435 '';
436 Restart = "on-failure";
437 RestartSec = 10;
438 };
439 };
333 440
334 programs.niri.scratchspaces = [ 441 programs.niri.scratchspaces = [
335 { name = "pwctl"; 442 { name = "pwctl";
@@ -343,8 +450,8 @@ in {
343 { title = "^Access Request.*"; } 450 { title = "^Access Request.*"; }
344 { title = ".*Passkey credentials$"; } 451 { title = ".*Passkey credentials$"; }
345 ]; 452 ];
346 windowRuleExtra = [ 453 windowRuleExtra = with kdl; [
347 (kdl.leaf "open-focused" false) 454 (sleaf "open-focused" false)
348 ]; 455 ];
349 key = "Mod+Control+P"; 456 key = "Mod+Control+P";
350 app-id = "org.keepassxc.KeePassXC"; 457 app-id = "org.keepassxc.KeePassXC";
@@ -371,6 +478,20 @@ in {
371 app-id = "com.github.wwmm.easyeffects"; 478 app-id = "com.github.wwmm.easyeffects";
372 spawn = [ "easyeffects" ]; 479 spawn = [ "easyeffects" ];
373 } 480 }
481 { name = "time";
482 key = "Mod+Control+K";
483 app-id = "chrome-kimai.yggdrasil.li__-Default";
484 spawn = [ (toString (pkgs.resholve.writeScript "kimai" {
485 interpreter = pkgs.runtimeShell;
486 inputs = [ pkgs.dex ];
487 execer = [ "cannot:${lib.getExe pkgs.dex}" ];
488 } ''
489 exec dex $HOME/.local/state/nix/profile/share/applications/kimai.desktop
490 '')) ];
491 windowRuleExtra = with kdl; [
492 (sleaf "block-out-from" "screencast")
493 ];
494 }
374 ]; 495 ];
375 programs.niri.config = 496 programs.niri.config =
376 let 497 let
@@ -380,10 +501,12 @@ in {
380 then v 501 then v
381 else null; 502 else null;
382 opt-props = lib.filterAttrs (lib.const (value: value != null)); 503 opt-props = lib.filterAttrs (lib.const (value: value != null));
504 normalize-nodes = nodes: lib.remove null (lib.flatten nodes);
383 in 505 in
384 [ (flag "prefer-no-csd") 506 normalize-nodes [
507 (flag "prefer-no-csd")
385 508
386 (leaf "screenshot-path" "~/screenshots/%Y-%m-%dT%H:%M:%S.png") 509 (sleaf "screenshot-path" "~/screenshots/%Y-%m-%dT%H:%M:%S.png")
387 510
388 (plain "hotkey-overlay" [ 511 (plain "hotkey-overlay" [
389 (flag "skip-at-startup") 512 (flag "skip-at-startup")
@@ -391,80 +514,88 @@ in {
391 514
392 (plain "input" [ 515 (plain "input" [
393 (plain "keyboard" [ 516 (plain "keyboard" [
394 (leaf "repeat-delay" 300) 517 (sleaf "repeat-delay" 300)
395 (leaf "repeat-rate" 50) 518 (sleaf "repeat-rate" 50)
396 519
397 (plain "xkb" [ 520 (plain "xkb" [
398 (leaf "layout" "us,us") 521 (sleaf "layout" "us,us")
399 (leaf "variant" "dvp,") 522 (sleaf "variant" "dvp,")
400 (leaf "options" "compose:caps,grp:win_space_toggle") 523 (sleaf "options" "compose:caps,grp:win_space_toggle")
401 ]) 524 ])
402 ]) 525 ])
403 526
404 (flag "workspace-auto-back-and-forth") 527 (flag "workspace-auto-back-and-forth")
405 # (leaf "focus-follows-mouse" {}) 528 # (sleaf "focus-follows-mouse" {})
406 # (flag "warp-mouse-to-focus") 529 # (flag "warp-mouse-to-focus")
407 530
408 # (plain "touchpad" [ (flag "off") ]) 531 # (plain "touchpad" [ (flag "off") ])
409 (plain "trackball" [ 532 (plain "trackball" [
410 (leaf "scroll-method" "on-button-down") 533 (sleaf "scroll-method" "on-button-down")
411 (leaf "scroll-button" 278) 534 (sleaf "scroll-button" 278)
412 ]) 535 ])
413 (plain "touch" [ 536 (plain "touch" [
414 (leaf "map-to-output" "eDP-1") 537 (sleaf "map-to-output" "eDP-1")
415 ]) 538 ])
416 ]) 539 ])
417 540
418 (plain "environment" (lib.mapAttrsToList leaf { 541 (plain "gestures" [
542 (plain "hot-corners" [(flag "off")])
543 ])
544
545 (plain "environment" (lib.mapAttrsToList sleaf {
419 NIXOS_OZONE_WL = "1"; 546 NIXOS_OZONE_WL = "1";
420 QT_QPA_PLATFORM = "wayland"; 547 QT_QPA_PLATFORM = "wayland";
421 QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; 548 QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
422 GDK_BACKEND = "wayland"; 549 GDK_BACKEND = "wayland";
423 SDL_VIDEODRIVER = "wayland"; 550 SDL_VIDEODRIVER = "wayland";
424 DISPLAY = ":0"; 551 DISPLAY = ":0";
552 ELECTRON_OZONE_PLATFORM_HINT = "auto";
553 SSH_ASKPASS_REQUIRE = "prefer";
554 SSH_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass;
555 SUDO_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass;
425 })) 556 }))
426 557
427 (node "output" "eDP-1" [ 558 (node "output" ["eDP-1"] [
428 (leaf "scale" 1.5) 559 (sleaf "scale" 1.5)
429 (leaf "position" { x = 0; y = 0; }) 560 (sleaf "position" { x = 0; y = 0; })
430 ]) 561 ])
431 (node "output" "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" [ 562 (node "output" ["Ancor Communications Inc ASUS PB287Q 0x0000DD9B"] [
432 (leaf "scale" 1.5) 563 (sleaf "scale" 1.5)
433 (leaf "position" { x = 2560; y = 0; }) 564 (sleaf "position" { x = 2560; y = 0; })
434 ]) 565 ])
435 (node "output" "HP Inc. HP 727pu CN4417143K" [ 566 (node "output" ["HP Inc. HP 727pu CN4417143K"] [
436 (leaf "mode" "2560x1440@119.998") 567 (sleaf "mode" "2560x1440@119.998")
437 (leaf "scale" 1) 568 (sleaf "scale" 1)
438 (leaf "position" { x = 2560; y = 0; }) 569 (sleaf "position" { x = 2560; y = 0; })
439 (flag "variable-refresh-rate") 570 (flag "variable-refresh-rate")
440 ]) 571 ])
441 572
442 (plain "debug" [ 573 (plain "debug" [
443 (leaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render") 574 (sleaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render")
444 ]) 575 ])
445 576
446 (plain "animations" [ 577 (plain "animations" [
447 (leaf "slowdown" 0.5) 578 (sleaf "slowdown" 0.5)
448 (plain "workspace-switch" [(flag "off")]) 579 (plain "workspace-switch" [(flag "off")])
449 ]) 580 ])
450 581
451 (plain "layout" [ 582 (plain "layout" [
452 (leaf "gaps" 8) 583 (sleaf "gaps" 8)
453 (plain "struts" [ 584 (plain "struts" [
454 (leaf "left" 26) 585 (sleaf "left" 26)
455 (leaf "right" 26) 586 (sleaf "right" 26)
456 (leaf "top" 0) 587 (sleaf "top" 0)
457 (leaf "bottom" 0) 588 (sleaf "bottom" 0)
458 ]) 589 ])
459 (plain "border" [ 590 (plain "border" [
460 (leaf "width" 2) 591 (sleaf "width" 2)
461 (leaf "active-gradient" { 592 (sleaf "active-gradient" {
462 from = "hsla(195 100% 45% 1)"; 593 from = "hsla(195 100% 45% 1)";
463 to = "hsla(155 100% 37.5% 1)"; 594 to = "hsla(155 100% 37.5% 1)";
464 angle = 29; 595 angle = 29;
465 relative-to = "workspace-view"; 596 relative-to = "workspace-view";
466 }) 597 })
467 (leaf "inactive-gradient" { 598 (sleaf "inactive-gradient" {
468 from = "hsla(0 0% 27.7% 1)"; 599 from = "hsla(0 0% 27.7% 1)";
469 to = "hsla(0 0% 23% 1)"; 600 to = "hsla(0 0% 23% 1)";
470 angle = 29; 601 angle = 29;
@@ -475,29 +606,29 @@ in {
475 (flag "off") 606 (flag "off")
476 ]) 607 ])
477 608
478 (plain "preset-column-widths" (map (prop: leaf "proportion" prop) [ 609 (plain "preset-column-widths" (map (prop: sleaf "proportion" prop) [
479 (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.) (1.) 610 (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.) (1.)
480 ])) 611 ]))
481 (plain "default-column-width" [ (leaf "proportion" (1. / 2.)) ]) 612 (plain "default-column-width" [ (sleaf "proportion" (1. / 2.)) ])
482 (plain "preset-window-heights" (map (prop: leaf "proportion" prop) [ 613 (plain "preset-window-heights" (map (prop: sleaf "proportion" prop) [
483 (1. / 3.) (1. / 2.) (2. / 3.) (1.) 614 (1. / 3.) (1. / 2.) (2. / 3.) (1.)
484 ])) 615 ]))
485 616
486 (flag "always-center-single-column") 617 (flag "always-center-single-column")
487 618
488 (plain "tab-indicator" [ 619 (plain "tab-indicator" [
489 (leaf "gap" 4) 620 (sleaf "gap" 4)
490 (leaf "width" 8) 621 (sleaf "width" 8)
491 (leaf "gaps-between-tabs" 4) 622 (sleaf "gaps-between-tabs" 4)
492 (flag "place-within-column") 623 (flag "place-within-column")
493 (leaf "length" { total-proportion = 1.; }) 624 (sleaf "length" { total-proportion = 1.; })
494 (leaf "active-gradient" { 625 (sleaf "active-gradient" {
495 from = "hsla(195 100% 60% 0.75)"; 626 from = "hsla(195 100% 60% 0.75)";
496 to = "hsla(155 100% 50% 0.75)"; 627 to = "hsla(155 100% 50% 0.75)";
497 angle = 29; 628 angle = 29;
498 relative-to = "workspace-view"; 629 relative-to = "workspace-view";
499 }) 630 })
500 (leaf "inactive-gradient" { 631 (sleaf "inactive-gradient" {
501 from = "hsla(0 0% 42% 0.66)"; 632 from = "hsla(0 0% 42% 0.66)";
502 to = "hsla(0 0% 35% 0.66)"; 633 to = "hsla(0 0% 35% 0.66)";
503 angle = 29; 634 angle = 29;
@@ -511,128 +642,140 @@ in {
511 ]) 642 ])
512 643
513 (map (name: 644 (map (name:
514 (node "workspace" name [ 645 (node "workspace" [name] [
515 (leaf "open-on-output" "eDP-1") 646 (sleaf "open-on-output" "eDP-1")
516 ]) 647 ])
517 ) (map ({name, ...}: name) cfg.scratchspaces)) 648 ) (map ({name, ...}: name) cfg.scratchspaces))
518 (map (name: 649 (map (name:
519 (leaf "workspace" name) 650 (sleaf "workspace" name)
520 ) ["comm" "web" "vid" "bmr"]) 651 ) ["comm" "web" "vid" "bmr"])
521 652
522 (plain "window-rule" [ 653 (plain "window-rule" [
523 (leaf "clip-to-geometry" true) 654 (sleaf "clip-to-geometry" true)
524 ]) 655 ])
525 656
526 (plain "window-rule" [ 657 (plain "window-rule" [
527 (leaf "match" { is-floating = true; }) 658 (sleaf "match" { is-floating = true; })
528 (leaf "geometry-corner-radius" 8) 659 (sleaf "geometry-corner-radius" 8)
529 (plain "shadow" [ (flag "on") ]) 660 (plain "shadow" [ (flag "on") ])
530 ]) 661 ])
531 662
532 (plain "window-rule" [ 663 (plain "window-rule" [
533 (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; }) 664 (sleaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; })
534 (leaf "block-out-from" "screencast") 665 (sleaf "block-out-from" "screencast")
535 ]) 666 ])
536 (plain "window-rule" [ 667 (plain "window-rule" (normalize-nodes [
537 (map (title: 668 (map (title:
538 (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; }) 669 (sleaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; })
539 ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$"]) 670 ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$" "Browser Access Request$"])
540 (leaf "open-focused" true) 671 (sleaf "open-focused" true)
541 (leaf "open-floating" true) 672 (sleaf "open-floating" true)
542 ]) 673 ]))
543 674
544 (map ({ name, match, exclude, windowRuleExtra, ... }: 675 (map ({ name, match, exclude, windowRuleExtra, ... }:
545 (optional-node (match != []) (plain "window-rule" [ 676 (optional-node (match != []) (plain "window-rule" (normalize-nodes [
546 (map (leaf "match") match) 677 (map (sleaf "match") match)
547 (map (leaf "exclude") exclude) 678 (map (sleaf "exclude") exclude)
548 (leaf "open-on-workspace" name) 679 (sleaf "open-on-workspace" name)
549 (leaf "open-maximized" true) 680 (sleaf "open-maximized" true)
550 windowRuleExtra 681 windowRuleExtra
551 ])) 682 ])))
552 ) cfg.scratchspaces) 683 ) cfg.scratchspaces)
553 684
554 (plain "window-rule" [ 685 (plain "window-rule" [
555 (leaf "match" { app-id = "^emacs$"; }) 686 (sleaf "match" { app-id = "^emacs$"; })
556 (leaf "match" { app-id = "^firefox$"; }) 687 (sleaf "match" { app-id = "^firefox$"; })
557 (plain "default-column-width" [(leaf "proportion" (2. / 3.))]) 688 (plain "default-column-width" [(sleaf "proportion" (2. / 3.))])
558 ]) 689 ])
559 (plain "window-rule" [ 690 (plain "window-rule" [
560 (leaf "match" { app-id = "^kitty$"; }) 691 (sleaf "match" { app-id = "^kitty$"; })
561 (leaf "match" { app-id = "^kitty-play$"; }) 692 (sleaf "match" { app-id = "^kitty-play$"; })
562 (plain "default-column-width" [(leaf "proportion" (1. / 3.))]) 693 (plain "default-column-width" [(sleaf "proportion" (1. / 3.))])
563 ]) 694 ])
564 695
565 (plain "window-rule" [ 696 (plain "window-rule" [
566 (leaf "match" { app-id = "^thunderbird$"; }) 697 (sleaf "match" { app-id = "^thunderbird$"; })
567 (leaf "match" { app-id = "^Element$"; }) 698 (sleaf "match" { app-id = "^Element$"; })
568 (leaf "match" { app-id = "^Rainbow$"; }) 699 (sleaf "match" { app-id = "^chrome-web\.openrainbow\.com__-Default$"; })
569 (leaf "open-on-workspace" "comm") 700 (sleaf "open-on-workspace" "comm")
570 ]) 701 ])
571 (plain "window-rule" [ 702 (plain "window-rule" [
572 (leaf "match" { app-id = "^firefox$"; }) 703 (sleaf "match" { app-id = "^firefox$"; })
573 (leaf "open-on-workspace" "web") 704 (sleaf "open-on-workspace" "web")
574 (leaf "open-maximized" true) 705 (sleaf "open-maximized" true)
575 ]) 706 ])
576 (plain "window-rule" [ 707 (plain "window-rule" [
577 (leaf "match" { app-id = "^mpv$"; }) 708 (sleaf "match" { app-id = "^mpv$"; })
578 (leaf "open-on-workspace" "vid") 709 (sleaf "open-on-workspace" "vid")
579 (plain "default-column-width" [(leaf "proportion" 1.)]) 710 (plain "default-column-width" [(sleaf "proportion" 1.)])
580 ]) 711 ])
581 (plain "window-rule" [ 712 (plain "window-rule" [
582 (leaf "match" { app-id = "^kitty-play$"; }) 713 (sleaf "match" { app-id = "^kitty-play$"; })
583 (leaf "open-on-workspace" "vid") 714 (sleaf "open-on-workspace" "vid")
584 (leaf "open-focused" false) 715 (sleaf "open-focused" false)
585 ]) 716 ])
586 (plain "window-rule" [ 717 (plain "window-rule" [
587 (leaf "match" { app-id = "^pdfpc$"; }) 718 (sleaf "match" { app-id = "^chrome-audiobookshelf\.yggdrasil\.li__-Default$"; })
588 (plain "default-column-width" [(leaf "proportion" 1.)]) 719 (sleaf "match" { app-id = "^YouTube Music Desktop App$"; })
720 (sleaf "open-on-workspace" "vid")
589 ]) 721 ])
590 (plain "window-rule" [ 722 (plain "window-rule" [
591 (leaf "match" { app-id = "^pdfpc$"; title = "^.*presentation.*$"; }) 723 (sleaf "match" { app-id = "^pdfpc$"; })
592 (plain "default-column-width" [(leaf "proportion" 1.)]) 724 (plain "default-column-width" [(sleaf "proportion" 1.)])
593 (leaf "open-fullscreen" true)
594 (leaf "open-on-workspace" "bmr")
595 (leaf "open-focused" false)
596 ]) 725 ])
597 (plain "window-rule" [ 726 (plain "window-rule" [
598 (map (leaf "match") [ 727 (sleaf "match" { app-id = "^pdfpc$"; title = "^.*presentation.*$"; })
728 (plain "default-column-width" [(sleaf "proportion" 1.)])
729 (sleaf "open-fullscreen" true)
730 (sleaf "open-on-workspace" "bmr")
731 (sleaf "open-focused" false)
732 ])
733 (plain "window-rule" (normalize-nodes [
734 (map (sleaf "match") [
599 { app-id = "^Gimp-"; title = "^Quit GIMP$"; } 735 { app-id = "^Gimp-"; title = "^Quit GIMP$"; }
600 { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; } 736 { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; }
601 { app-id = "^xdg-desktop-portal-gtk$"; } 737 { app-id = "^xdg-desktop-portal-gtk$"; }
602 ]) 738 ])
603 (leaf "open-floating" true) 739 (sleaf "open-floating" true)
604 ]) 740 ]))
605 (plain "window-rule" [ 741 (plain "window-rule" [
606 (leaf "match" { app-id = "^org\\.pwmt\\.zathura$"; }) 742 (sleaf "match" { app-id = "^org\\.pwmt\\.zathura$"; })
607 (leaf "match" { app-id = "^evince$"; }) 743 (sleaf "match" { app-id = "^evince$"; })
608 (leaf "match" { app-id = "^org\\.gnome\\.Papers$"; }) 744 (sleaf "match" { app-id = "^org\\.gnome\\.Papers$"; })
609 (leaf "default-column-display" "tabbed") 745 (sleaf "default-column-display" "tabbed")
610 ]) 746 ])
611 747
612 (plain "layer-rule" [ 748 (plain "layer-rule" [
613 (leaf "match" { namespace = "^notifications$"; }) 749 (sleaf "match" { namespace = "^notifications$"; })
614 (leaf "match" { namespace = "^waybar$"; }) 750 (sleaf "match" { namespace = "^waybar$"; })
615 (leaf "match" { namespace = "^launcher$"; }) 751 (sleaf "match" { namespace = "^launcher$"; })
616 (leaf "block-out-from" "screencast") 752 (sleaf "block-out-from" "screencast")
617 ]) 753 ])
618 754
619 (plain "binds" 755 (plain "binds"
620 (let 756 (let
621 bind = name: cfg: node name (opt-props { 757 bind = name: cfg: node name [(lib.removeAttrs cfg ["action"])] (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"]));
622 cooldown-ms = cfg.cooldown-ms or null;
623 }
624 // (lib.optionalAttrs (!(cfg.repeat or true)) {
625 repeat = false;
626 })
627 // (lib.optionalAttrs (cfg.allow-when-locked or false) {
628 allow-when-locked = true;
629 })) (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"]));
630 in 758 in
631 [ 759 normalize-nodes [
632 (lib.mapAttrsToList bind (with config.lib.niri.actions; { 760 (lib.mapAttrsToList bind (with config.lib.niri.actions; {
633 "Mod+Slash".action = show-hotkey-overlay; 761 "Mod+Slash".action = show-hotkey-overlay;
634 762
635 "Mod+Return".action = spawn terminal; 763 "Mod+Return".action = spawn terminal;
764 "Mod+Shift+Return".action =
765 let
766 nushellKitty = pkgs.symlinkJoin {
767 name = "nushell-kitty";
768 paths = [ config.programs.kitty.package ];
769 buildInputs = [ pkgs.makeWrapper ];
770 postBuild = ''
771 wrapProgram $out/bin/kitty \
772 --add-flags "--config ${pkgs.writeText "kitty.conf" ''
773 include $HOME/${config.xdg.configFile."kitty/kitty.conf".target}
774 shell ${lib.getExe config.programs.nushell.package}
775 ''}"
776 '';
777 };
778 in spawn (lib.getExe' nushellKitty "kitty");
636 "Mod+Q".action = close-window; 779 "Mod+Q".action = close-window;
637 "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package); 780 "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package);
638 "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path"; 781 "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path";
@@ -668,12 +811,12 @@ in {
668 done < <(export LC_ALL=C.UTF-8; echo; find "$RESULTS_DIR" -type f -printf $'%T@ %p\n' | sort -n | cut -d' ' -f2- | xargs -r cat) 811 done < <(export LC_ALL=C.UTF-8; echo; find "$RESULTS_DIR" -type f -printf $'%T@ %p\n' | sort -n | cut -d' ' -f2- | xargs -r cat)
669 $FOUND || echo 812 $FOUND || echo
670 } 813 }
671 FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $? 814 FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> " --width=60) || exit $?
672 if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then 815 if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then
673 QALC_RES="$FUZZEL_RES" 816 QALC_RES="$FUZZEL_RES"
674 QALC_RET=0 817 QALC_RET=0
675 else 818 else
676 QALC_RES=$(qalc "$FUZZEL_RES" 2>&1) 819 QALC_RES=$(qalc -set "autocalc off" "$FUZZEL_RES" 2>&1)
677 QALC_RET=$? 820 QALC_RET=$?
678 fi 821 fi
679 [[ -n "$QALC_RES" ]] || exit 1 822 [[ -n "$QALC_RES" ]] || exit 1
@@ -693,18 +836,33 @@ in {
693 notify-send "$QALC_RES" 836 notify-send "$QALC_RES"
694 ''; 837 '';
695 })); 838 }));
839 "Mod+Shift+U".action =
840 let
841 qalcKitty = pkgs.symlinkJoin {
842 name = "qalc-kitty";
843 paths = [ config.programs.kitty.package ];
844 buildInputs = [ pkgs.makeWrapper ];
845 postBuild = ''
846 wrapProgram $out/bin/kitty \
847 --add-flags "--config ${pkgs.writeText "kitty.conf" ''
848 include $HOME/${config.xdg.configFile."kitty/kitty.conf".target}
849 shell ${lib.getExe pkgs.libqalculate}
850 ''}"
851 '';
852 };
853 in spawn (lib.getExe' qalcKitty "kitty");
696 "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication { 854 "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication {
697 name = "emoji-fuzzel"; 855 name = "emoji-fuzzel";
698 runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ]; 856 runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ];
699 text = '' 857 text = ''
700 FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <"$HOME"/.local/share/emoji-data/list.txt) || exit $? 858 FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " --cache "$HOME"/.cache/fuzzel-emoji --width=60 <"$HOME"/.local/share/emoji-data/list.txt) || exit $?
701 [[ -n "$FUZZEL_RES" ]] || exit 1 859 [[ -n "$FUZZEL_RES" ]] || exit 1
702 wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste 860 wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste
703 ''; 861 '';
704 })); 862 }));
705 "Print".action = screenshot; 863 "Print".action = screenshot;
706 "Control+Print".action = screenshot-window; 864 "Control+Print".action = screenshot-window;
707 # "Shift+Print".action = screenshot-screen; 865 "Shift+Print".action = kdl.magic-leaf "screenshot-screen";
708 "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; 866 "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
709 "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; 867 "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
710 868
@@ -743,22 +901,22 @@ in {
743 "Mod+Shift+Control+C".action = move-workspace-up; 901 "Mod+Shift+Control+C".action = move-workspace-up;
744 902
745 "Mod+ParenLeft".action = focus-workspace "comm"; 903 "Mod+ParenLeft".action = focus-workspace "comm";
746 "Mod+Shift+ParenLeft".action = move-column-to-workspace "comm"; 904 "Mod+Shift+ParenLeft".action = kdl.magic-leaf "move-column-to-workspace" "comm";
747 905
748 "Mod+ParenRight".action = focus-workspace "web"; 906 "Mod+ParenRight".action = focus-workspace "web";
749 "Mod+Shift+ParenRight".action = move-column-to-workspace "web"; 907 "Mod+Shift+ParenRight".action = kdl.magic-leaf "move-column-to-workspace" "web";
750 908
751 "Mod+BraceRight".action = focus-workspace "read"; 909 "Mod+BraceRight".action = focus-workspace "read";
752 "Mod+Shift+BraceRight".action = move-column-to-workspace "read"; 910 "Mod+Shift+BraceRight".action = kdl.magic-leaf "move-column-to-workspace" "read";
753 911
754 "Mod+BraceLeft".action = focus-workspace "mon"; 912 "Mod+BraceLeft".action = focus-workspace "mon";
755 "Mod+Shift+BraceLeft".action = move-column-to-workspace "mon"; 913 "Mod+Shift+BraceLeft".action = kdl.magic-leaf "move-column-to-workspace" "mon";
756 914
757 "Mod+Asterisk".action = focus-workspace "vid"; 915 "Mod+Asterisk".action = focus-workspace "vid";
758 "Mod+Shift+Asterisk".action = move-column-to-workspace "vid"; 916 "Mod+Shift+Asterisk".action = kdl.magic-leaf "move-column-to-workspace" "vid";
759 917
760 "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; 918 "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}'';
761 "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}''; 919 "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}'';
762 920
763 "Mod+M".action = consume-or-expel-window-left; 921 "Mod+M".action = consume-or-expel-window-left;
764 "Mod+W".action = consume-or-expel-window-right; 922 "Mod+W".action = consume-or-expel-window-right;
@@ -766,10 +924,11 @@ in {
766 "Mod+Shift+M".action = toggle-column-tabbed-display; 924 "Mod+Shift+M".action = toggle-column-tabbed-display;
767 925
768 "Mod+R".action = switch-preset-column-width; 926 "Mod+R".action = switch-preset-column-width;
769 "Mod+Shift+R".action = switch-preset-window-height; 927 "Mod+Shift+R".action = maximize-column;
928 "Mod+Shift+Ctrl+R".action = switch-preset-window-height;
770 "Mod+F".action = center-column; 929 "Mod+F".action = center-column;
771 "Mod+Shift+F".action = maximize-column; 930 "Mod+Shift+F".action = toggle-windowed-fullscreen;
772 "Mod+Shift+Ctrl+F".action = fullscreen-window; 931 "Mod+Ctrl+Shift+F".action = fullscreen-window;
773 932
774 "Mod+V".action = switch-focus-between-floating-and-tiling; 933 "Mod+V".action = switch-focus-between-floating-and-tiling;
775 "Mod+Shift+V".action = toggle-window-floating; 934 "Mod+Shift+V".action = toggle-window-floating;
@@ -825,13 +984,24 @@ in {
825 984
826 "Mod+Semicolon".action = spawn makoctl "dismiss" "--group"; 985 "Mod+Semicolon".action = spawn makoctl "dismiss" "--group";
827 "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all"; 986 "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all";
828 "Mod+Period".action = spawn makoctl "menu" (lib.getExe config.programs.fuzzel.package) "--dmenu"; 987 "Mod+Period".action = spawn makoctl "menu" "--" (lib.getExe config.programs.fuzzel.package) "--dmenu";
829 "Mod+Comma".action = spawn makoctl "restore"; 988 "Mod+Comma".action = spawn makoctl "restore";
830 989
831 "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}"; 990 "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}";
832 "Mod+Control+Shift+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"MoveColumnToWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}"; 991 "Mod+Control+Shift+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"MoveColumnToWorkspace\":{\"reference\":{\"Id\": $workspace_id}, \"focus\": true}}}";
992
993 "Mod+X".action = set-dynamic-cast-window;
994 "Mod+Shift+X".action = set-dynamic-cast-monitor;
995 "Mod+Control+Shift+X".action = clear-dynamic-cast-target;
996
997 "Mod+D".action = with-urgent-window-action "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
998 "Mod+Shift+D".action = with-focused-window-action "{\"Action\":{\"UnsetUrgent\":{\"id\": .id}}}";
999
1000 "Mod+K".action = spawn (lib.getExe' pkgs.worktime "worktime-ui");
1001 "Mod+Shift+K".action = spawn (lib.getExe' pkgs.worktime "worktime-stop");
833 })) 1002 }))
834 (map ({ name, selector, spawn, key, ...}: if key != null && selector != null && spawn != null then bind key { action = focus-or-spawn-action selector name spawn; } else null) cfg.scratchspaces) 1003 (map ({ name, selector, spawn, key, ...}: if key != null && selector != null && spawn != null then bind key { action = focus-or-spawn-action selector name spawn; } else null) cfg.scratchspaces)
1004 (map ({ name, moveKey, ...}: if moveKey != null then bind moveKey { action = kdl.magic-leaf "move-column-to-workspace" name; } else null) cfg.scratchspaces)
835 ] 1005 ]
836 )) 1006 ))
837 ]; 1007 ];
diff --git a/accounts/gkleen@sif/niri/mako.nix b/accounts/gkleen@sif/niri/mako.nix
index 2788fb82..703d5f7b 100644
--- a/accounts/gkleen@sif/niri/mako.nix
+++ b/accounts/gkleen@sif/niri/mako.nix
@@ -3,37 +3,30 @@
3 config = { 3 config = {
4 services.mako = { 4 services.mako = {
5 enable = true; 5 enable = true;
6 font = "Fira Sans 10"; 6 settings = {
7 format = "<i>%s</i>\\n%b"; 7 font = "Fira Sans 10";
8 margin = "2"; 8 format = "<i>%s</i>\\n%b";
9 maxVisible = -1; 9 margin = "2";
10 backgroundColor = "#000000dd"; 10 max-visible = -1;
11 progressColor = "source #223544ff"; 11 background-color = "#000000dd";
12 width = 384; 12 progress-color = "source #223544ff";
13 extraConfig = '' 13 width = 384;
14 outer-margin=1 14 outer-margin = 1;
15 max-history=100 15 max-history = 100;
16 max-icon-size=48 16 max-icon-size = 48;
17 17
18 [grouped] 18 grouped.format = "<b>(%g)</b> <i>%s</i>\\n%b";
19 format=<b>(%g)</b> <i>%s</i>\n%b 19 "urgency=low".text-color = "#999999ff";
20 20 "urgency=critical".background-color = "#900000dd";
21 [urgency=low] 21 "app-name=Element".group-by = "summary";
22 text-color=#999999ff 22 "app-name=poweralertd" = {
23 23 history = false;
24 [urgency=critical] 24 ignore-timeout = true;
25 background-color=#900000dd 25 default-timeout = 2000;
26 26 };
27 [app-name=Element] 27 "app-name=worktime".history = false;
28 group-by=summary 28 "mode=silent".invisible = true;
29 29 };
30 [app-name=poweralertd]
31 ignore-timeout=1
32 default-timeout=2000
33
34 [mode=silent]
35 invisible=1
36 '';
37 package = pkgs.symlinkJoin { 30 package = pkgs.symlinkJoin {
38 name = "${pkgs.mako.name}-wrapped"; 31 name = "${pkgs.mako.name}-wrapped";
39 paths = with pkgs; [ mako ]; 32 paths = with pkgs; [ mako ];
diff --git a/accounts/gkleen@sif/niri/swayosd.nix b/accounts/gkleen@sif/niri/swayosd.nix
index 984927c2..54ebb302 100644
--- a/accounts/gkleen@sif/niri/swayosd.nix
+++ b/accounts/gkleen@sif/niri/swayosd.nix
@@ -3,9 +3,10 @@
3 config = { 3 config = {
4 services.swayosd = { 4 services.swayosd = {
5 enable = true; 5 enable = true;
6 topMargin = 0.946154; 6 topMargin = 0.4769706078;
7 stylePath = pkgs.runCommand "style.css" { 7 stylePath = pkgs.runCommand "style.css" {
8 src = pkgs.writeText "style.scss" '' 8 passAsFile = [ "src" ];
9 src = ''
9 window#osd { 10 window#osd {
10 padding: 12px 20px; 11 padding: 12px 20px;
11 border-radius: 999px; 12 border-radius: 999px;
@@ -59,7 +60,7 @@
59 } 60 }
60 ''; 61 '';
61 buildInputs = with pkgs; [sass]; 62 buildInputs = with pkgs; [sass];
62 } "scss -C --sourcemap=none --style=compact $src $out"; 63 } "scss -C --sourcemap=none --style=compact $srcPath $out";
63 }; 64 };
64 }; 65 };
65} 66}
diff --git a/accounts/gkleen@sif/niri/waybar.nix b/accounts/gkleen@sif/niri/waybar.nix
index cc131c08..c02a9a76 100644
--- a/accounts/gkleen@sif/niri/waybar.nix
+++ b/accounts/gkleen@sif/niri/waybar.nix
@@ -27,8 +27,14 @@ in {
27 modules-right = [ "custom/worktime" "custom/worktime-today" 27 modules-right = [ "custom/worktime" "custom/worktime-today"
28 "custom/weather" 28 "custom/weather"
29 "custom/keymap" 29 "custom/keymap"
30 "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "clock" ]; 30 "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "custom/lid_inhibitor" "clock" ];
31 31
32 "custom/lid_inhibitor" = {
33 format = "{}";
34 return-type = "json";
35 exec = lib.getExe pkgs.waybar-systemd-inhibit;
36 on-click = lib.getExe' pkgs.waybar-systemd-inhibit "waybar-systemd-inhibit-toggle";
37 };
32 "custom/mako" = { 38 "custom/mako" = {
33 format = "{}"; 39 format = "{}";
34 return-type = "json"; 40 return-type = "json";
@@ -211,7 +217,7 @@ in {
211 layer = "top"; 217 layer = "top";
212 position = "top"; 218 position = "top";
213 height = 14; 219 height = 14;
214 output = [ "!eDP-1" "!DP-2" "!DP-3" ]; 220 output = [ "!eDP-1" "!DP-2" "!DP-3" "*" ];
215 modules-left = [ "niri/workspaces" ]; 221 modules-left = [ "niri/workspaces" ];
216 modules-center = [ "niri/window" ]; 222 modules-center = [ "niri/window" ];
217 modules-right = [ "clock" ]; 223 modules-right = [ "clock" ];
@@ -293,7 +299,7 @@ in {
293 #tray { 299 #tray {
294 margin: 0; 300 margin: 0;
295 } 301 }
296 #battery, #idle_inhibitor, #backlight, #wireplumber, #custom-mako { 302 #battery, #idle_inhibitor, #backlight, #wireplumber, #custom-mako, #custom-lid_inhibitor {
297 color: @grey; 303 color: @grey;
298 margin: 0 5px 0 2px; 304 margin: 0 5px 0 2px;
299 } 305 }
@@ -302,7 +308,11 @@ in {
302 margin-left: 6px; 308 margin-left: 6px;
303 } 309 }
304 #custom-mako { 310 #custom-mako {
305 margin-right: 2px; 311 margin-right: 4px;
312 margin-left: 3px;
313 }
314 #custom-lid_inhibitor {
315 margin-right: 3px;
306 margin-left: 3px; 316 margin-left: 3px;
307 } 317 }
308 #battery { 318 #battery {
@@ -330,7 +340,7 @@ in {
330 color: @orange; 340 color: @orange;
331 } 341 }
332 342
333 #idle_inhibitor { 343 #idle_inhibitor, #custom-lid_inhibitor {
334 padding-top: 1px; 344 padding-top: 1px;
335 } 345 }
336 346
@@ -340,6 +350,7 @@ in {
340 } 350 }
341 #clock { 351 #clock {
342 /* margin-right: 5px; */ 352 /* margin-right: 5px; */
353 font-feature-settings: "tnum";
343 } 354 }
344 ''; 355 '';
345 }; 356 };
diff --git a/accounts/gkleen@sif/synadm/default.nix b/accounts/gkleen@sif/synadm/default.nix
new file mode 100644
index 00000000..0a8e0d4c
--- /dev/null
+++ b/accounts/gkleen@sif/synadm/default.nix
@@ -0,0 +1,9 @@
1{ config, pkgs, ... }:
2{
3 home.packages = with pkgs; [ synadm ];
4 sops.secrets."synadm.yaml" = {
5 format = "binary";
6 sopsFile = ./synadm_yaml;
7 path = config.xdg.configHome + "/synadm.yaml";
8 };
9}
diff --git a/accounts/gkleen@sif/synadm/synadm_yaml b/accounts/gkleen@sif/synadm/synadm_yaml
new file mode 100644
index 00000000..8d951ccc
--- /dev/null
+++ b/accounts/gkleen@sif/synadm/synadm_yaml
@@ -0,0 +1,15 @@
1{
2 "data": "ENC[AES256_GCM,data:qJy4Pmbbxja4jmW7OaHsD0mQZ7anZwLhiVmAgkavb+CqwWGDnUBXdz22/MHCbxng5NshcFSpBoCBhgY6B9V2bUiES6bH9AtMlDcs9ebKGMArBTUTnQ2MjWQGfQTqraWdNgy+n327uj9swwCH8EZXdYH/Hlv0t/re470W+VOHeXhGghQ3Y9IGz2sgfvMGr8QxaJNydZz85rgs5QUP/PglCwWIOw2mY1EX2vYwnmiAo49LmIEaxWvRi++KHaeBveDt0nlkJwzUlipL2VOKWxkgpK3yGucQn2mz+FRe1btp+4KGm8H17eUI9FO9sBwq,iv:kgM921ovwCgDYHQj3c5Rupy/8JxHehxUD2jb1k9Ik2Y=,tag:3TLQkJbv679VWy8V2TMugw==,type:str]",
3 "sops": {
4 "age": [
5 {
6 "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866",
7 "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6bzVHUGNxZTF2WC9MYmZr\neGdVVzJXN3lGdEk3cTBER3J6UTFtcUJna2d3CjdNQmRXd2haZW1MYlJzNkk1dWVD\nVTFQc2gvS0JrejJ6SFh2MXpPWDZpRE0KLS0tIE0wTC85bEpvSnlGdGFkZVFhNjFZ\nbzRiZkxMWUg2ODNVUlBmNFlPNGRrZlkK1VXLJWcssv3ETyZSSM/Hhn5VIaI9iov9\nzShZA9Zx/FX6PYTuUMC29pJ57gKourcIxa/7HwSv/xYn1A6WcYfgSg==\n-----END AGE ENCRYPTED FILE-----\n"
8 }
9 ],
10 "lastmodified": "2025-05-18T11:03:42Z",
11 "mac": "ENC[AES256_GCM,data:yonJC68PhilAgEHNNJQ8nO53Qo3rx/LnfiOWfuMm24bOUIH9QM3WZZxpigd7bHI4eC4TqRb4LvcSi0nEURTRAhwiTqGNrWbpw2Iv3n5dhLEN9aTcetG5ZuhaXqfVUoML45/ovdBZG/0l8+XIHqxN2M/g/h4JwKoR/6lqzcrVhgo=,iv:xvxBJwy+E5zUdjhGPdZPdy7tnBIEj50hfiDJFsS3wNg=,tag:L4Fas36ZOg4h0QQwC4gjNA==,type:str]",
12 "unencrypted_suffix": "_unencrypted",
13 "version": "3.10.2"
14 }
15}
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix
index 2237b708..18c2315f 100644
--- a/accounts/gkleen@sif/systemd.nix
+++ b/accounts/gkleen@sif/systemd.nix
@@ -242,7 +242,7 @@ in {
242 "-${lib.getExe pkgs.playerctl} -a pause" 242 "-${lib.getExe pkgs.playerctl} -a pause"
243 "-${lib.getExe (pkgs.writeShellApplication { 243 "-${lib.getExe (pkgs.writeShellApplication {
244 name = "generate-css"; 244 name = "generate-css";
245 runtimeInputs = with pkgs; [cfg.programs.wpaperd.package jq coreutils imagemagick findutils]; 245 runtimeInputs = with pkgs; [cfg.services.wpaperd.package jq coreutils imagemagick findutils];
246 text = '' 246 text = ''
247 declare -A monitors 247 declare -A monitors
248 monitors=() 248 monitors=()
@@ -333,21 +333,21 @@ in {
333 ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; 333 ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\"";
334 }; 334 };
335 }; 335 };
336 wpaperd = { 336 # wpaperd = {
337 Install = { 337 # Install = {
338 WantedBy = ["graphical-session.target"]; 338 # WantedBy = ["graphical-session.target"];
339 }; 339 # };
340 Unit = { 340 # Unit = {
341 After = [ "graphical-session.target" ]; 341 # After = [ "graphical-session.target" ];
342 PartOf = [ "graphical-session.target" ]; 342 # PartOf = [ "graphical-session.target" ];
343 }; 343 # };
344 Service = { 344 # Service = {
345 ExecStart = lib.getExe cfg.programs.wpaperd.package; 345 # ExecStart = lib.getExe cfg.services.wpaperd.package;
346 Type = "simple"; 346 # Type = "simple";
347 Restart = "always"; 347 # Restart = "always";
348 RestartSec = "2s"; 348 # RestartSec = "2s";
349 }; 349 # };
350 }; 350 # };
351 xembed-sni-proxy = { 351 xembed-sni-proxy = {
352 Unit = { 352 Unit = {
353 PartOf = lib.mkForce ["tray.target"]; 353 PartOf = lib.mkForce ["tray.target"];
@@ -385,6 +385,8 @@ in {
385 }; 385 };
386 Service = { 386 Service = {
387 ExecStart = "${config.systemd.package}/lib/systemd/systemd-socket-proxyd --exit-idle-time=60s 127.0.0.1:${toString (port + 1)}"; 387 ExecStart = "${config.systemd.package}/lib/systemd/systemd-socket-proxyd --exit-idle-time=60s 127.0.0.1:${toString (port + 1)}";
388 Restart = "always";
389 RestartSec = "23s";
388 }; 390 };
389 }) [{ host = "proxy.ssh.math.lmu.de"; port = 8118; } { host = "proxy.vidhar"; port = 8120; } { host = "proxy.mathw0h"; port = 8122; } { host = "proxy.mathw0e"; port = 8124; }]); 391 }) [{ host = "proxy.ssh.math.lmu.de"; port = 8118; } { host = "proxy.vidhar"; port = 8120; } { host = "proxy.mathw0h"; port = 8122; } { host = "proxy.mathw0e"; port = 8124; }]);
390 sockets = listToAttrs (map (port: nameValuePair "proxy-to-autossh-socks@${toString port}" { 392 sockets = listToAttrs (map (port: nameValuePair "proxy-to-autossh-socks@${toString port}" {
diff --git a/accounts/gkleen@sif/utils/async-yt-dlp.nix b/accounts/gkleen@sif/utils/async-yt-dlp.nix
new file mode 100644
index 00000000..c3b82ec5
--- /dev/null
+++ b/accounts/gkleen@sif/utils/async-yt-dlp.nix
@@ -0,0 +1,57 @@
1{ writers, python3Packages, ... }:
2
3writers.writePython3Bin "async-yt-dlp" {
4 libraries = with python3Packages; [ yt-dlp ];
5 flakeIgnore = ["E501"];
6} ''
7import sys
8import os
9
10import yt_dlp
11import yt_dlp.options
12from collections import namedtuple
13import socket
14from pathlib import Path
15import json
16
17create_parser = yt_dlp.options.create_parser
18
19
20def parse_patched_options(opts):
21 patched_parser = create_parser()
22 patched_parser.defaults.update({
23 'ignoreerrors': False,
24 'retries': 0,
25 'fragment_retries': 0,
26 'extract_flat': False,
27 'concat_playlist': 'never',
28 })
29 yt_dlp.options.create_parser = lambda: patched_parser
30 try:
31 return yt_dlp.parse_options(opts)
32 finally:
33 yt_dlp.options.create_parser = create_parser
34
35
36default_opts = parse_patched_options([]).ydl_opts
37
38
39def cli_to_api(opts):
40 opts = parse_patched_options(opts)
41 urls = opts.urls
42 opts = opts.ydl_opts
43
44 diff = {k: v for k, v in opts.items() if default_opts[k] != v}
45 if 'postprocessors' in diff:
46 diff['postprocessors'] = [pp for pp in diff['postprocessors']
47 if pp not in default_opts['postprocessors']]
48 return namedtuple('Options', ('params', 'urls'))(diff, urls)
49
50
51if __name__ == '__main__':
52 opts = cli_to_api(sys.argv[1:])
53 with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
54 sock.connect(str(Path(os.environ["XDG_RUNTIME_DIR"]) / "yt-dlp.sock").encode('utf-8'))
55 with sock.makefile(mode='w', buffering=1, encoding='utf-8') as fh:
56 json.dump(opts._asdict(), fh)
57''
diff --git a/accounts/gkleen@sif/utils/pdf2pdf.nix b/accounts/gkleen@sif/utils/pdf2pdf.nix
new file mode 100644
index 00000000..9f4cbc3e
--- /dev/null
+++ b/accounts/gkleen@sif/utils/pdf2pdf.nix
@@ -0,0 +1,8 @@
1pkgs@{ lib, resholve, zsh, ghostscript_headless, ... }:
2
3resholve.writeScriptBin "pdf2pdf" {
4 inputs = with pkgs; [ghostscript_headless];
5 interpreter = lib.getExe zsh;
6} ''
7 exec gs -dPDFSETTINGS=/prepress -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER -dPreserveAnnots=false "-sOutputFile=''${2}" "''${1}"
8''
diff --git a/accounts/gkleen@sif/zshrc b/accounts/gkleen@sif/zshrc
index c628e2e9..702990c3 100644
--- a/accounts/gkleen@sif/zshrc
+++ b/accounts/gkleen@sif/zshrc
@@ -2,17 +2,14 @@ dir() {
2 curlArchive=false 2 curlArchive=false
3 templateArchive="" 3 templateArchive=""
4 repoUrl="" 4 repoUrl=""
5 nixShell=""
6 findNix=false
7 dir="" 5 dir=""
8 forceShell=false 6 forceShell=false
9 wormhole=false 7 wormhole=false
10 gitWorktree="" 8 gitWorktree=""
11 # notmuchMsg=""
12 quickserve=false
13 modifyPDF="" 9 modifyPDF=""
10 miniserve=false
14 11
15 while getopts ':t:a:s:Sd:ir:wqg:p:' arg; do 12 while getopts ':t:a:d:ir:wg:p:m' arg; do
16 case $arg in 13 case $arg in
17 "t") ;; 14 "t") ;;
18 "a") 15 "a")
@@ -23,16 +20,13 @@ dir() {
23 templateArchive=${OPTARG:a} 20 templateArchive=${OPTARG:a}
24 fi 21 fi
25 ;; 22 ;;
26 "s") nixShell=${OPTARG:a} ;;
27 "S") findNix=true ;;
28 "d") dir=${OPTARG} ;; 23 "d") dir=${OPTARG} ;;
29 "i") forceShell=true ;; 24 "i") forceShell=true ;;
30 "r") repoUrl=${OPTARG} ;; 25 "r") repoUrl=${OPTARG} ;;
31 "w") wormhole=true ;; 26 "w") wormhole=true ;;
32 "g") gitWorktree=${OPTARG} ;; 27 "g") gitWorktree=${OPTARG} ;;
33 # "n") notmuchMsg=${OPTARG} ;;
34 "q") quickserve=true ;;
35 "p") modifyPDF=${OPTARG:a} ;; 28 "p") modifyPDF=${OPTARG:a} ;;
29 "m") miniserve=true ;;
36 *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;; 30 *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;;
37 esac 31 esac
38 done 32 done
@@ -56,20 +50,34 @@ dir() {
56 gitWorktree="" 50 gitWorktree=""
57 fi 51 fi
58 52
53 miniservePIDFile=""
54 if [[ ${miniserve} = "true" ]]; then
55 miniservePIDFile=$(mktemp --tmpdir --suffix=.pid)
56 fi
57
59 cleanup() 58 cleanup()
60 { 59 {
61 cd ${modifyPDF:h} 60 if [[ -n ${modifyPDF} ]]; then
62 [[ -n ${modifyPDF} ]] && nix shell 'nixos#imagemagick' -c convert -verbose ${dir}/${modifyPDF:t:r}_*.png(on) ${modifyPDF} 61 cd ${modifyPDF:h}
62 typeset -a pages
63 eval 'pages=(${dir}/${modifyPDF:t:r}_*.png(on))'
64 magick -verbose "$pages" ${modifyPDF}
65 modifyPDF=""
66 fi
67 if [[ -n ${miniservePIDFile} ]]; then
68 command kill --verbose -- $(cat ${miniservePIDFile}) && wait $(cat ${miniservePIDFile})
69 miniservePIDFile=""
70 fi
63 } 71 }
64 72
65 ( 73 (
74 set -o localoptions -o localtraps
75 trap 'return 1' INT TERM
66 trap cleanup EXIT 76 trap cleanup EXIT
67 77
68 cd ${dir} 78 cd ${dir}
69 export dir; 79 export dir;
70 80
71 ${findNix} && { nixShell=$(findNix) || return $? }
72
73 [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} . 81 [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} .
74 82
75 [[ -n ${modifyPDF} ]] && templateArchive=${modifyPDF} 83 [[ -n ${modifyPDF} ]] && templateArchive=${modifyPDF}
@@ -82,23 +90,23 @@ dir() {
82 } 90 }
83 trap cleanup EXIT 91 trap cleanup EXIT
84 92
85 if ${curlArchive}; then 93 if [[ $curlArchive = "true" ]]; then
86 archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}") 94 archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}")
87 95
88 curl -L -o ${archiveFile} ${templateArchive} 96 curl -SfL -o ${archiveFile} ${templateArchive}
89 97
90 templateArchive=${archiveFile} 98 templateArchive=${archiveFile}
91 fi 99 fi
92 100
93 unpack=true 101 unpack=true
94 while ${unpack}; do 102 while [[ $unpack = "true" ]]; do
95 case $(file --brief --mime-type --dereference ${templateArchive}) in 103 case $(file --brief --mime-type --dereference ${templateArchive}) in
96 application/zip) 104 application/zip)
97 unzip ${templateArchive} 105 unzip ${templateArchive}
98 unpack=false 106 unpack=false
99 ;; 107 ;;
100 application/vnd.debian.binary-package) 108 application/vnd.debian.binary-package)
101 nix shell 'nixos#binutils' --command ar x ${templateArchive} 109 ar x ${templateArchive}
102 mkdir control data 110 mkdir control data
103 tar -C control -xvaf control.* 111 tar -C control -xvaf control.*
104 tar -C data -xvaf data.* 112 tar -C data -xvaf data.*
@@ -106,7 +114,7 @@ dir() {
106 ;; 114 ;;
107 application/x-rpm) 115 application/x-rpm)
108 cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio") 116 cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio")
109 nix shell 'nixos#busybox' --command rpm2cpio ${templateArchive} > ${cpioArchive} 117 rpm2cpio ${templateArchive} > ${cpioArchive}
110 templateArchive=${cpioArchive} 118 templateArchive=${cpioArchive}
111 unpack=true 119 unpack=true
112 ;; 120 ;;
@@ -115,15 +123,19 @@ dir() {
115 unpack=false 123 unpack=false
116 ;; 124 ;;
117 application/pdf) 125 application/pdf)
118 nix shell 'nixos#ghostscript' 'nixos#imagemagick' -c convert -verbose -density 400 ${templateArchive} ${modifyPDF:t:r}_%0d.png 126 magick -verbose -density 400 ${templateArchive} ${modifyPDF:t:r}_%0d.png
119 unpack=false 127 unpack=false
120 ;; 128 ;;
121 application/octet-stream) 129 application/octet-stream)
122 if [[ $(file --brief --dereferenc ${templateArchive}) =~ Squashfs ]]; then 130 if [[ $(file --brief --dereference ${templateArchive}) =~ Squashfs ]]; then
123 nix shell 'nixos#squashfsTools' -c unsquashfs -d . ${templateArchive} 131 unsquashfs -d . ${templateArchive}
124 unpack=false 132 unpack=false
125 fi 133 fi
126 ;; 134 ;;
135 application/x-iso9660-image)
136 7z x ${templateArchive}
137 unpack=false
138 ;;
127 *) 139 *)
128 tar -xvaf ${templateArchive} 140 tar -xvaf ${templateArchive}
129 unpack=false 141 unpack=false
@@ -134,25 +146,21 @@ dir() {
134 fi 146 fi
135 147
136 148
137 ${wormhole} && wormhole receive --accept-file 149 [[ $wormhole = "true" ]] && wormhole receive --accept-file
138 150
139 151
140 if ${quickserve}; then 152 if [[ ${#@} -gt 0 ]]; then
141 quickserve --root . --upload . --show-hidden --tar gz 153 ${@}
142 fi 154 fi
143 155
156 cd $(pwd) # Needed for mounting to work
144 157
145 if [[ ${#@} -eq 0 ]] || ${forceShell}; then 158 if [[ ${miniserve} = "true" ]]; then
146 if [[ ${#@} -gt 0 ]]; then 159 miniserve --random-route --hidden --enable-tar-gz --enable-zip . &
147 if [[ -z ${nixShell} ]]; then 160 echo $! > "${miniservePIDFile}"
148 ${@} 161 fi
149 else
150 nix-shell ${nixShell} --run "${@}"
151 fi
152 fi
153
154 cd $(pwd) # Needed for mounting to work
155 162
163 if [[ ${#@} -eq 0 ]] && [[ ${miniserve} != "true" ]] || [[ $forceShell = "true" ]]; then
156 isSingleDir() { 164 isSingleDir() {
157 typeset -a contents 165 typeset -a contents
158 contents=(*(N) .*(N)) 166 contents=(*(N) .*(N))
@@ -166,18 +174,9 @@ dir() {
166 } 174 }
167 while d=$(isSingleDir); do cd ${d}; done 175 while d=$(isSingleDir); do cd ${d}; done
168 176
169 177 zsh
170 if [[ -z ${nixShell} ]]; then 178 elif [[ ${miniserve} == "true" ]]; then
171 zsh 179 wait $(cat "${miniservePIDFile}")
172 else
173 nix-shell ${nixShell} --run zsh
174 fi
175 else
176 if [[ -z ${nixShell} ]]; then
177 ${@}
178 else
179 nix-shell ${nixShell} --run "${@}"
180 fi
181 fi 180 fi
182 ) 181 )
183} 182}
@@ -185,27 +184,30 @@ dir() {
185tmpdir() { 184tmpdir() {
186 cleanup() 185 cleanup()
187 { 186 {
188 cd / 187 cd /
189 unmount() { 188 unmount() {
190 printf "Unmounting %s\n" ${1} >&2 189 printf "Unmounting %s\n" ${1} >&2
191 fusermount -u ${1} || umount ${1} || sudo umount ${1} 190 fusermount -u ${1} || umount ${1} || sudo umount ${1}
192 } 191 }
193 192
194 if mountpoint -q -- ${dir}; then 193 if [[ -n ${dir} ]]; then
195 unmount ${dir} || return $? 194 if mountpoint -q -- ${dir}; then
196 else 195 unmount ${dir} || return $?
197 while read -d $'\0' subDir; do 196 else
198 mountpoint -q -- ${subDir} || continue 197 while read -d $'\0' subDir; do
199 unmount ${subDir} || return $? 198 mountpoint -q -- ${subDir} || continue
200 done <<<$(find ${dir} -xdev -type d -print0 | sort -zr) 199 unmount ${subDir} || return $?
201 fi 200 done <<<$(find ${dir} -xdev -type d -print0 | sort -zr)
202 201 fi
203 rm -rfv --one-file-system -- ${dir} 202
203 rm -rfv --one-file-system -- ${dir}
204 dir=""
205 fi
204 } 206 }
205 207
206 local tmpdir="" 208 local tmpdir=""
207 209
208 while getopts ':t:a:s:Sd:ir:wqg:p:' arg; do 210 while getopts ':t:a:d:ir:wg:p:m' arg; do
209 case $arg in 211 case $arg in
210 "t") tmpdir="=${OPTARG}" ;; 212 "t") tmpdir="=${OPTARG}" ;;
211 "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;; 213 "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;;
@@ -213,6 +215,8 @@ tmpdir() {
213 done 215 done
214 216
215 ( 217 (
218 set -o localoptions -o localtraps
219 trap 'return 1' INT TERM
216 trap cleanup EXIT 220 trap cleanup EXIT
217 221
218 222
@@ -231,17 +235,7 @@ clock() {
231} 235}
232 236
233public-ip() { 237public-ip() {
234 curl -s -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip' 238 curl -sSf -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip'
235}
236
237nix-ghci() {
238 pkgExpr=""
239 if [[ ${#@} -gt 0 ]]; then
240 pkgExpr="${1}"
241 shift
242 fi
243
244 nix-shell -p "with (import <nixpkgs> {}); pkgs.haskellPackages.ghcWithPackages (p: with p; [${pkgExpr}])" --run "ghci ${@}"
245} 239}
246 240
247swap() { 241swap() {
@@ -271,14 +265,6 @@ l() {
271 ls --long --binary --git --time-style=iso --header $@ 265 ls --long --binary --git --time-style=iso --header $@
272} 266}
273 267
274re() {
275 systemctl restart $@
276}
277
278ure() {
279 systemctl --user restart $@
280}
281
282ssh-installer() { 268ssh-installer() {
283 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@ 269 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@
284} 270}