Flake for my NixOS devices
at musnix 716 lines 24 kB view raw
1{inputs, ...}: { 2 config, 3 lib, 4 pkgs, 5 ... 6}: { 7 imports = [inputs.niri.homeModules.niri]; 8 9 options.cow.gdi = { 10 enable = lib.mkEnableOption "Niri + Customizations"; 11 doIdle = lib.mkEnableOption "Turn off screen, sleep, etc. from inactivity"; 12 }; 13 14 config = let 15 iconTheme = { 16 name = "Tela-green"; 17 package = pkgs.tela-icon-theme; 18 }; 19 cursorTheme = { 20 name = "catppuccin-mocha-dark-cursors"; 21 package = pkgs.catppuccin-cursors.mochaDark; 22 size = 24; 23 }; 24 in 25 lib.mkIf config.cow.gdi.enable { 26 nixpkgs.overlays = [inputs.niri.overlays.niri]; 27 28 # Me when I'm stupid and enable my preferred keyring unconditionally in a flake for a WM 29 services.gnome-keyring.enable = lib.mkForce false; 30 31 home.packages = with pkgs; [ 32 alsa-utils 33 34 dconf 35 36 cursorTheme.package 37 iconTheme.package 38 39 xdg-terminal-exec # For gtk-launch, etc to be able to open `Terminal` desktop entries 40 wezterm 41 42 # Shell Components 43 hyprlock 44 45 ## Waybar 46 qt6.qttools # For component 47 48 pavucontrol 49 50 wf-recorder 51 slurp 52 grim 53 xdg-utils 54 swappy 55 libnotify 56 swaynotificationcenter 57 wl-clipboard 58 59 hunspell 60 hunspellDicts.en_US-large 61 ]; 62 63 xdg.mimeApps = { 64 enable = true; 65 defaultApplications = { 66 "application/pdf" = lib.mkIf config.cow.firefox.enable "firefox-devedition.desktop"; 67 "image/*" = lib.mkIf config.cow.firefox.enable "firefox-devedition.desktop"; 68 "text/*" = lib.mkIf config.cow.neovim.enable "neovide.desktop"; 69 "inode/directory" = lib.mkIf config.cow.yazi.enable "yazi.desktop"; 70 "inode/mount-point" = lib.mkIf config.cow.yazi.enable "yazi.desktop"; 71 "x-scheme-handler/file" = lib.mkIf config.cow.yazi.enable "yazi.desktop"; 72 }; 73 }; 74 75 fonts.fontconfig.enable = false; 76 77 programs.niri = { 78 enable = true; 79 settings = { 80 prefer-no-csd = true; 81 82 environment = 83 { 84 NIXOS_OZONE_WL = "1"; 85 TERMINAL = lib.getExe pkgs.wezterm; 86 } 87 // (builtins.mapAttrs (_: v: builtins.toString v) config.home.sessionVariables); # TODO: Hack? 88 89 screenshot-path = "~/Pictures/Screenshots/%Y%m%d_%H%M%S.png"; 90 91 xwayland-satellite.path = lib.getExe pkgs.xwayland-satellite-unstable; 92 93 window-rules = [ 94 { 95 geometry-corner-radius = let 96 s = 10.0; 97 in { 98 top-left = s; 99 top-right = s; 100 bottom-left = s; 101 bottom-right = s; 102 }; 103 clip-to-geometry = true; 104 } 105 ]; 106 107 hotkey-overlay = { 108 hide-not-bound = true; 109 skip-at-startup = true; 110 }; 111 112 clipboard.disable-primary = true; 113 114 input = { 115 focus-follows-mouse.enable = true; 116 keyboard.numlock = true; 117 # power-key-handling.enable = false; 118 touchpad.natural-scroll = true; 119 }; 120 121 layout = { 122 default-column-width.proportion = 1.; 123 focus-ring.active.gradient = { 124 in' = "oklab"; 125 angle = 135; 126 relative-to = "workspace-view"; 127 from = "#74c7ec"; 128 to = "#cba6f7"; 129 }; 130 }; 131 132 binds = with config.lib.niri.actions; let 133 spawnPkg = p: spawn "${lib.getExe p}"; 134 move-column-to-workspace = n: {move-column-to-workspace = [n];}; 135 terminal = pkgs.wezterm; 136 spawnTerm = spawnPkg terminal; 137 spawnPlayerctl = spawnPkg pkgs.playerctl; 138 spawnRofi = spawn "rofi"; 139 spawnSh = spawn "sh" "-c"; 140 spawnOsd = spawn "${pkgs.swayosd}/bin/swayosd-client"; 141 launchDesktop = x: spawn "${pkgs.gtk3}/bin/gtk-launch" "${x}.desktop"; 142 brightness = spawnOsd "--brightness"; 143 volume = spawnOsd "--output-volume"; 144 in { 145 # Niri Stuff 146 147 ## Basics 148 "Mod+X".action = quit; 149 "Mod+Tab".action = toggle-overview; 150 "Mod+Slash".action = show-hotkey-overlay; 151 "Mod+C".action = close-window; 152 153 "Mod+Left".action = focus-column-left; 154 "Mod+Down".action = focus-window-down; 155 "Mod+Up".action = focus-window-up; 156 "Mod+Right".action = focus-column-right; 157 158 "Mod+Shift+Left".action = move-column-left; 159 "Mod+Shift+Down".action = move-window-down; 160 "Mod+Shift+Up".action = move-window-up; 161 "Mod+Shift+Right".action = move-column-right; 162 163 "Mod+Home".action = focus-column-first; 164 "Mod+End".action = focus-column-last; 165 "Mod+Shift+Home".action = move-column-to-first; 166 "Mod+Shift+End".action = move-column-to-last; 167 168 "Mod+BracketLeft".action = consume-or-expel-window-left; 169 "Mod+BracketRight".action = consume-or-expel-window-right; 170 171 "Mod+F".action = maximize-column; 172 "Mod+Shift+F".action = fullscreen-window; 173 "Mod+Ctrl+F".action = expand-column-to-available-width; 174 "Mod+Ctrl+Shift+F".action = toggle-windowed-fullscreen; 175 176 "Mod+Page_Down".action = focus-workspace-down; 177 "Mod+Page_Up".action = focus-workspace-up; 178 "Mod+U".action = focus-workspace-down; 179 "Mod+I".action = focus-workspace-up; 180 "Mod+Shift+Page_Down".action = move-column-to-workspace-down; 181 "Mod+Shift+Page_Up".action = move-column-to-workspace-up; 182 "Mod+Shift+U".action = move-column-to-workspace-down; 183 "Mod+Shift+I".action = move-column-to-workspace-up; 184 185 "Mod+1".action = focus-workspace 1; 186 "Mod+2".action = focus-workspace 2; 187 "Mod+3".action = focus-workspace 3; 188 "Mod+4".action = focus-workspace 4; 189 "Mod+5".action = focus-workspace 5; 190 "Mod+6".action = focus-workspace 6; 191 "Mod+7".action = focus-workspace 7; 192 "Mod+8".action = focus-workspace 8; 193 "Mod+9".action = focus-workspace 9; 194 "Mod+Ctrl+1".action = move-column-to-workspace 1; 195 "Mod+Ctrl+2".action = move-column-to-workspace 2; 196 "Mod+Ctrl+3".action = move-column-to-workspace 3; 197 "Mod+Ctrl+4".action = move-column-to-workspace 4; 198 "Mod+Ctrl+5".action = move-column-to-workspace 5; 199 "Mod+Ctrl+6".action = move-column-to-workspace 6; 200 "Mod+Ctrl+7".action = move-column-to-workspace 7; 201 "Mod+Ctrl+8".action = move-column-to-workspace 8; 202 "Mod+Ctrl+9".action = move-column-to-workspace 9; 203 204 "Mod+Escape" = { 205 action = toggle-keyboard-shortcuts-inhibit; 206 allow-inhibiting = false; 207 }; 208 209 "Mod+W".action = spawn "systemctl" "--user" "restart" "waybar.service"; 210 "Mod+Shift+W".action = spawn "systemctl" "--user" "stop" "waybar.service"; 211 212 "Mod+Shift+R".action = 213 spawnSh "pkill wf-recorder --signal SIGINT ||${pkgs.nushell}/bin/nu ${../res/screenrec.nu}"; 214 "Mod+Shift+S".action = { 215 screenshot = []; 216 }; 217 "Print".action = { 218 screenshot = []; 219 }; 220 "Mod+L".action = spawnSh "pidof hyprlock || hyprlock --immediate"; 221 "Mod+Z".action = spawn "systemctl" "suspend"; 222 "Super+Alt+Ctrl+Shift+L".action = spawn "xdg-open" "https://linkedin.com"; 223 224 # Terminal 225 "Mod+T".action = spawnTerm; 226 227 # Rofi 228 "Mod+S".action = spawnRofi "-show" "drun" "-show-icons"; 229 "Mod+B".action = spawnPkg pkgs.rofi-bluetooth; 230 "Mod+Shift+E".action = spawnRofi "-modi" "emoji" "-show" "emoji"; 231 "Mod+Alt+C".action = 232 spawnRofi "-show" "calc" "-modi" "calc" "-no-show-match" "-no-sort" "-calc-command" 233 "echo -n '{result}' | wl-copy"; 234 "Mod+V".action = 235 spawnSh "cliphist list | sed -r 's/\[\[ binary data (.* .iB) (.*) (.*) \]\]/ 󰋩 \2 Image (\3, \1)/g' | rofi -dmenu -display-columns 2 -p Clipboard | cliphist decode | wl-copy"; 236 "Mod+Alt+V".action = 237 spawnSh "echo -e \"Yes\\nNo\" | [[ $(rofi -dmenu -mesg \"Clear Clipboard History?\" -p Clear) == \"Yes\" ]] && cliphist wipe"; 238 239 # Yazi 240 "Mod+E".action = lib.mkIf config.cow.yazi.enable (launchDesktop "yazi"); 241 242 # Firefox 243 "Mod+Q".action = lib.mkIf config.cow.firefox.enable (launchDesktop "firefox-devedition"); 244 245 # Pavucontrol 246 "Mod+A".action = spawnPkg pkgs.pavucontrol "--tab" "5"; 247 248 # Brightness 249 "XF86MonBrightnessUp" = { 250 action = brightness "raise"; 251 allow-when-locked = true; 252 }; 253 "XF86MonBrightnessDown" = { 254 action = brightness "lower"; 255 allow-when-locked = true; 256 }; 257 258 # Volume 259 "XF86AudioRaiseVolume" = { 260 action = volume "raise"; 261 allow-when-locked = true; 262 }; 263 "XF86AudioLowerVolume" = { 264 action = volume "lower"; 265 allow-when-locked = true; 266 }; 267 "XF86AudioMute" = { 268 action = volume "mute-toggle"; 269 allow-when-locked = true; 270 }; 271 272 # Playerctl 273 "XF86AudioPlay" = { 274 action = spawnPlayerctl "play-pause"; 275 allow-when-locked = true; 276 }; 277 "XF86AudioPause" = { 278 action = spawnPlayerctl "pause"; 279 allow-when-locked = true; 280 }; 281 "XF86AudioStop" = { 282 action = spawnPlayerctl "stop"; 283 allow-when-locked = true; 284 }; 285 "XF86AudioNext" = { 286 action = spawnPlayerctl "next"; 287 allow-when-locked = true; 288 }; 289 "XF86AudioPrev" = { 290 action = spawnPlayerctl "previous"; 291 allow-when-locked = true; 292 }; 293 }; 294 }; 295 }; 296 297 catppuccin.hyprlock.useDefaultConfig = false; 298 programs.hyprlock = { 299 enable = true; 300 301 settings = { 302 background = { 303 monitor = ""; 304 path = "${config.cow.pictures.bg}"; 305 blur_passes = 1; 306 }; 307 shape = [ 308 { 309 monitor = ""; 310 color = "$crust"; 311 position = "0, 30"; 312 rounding = 10; 313 border_size = 2; 314 border_color = "$mauve"; 315 size = "500, 500"; 316 shadow_passes = 1; 317 shadow_size = 2; 318 } 319 { 320 monitor = ""; 321 color = "$crust"; 322 position = "0, -30"; 323 rounding = 10; 324 border_size = 2; 325 border_color = "$mauve"; 326 size = "600, 50"; 327 valign = "top"; 328 shadow_passes = 1; 329 shadow_size = 2; 330 } 331 ]; 332 image = { 333 monitor = ""; 334 path = "${config.cow.pictures.pfp}"; 335 size = 150; 336 rounding = -1; 337 border_size = 4; 338 border_color = "$mauve"; 339 rotate = 0; 340 position = "0, 120"; 341 halign = "center"; 342 valign = "center"; 343 }; 344 "input-field" = { 345 monitor = ""; 346 size = "250, 50"; 347 outline_thickness = 2; 348 dots_size = 0.25; # Scale of input-field height, 0.2 - 0.8 349 dots_spacing = 0.15; # Scale of dots' absolute size, 0.0 - 1.0 350 dots_center = false; 351 dots_rounding = -1; # -1 default circle, -2 follow input-field rounding 352 outer_color = "$surface0"; 353 inner_color = "$base"; 354 font_color = "$text"; 355 fade_on_empty = false; 356 fade_timeout = 1000; # Milliseconds before fade_on_empty is triggered. 357 placeholder_text = ''<span foreground="##cdd6f4" style="italic">Password</span>''; 358 hide_input = false; 359 rounding = -1; # -1 means complete rounding (circle/oval) 360 check_color = "$peach"; 361 fail_color = "$red"; # if authentication failed, changes outer_color and fail message color 362 fail_text = "<i>$FAIL <b>($ATTEMPTS)</b></i>"; 363 fail_transition = 300; # transition time in ms between normal outer_color and fail_color 364 capslock_color = -1; 365 numlock_color = -1; 366 bothlock_color = -1; # when both locks are active. -1 means don't change outer color (same for above) 367 invert_numlock = false; # change color if numlock is off 368 swap_font_color = false; # see below 369 370 position = "0, -80"; 371 halign = "center"; 372 valign = "center"; 373 }; 374 label = [ 375 { 376 monitor = ""; 377 text = "$DESC"; 378 color = "$text"; 379 font_size = 25; 380 font_family = "sans-serif"; 381 rotate = 0; # degrees, counter-clockwise 382 383 position = "0, 0"; 384 halign = "center"; 385 valign = "center"; 386 } 387 { 388 monitor = ""; 389 text = ''cmd[update:30000] echo " $(${pkgs.uutils-coreutils-noprefix}/bin/date +"%A, %B %-d | %I:%M %p")$(${pkgs.nushell}/bin/nu ${../res/bat_display.nu}) "''; 390 color = "$text"; 391 font_size = 20; 392 font_family = "sans-serif"; 393 rotate = 0; # degrees, counter-clockwise 394 395 position = "0, -40"; 396 halign = "center"; 397 valign = "top"; 398 } 399 ]; 400 }; 401 }; 402 403 catppuccin.rofi.enable = false; 404 405 systemd.user.services = let 406 target = config.wayland.systemd.target; 407 mkShellService = { 408 desc, 409 service, 410 }: { 411 Install = { 412 WantedBy = [target]; 413 }; 414 415 Unit = { 416 ConditionEnvironment = "WAYLAND_DISPLAY"; 417 Description = desc; 418 After = [target]; 419 PartOf = [target]; 420 }; 421 422 Service = service; 423 }; 424 in { 425 battery-notif = mkShellService { 426 desc = "Battery Notification Service"; 427 428 service = { 429 ExecStart = ''${pkgs.nushell}/bin/nu --plugins "[${ 430 lib.getExe inputs.nu_plugin_dbus.packages.${pkgs.system}.default 431 }]" ${../res/battery_notif.nu}''; 432 Restart = "on-failure"; 433 RestartSec = "10"; 434 }; 435 }; 436 437 swaybg = lib.mkIf config.cow.pictures.enable (mkShellService { 438 desc = "Sway Background Image"; 439 service = { 440 ExecStart = "${lib.getExe pkgs.swaybg} -m fill --image ${config.cow.pictures.bg}"; 441 Restart = "on-failure"; 442 RestartSec = "10"; 443 }; 444 }); 445 446 mpris-idle-inhibit = mkShellService { 447 desc = "MPRIS Idle Inhibitor"; 448 449 service = { 450 ExecStart = ''${ 451 lib.getExe inputs.wayland-mpris-idle-inhibit.packages.${pkgs.system}.default 452 } --ignore "kdeconnect" --ignore "playerctld"''; 453 Restart = "on-failure"; 454 RestartSec = "10"; 455 }; 456 }; 457 }; 458 459 qt = { 460 enable = true; 461 platformTheme.name = "kvantum"; 462 style.name = "kvantum"; 463 }; 464 465 home.pointerCursor = { 466 inherit (cursorTheme) name package size; 467 enable = true; 468 gtk.enable = true; 469 x11.enable = true; 470 }; 471 472 gtk = { 473 enable = true; 474 iconTheme = lib.mkForce iconTheme; 475 gtk2.extraConfig = "gtk-application-prefer-dark-theme=true"; 476 gtk3.extraConfig.gtk-application-prefer-dark-theme = true; 477 gtk4.extraConfig.gtk-application-prefer-dark-theme = true; 478 }; 479 480 dconf.settings."org/gnome/desktop/interface".color-scheme = "prefer-dark"; 481 482 services = { 483 swaync = { 484 enable = true; 485 settings = { 486 control-center-exclusive-zone = false; 487 control-center-height = 1000; 488 control-center-margin-bottom = 10; 489 control-center-margin-left = 10; 490 control-center-margin-right = 10; 491 control-center-margin-top = 0; 492 control-center-width = 800; 493 fit-to-screen = false; 494 hide-on-action = true; 495 hide-on-clear = false; 496 image-visibility = "when-available"; 497 keyboard-shortcuts = true; 498 notification-body-image-height = 100; 499 notification-body-image-width = 200; 500 notification-icon-size = 64; 501 notification-window-width = 500; 502 positionX = "center"; 503 positionY = "top"; 504 script-fail-notify = true; 505 scripts = { 506 all = { 507 exec = "${pkgs.nushell}/bin/nu ${../res/notification.nu} ${../res/notif-sounds}"; 508 urgency = ".*"; 509 }; 510 }; 511 timeout = 10; 512 timeout-critical = 0; 513 timeout-low = 5; 514 transition-time = 200; 515 widget-config = { 516 dnd = { 517 text = "Do Not Disturb"; 518 }; 519 label = { 520 max-lines = 1; 521 text = "Notification Center"; 522 }; 523 title = { 524 button-text = "Clear All"; 525 clear-all-button = true; 526 text = "Notification Center"; 527 }; 528 }; 529 widgets = [ 530 "title" 531 "dnd" 532 "notifications" 533 ]; 534 }; 535 }; 536 537 swayidle = let 538 lockCmd = args: "pidof hyprlock || ${lib.getExe pkgs.hyprlock} ${args} &"; 539 in 540 lib.mkIf config.cow.gdi.doIdle { 541 enable = true; 542 timeouts = [ 543 { 544 timeout = 120; 545 command = lockCmd "--grace 5"; 546 } 547 { 548 timeout = 240; 549 command = "${pkgs.systemd}/bin/systemctl suspend"; 550 } 551 ]; 552 events = { 553 "before-sleep" = lockCmd "--immediate"; 554 "unlock" = "pkill hyprlock --signal SIGUSR1"; 555 "lock" = lockCmd "--immediate"; 556 }; 557 }; 558 559 cliphist = { 560 enable = true; 561 systemdTargets = lib.mkForce [ 562 config.wayland.systemd.target 563 ]; 564 }; 565 udiskie = { 566 enable = true; 567 automount = false; 568 tray = "never"; 569 }; 570 playerctld.enable = true; 571 wlsunset = { 572 enable = true; 573 sunrise = "6:00"; 574 sunset = "22:00"; 575 }; 576 swayosd = { 577 enable = true; 578 stylePath = pkgs.writeText "swayosd-style.css" '' 579 window#osd { 580 border-radius: 5rem; 581 } 582 583 #container { 584 padding: 5px 10px; 585 } 586 ''; 587 }; 588 }; 589 590 programs = { 591 rofi = { 592 enable = true; 593 package = pkgs.rofi.override { 594 plugins = with pkgs; [ 595 rofi-emoji 596 rofi-power-menu 597 rofi-bluetooth 598 rofi-calc 599 rofi-pulse-select 600 ]; 601 }; 602 theme = let 603 inherit (config.lib.formats.rasi) mkLiteral; 604 in { 605 "@import" = "${config.catppuccin.sources.rofi}/themes/catppuccin-${config.catppuccin.rofi.flavor}.rasi"; 606 "*" = 607 (builtins.mapAttrs (name: value: mkLiteral "@${value}") { 608 "bg0" = "base"; 609 "bg1" = "mantle"; 610 "bg2" = "crust"; 611 "bg3" = config.catppuccin.accent; 612 "fg0" = "subtext1"; 613 "fg1" = "text"; 614 "fg2" = "subtext0"; 615 "fg3" = "overlay0"; 616 "fg4" = "surface0"; 617 }) 618 // { 619 font = mkLiteral ''"Roboto 14"''; 620 background-color = mkLiteral ''transparent''; 621 text-color = mkLiteral ''@fg0''; 622 margin = mkLiteral ''0px''; 623 padding = mkLiteral ''0px''; 624 spacing = mkLiteral ''0px''; 625 }; 626 "window" = { 627 location = mkLiteral ''north''; 628 y-offset = mkLiteral ''calc(50% - 176px)''; 629 width = mkLiteral ''600''; 630 border-radius = mkLiteral ''24px''; 631 background-color = mkLiteral ''@bg0''; 632 }; 633 "mainbox" = { 634 padding = mkLiteral ''12px''; 635 }; 636 "inputbar" = { 637 background-color = mkLiteral ''@bg1''; 638 border-color = mkLiteral ''@bg3''; 639 border = mkLiteral ''2px''; 640 border-radius = mkLiteral ''16px''; 641 padding = mkLiteral ''8px 16px''; 642 spacing = mkLiteral ''8px''; 643 children = mkLiteral ''[ prompt, entry ]''; 644 }; 645 "prompt" = { 646 text-color = mkLiteral ''@fg2''; 647 }; 648 "entry" = { 649 placeholder = mkLiteral ''"Search"''; 650 placeholder-color = mkLiteral ''@fg3''; 651 }; 652 "message" = { 653 margin = mkLiteral ''12px 0 0''; 654 border-radius = mkLiteral ''16px''; 655 border-color = mkLiteral ''@bg2''; 656 background-color = mkLiteral ''@bg2''; 657 }; 658 "textbox" = { 659 padding = mkLiteral ''8px 24px''; 660 }; 661 "listview" = { 662 background-color = mkLiteral ''transparent''; 663 margin = mkLiteral ''12px 0 0''; 664 lines = mkLiteral ''8''; 665 columns = mkLiteral ''2''; 666 fixed-height = mkLiteral ''false''; 667 }; 668 "element" = { 669 padding = mkLiteral ''8px 16px''; 670 spacing = mkLiteral ''8px''; 671 border-radius = mkLiteral ''16px''; 672 }; 673 "element normal active" = { 674 text-color = mkLiteral ''@bg3''; 675 }; 676 "element alternate active" = { 677 text-color = mkLiteral ''@bg3''; 678 }; 679 "element selected normal, element selected active" = { 680 text-color = mkLiteral ''@fg4''; 681 background-color = mkLiteral ''@bg3''; 682 }; 683 "element-icon" = { 684 size = mkLiteral ''1em''; 685 vertical-align = mkLiteral ''0.5''; 686 }; 687 "element-text" = { 688 text-color = mkLiteral ''inherit''; 689 }; 690 }; 691 location = "center"; 692 }; 693 nushell.extraConfig = '' 694 plugin add ${inputs.nu_plugin_dbus.packages.${pkgs.system}.default}/bin/nu_plugin_dbus 695 ''; 696 697 wezterm = { 698 enable = true; 699 extraConfig = '' 700 return { 701 font = wezterm.font("monospace"), 702 font_size = 18.0, 703 color_scheme = "Catppuccin Mocha", 704 enable_tab_bar = false, 705 window_background_opacity = 0.92, 706 default_cursor_style = "SteadyBar", 707 cursor_thickness = 2, 708 keys = { 709 {key="o", mods="CTRL|SHIFT", action="OpenLinkAtMouseCursor"} 710 } 711 } 712 ''; 713 }; 714 }; 715 }; 716}