me like nix
1{
2 pkgs,
3 inputs,
4 config,
5 ...
6}:
7
8{
9 # Import the home-manager modules you want to use
10 imports = [
11 inputs.catppuccin.homeModules.catppuccin
12 inputs.niri.homeModules.niri
13 inputs.zen-browser.homeModules.beta
14 inputs.agenix.homeManagerModules.default
15 ];
16
17 programs.niri = {
18 enable = true;
19 settings = {
20 window-rules = [
21 {
22 geometry-corner-radius = {
23 top-left = 5.0;
24 top-right = 5.0;
25 bottom-left = 5.0;
26 bottom-right = 5.0;
27 };
28 clip-to-geometry = true;
29 draw-border-with-background = false;
30 }
31 ];
32 debug = {
33 honor-xdg-activation-with-invalid-serial = { };
34 };
35 layout = {
36 focus-ring = {
37 width = 2;
38 active.color = "#8caaee";
39 inactive.color = "#414559";
40 };
41 struts = {
42 top = -6;
43 bottom = -6;
44 left = 0;
45 right = 0;
46 };
47 gaps = 8;
48 };
49 gestures = {
50 hot-corners = {
51 enable = false;
52 };
53 };
54 binds = {
55 "Mod+d".action.spawn = "fuzzel";
56 "Mod+e".action.spawn = "bemoji";
57 "Mod+n".action.spawn = "networkmanager_dmenu";
58 "Mod+a".action.spawn = "alacritty";
59 "Mod+h".action = {
60 focus-column-left = { };
61 };
62 "Mod+j".action = {
63 focus-workspace-down = { };
64 };
65 "Mod+k".action = {
66 focus-workspace-up = { };
67 };
68 "Mod+l".action = {
69 focus-column-right = { };
70 };
71 "Mod+Shift+h".action = {
72 move-column-left = { };
73 };
74 "Mod+Shift+j".action = {
75 move-window-down-or-to-workspace-down = { };
76 };
77 "Mod+Shift+k".action = {
78 move-window-up-or-to-workspace-up = { };
79 };
80 "Mod+Shift+l".action = {
81 move-column-right = { };
82 };
83 "Mod+Down".action = {
84 move-workspace-down = { };
85 };
86 "Mod+Up".action = {
87 move-workspace-up = { };
88 };
89 "Mod+p".action = {
90 show-hotkey-overlay = { };
91 };
92 "Mod+o".action = {
93 toggle-overview = { };
94 };
95 "Mod+q".action = {
96 close-window = { };
97 };
98 "Mod+f".action = {
99 toggle-window-floating = { };
100 };
101 "Mod+Shift+f".action = {
102 switch-focus-between-floating-and-tiling = { };
103 };
104 "Mod+m".action = {
105 fullscreen-window = { };
106 };
107 "Mod+s".action = {
108 screenshot = {
109 show-pointer = true;
110 };
111 };
112 "Mod+1".action = {
113 set-column-width = "100%";
114 };
115 "Mod+2".action = {
116 set-column-width = "50%";
117 };
118 "Mod+Minus".action = {
119 set-column-width = "-10%";
120 };
121 "Mod+Equal".action = {
122 set-column-width = "+10%";
123 };
124 "Mod+Shift+r".action.spawn = [
125 "systemctl"
126 "--user"
127 "restart"
128 "quickshell.service"
129 ];
130 "XF86MonBrightnessDown".action.spawn = [
131 "brightnessctl"
132 "set"
133 "5%-"
134 ];
135 "XF86MonBrightnessUp".action.spawn = [
136 "brightnessctl"
137 "set"
138 "+5%"
139 ];
140 };
141 outputs = {
142 # External monitor - primary display at position (0, 0)
143 "DP-5" = {
144 scale = 2.0;
145 mode = {
146 width = 5120;
147 height = 2160;
148 refresh = 120.0;
149 };
150 position = {
151 x = 0;
152 y = 0;
153 };
154 };
155 "DP-1" = {
156 scale = 2.0;
157 mode = {
158 width = 5120;
159 height = 2160;
160 refresh = 120.0;
161 };
162 position = {
163 x = 0;
164 y = 0;
165 };
166 };
167 "DP-2" = {
168 scale = 1.0;
169 mode = {
170 width = 5120;
171 height = 2160;
172 refresh = 120.0;
173 };
174 position = {
175 x = 0;
176 y = 0;
177 };
178 };
179 "DP-6" = {
180 scale = 2.0;
181 mode = {
182 width = 5120;
183 height = 2160;
184 refresh = 120.0;
185 };
186 position = {
187 x = 0;
188 y = 0;
189 };
190 };
191 "DP-7" = {
192 scale = 2.0;
193 mode = {
194 width = 5120;
195 height = 2160;
196 refresh = 120.0;
197 };
198 position = {
199 x = 0;
200 y = 0;
201 };
202 };
203 # Laptop display - secondary display positioned underneath
204 "eDP-1" = {
205 scale = 1.5;
206 mode = {
207 width = 2560;
208 height = 1600;
209 refresh = 165.0;
210 };
211 position = {
212 x = 0;
213 y = 1080; # Position underneath the external monitor (2160 / 2 scale = 1080 logical height)
214 };
215 };
216 };
217 spawn-at-startup = [
218 { command = [ "xwayland-satellite" ]; }
219 { command = [ "swww-daemon" ]; }
220 { command = [ "wl-paste --watch cliphist store" ]; }
221 ];
222 environment = {
223 DISPLAY = ":0";
224 };
225 };
226 };
227
228 # Allow unfree packages
229 nixpkgs.config.allowUnfree = true;
230
231 nixpkgs.config.permittedInsecurePackages = [
232 "libsoup-2.74.3"
233 ];
234
235 # Quickshell status bar
236 xdg.configFile."quickshell/shell.qml".source = ./quickshell/shell.qml;
237
238 systemd.user.services.quickshell = {
239 Unit = {
240 Description = "QuickShell status bar";
241 After = [ "graphical-session.target" ];
242 PartOf = [ "graphical-session.target" ];
243 };
244 Service = {
245 ExecStart = "${pkgs.quickshell}/bin/quickshell";
246 Restart = "on-failure";
247 RestartSec = 2;
248 };
249 Install = {
250 WantedBy = [ "graphical-session.target" ];
251 };
252 };
253
254 systemd.user.services.quickshell-reload = {
255 Unit = {
256 Description = "Reload QuickShell on wake or display change";
257 After = [
258 "quickshell.service"
259 "graphical-session.target"
260 ];
261 PartOf = [ "graphical-session.target" ];
262 };
263 Service = {
264 Type = "simple";
265 ExecStart = "${pkgs.writeShellScript "quickshell-reload" ''
266 LOCKFILE="/tmp/quickshell-reload.lock"
267
268 do_restart() {
269 (
270 ${pkgs.util-linux}/bin/flock -xn 200 || exit 0
271 sleep 2
272 ${pkgs.systemd}/bin/systemctl --user restart quickshell.service
273 sleep 3
274 ) 200>"$LOCKFILE"
275 }
276
277 # Sleep/wake monitor
278 ${pkgs.dbus}/bin/dbus-monitor --system \
279 "type='signal',interface='org.freedesktop.login1.Manager',member='PrepareForSleep'" 2>/dev/null | \
280 while IFS= read -r line; do
281 if [[ "$line" == *"boolean false"* ]]; then
282 do_restart
283 fi
284 done &
285
286 # Display hotplug monitor
287 ${pkgs.systemd}/bin/udevadm monitor --property --subsystem-match=drm 2>/dev/null | \
288 while IFS= read -r line; do
289 if [[ "$line" == *"HOTPLUG=1"* ]]; then
290 do_restart
291 fi
292 done &
293
294 wait
295 ''}";
296 Restart = "on-failure";
297 RestartSec = 5;
298 };
299 Install = {
300 WantedBy = [ "graphical-session.target" ];
301 };
302 };
303
304 programs.ssh = {
305 enable = true;
306 enableDefaultConfig = false;
307 matchBlocks = {
308 "*" = {
309 identityFile = [ "${config.home.homeDirectory}/.ssh/id_ed25519_sk" ];
310 extraOptions = {
311 IdentityAgent = "${config.home.homeDirectory}/.1password/agent.sock";
312 };
313 };
314 };
315 };
316
317 programs.awscli = {
318 enable = true;
319 settings = {
320 "default" = {
321 region = "us-east-1";
322 };
323 };
324 };
325
326 # All your user-specific packages
327 home.packages = with pkgs; [
328 helix
329 git
330 jujutsu # jj-cli
331 htop
332 zellij # terminal multiplexer
333 alacritty
334 fuzzel # Application launcher
335 bemoji # emoji picker
336 networkmanager_dmenu # network picker for fuzzel
337 quickshell # Status bar (QML-based)
338 swww # For setting wallpapers
339 cliphist # Clipboard history manager
340 pavucontrol # GUI for PulseAudio/PipeWire volume control
341 fd
342 ripgrep
343 yazi # tui file browser
344 gh # github cli
345 signal-desktop
346 xwayland-satellite # for running x11 apps
347 nixfmt # nix formatter
348 nil # nix language server
349 atac # postman-like TUI
350 trippy # network analyzer
351 rsync # file sync utility
352 udiskie # for mounting external drives
353 darktable # photo editing
354 zoxide
355 chromium
356 claude-code
357 nautilus # file browser
358 sqlitebrowser
359 gnome-characters # symbol picker
360 sendme # file transfer
361 desktop-file-utils # for managing .desktop files
362 flyctl # fly.io cli
363 vscode-json-languageserver
364 gnome-network-displays
365 doppler # secret management
366 rainfrog # db tui
367 loupe # image viewer
368 glycin-loaders # various format loaders for loupe
369 docker-compose
370 discord
371 mangohud
372 prismlauncher # minecraft launcher
373 fastfetch
374 inputs.agenix.packages.${pkgs.system}.default # agenix CLI
375 age-plugin-yubikey # Yubikey support for agenix
376
377 # --- FONTS ARE IMPORTANT ---
378 # Berkeley Mono is the main system font, keeping JetBrains and Font Awesome for icons
379 font-awesome
380 noto-fonts
381 noto-fonts-cjk-sans
382 noto-fonts-color-emoji
383 nerd-fonts.jetbrains-mono
384 nerd-fonts.symbols-only
385 # --- POLKIT AGENT (for 1Password GUI, etc.) ---
386 lxqt.lxqt-policykit # Lightweight polkit agent
387 ];
388
389 services.udiskie = {
390 enable = true;
391 tray = "auto";
392 automount = true;
393 };
394
395 services.mako = {
396 enable = true;
397 settings = {
398 border-radius = 8;
399 border-size = 2;
400 padding = "12";
401 margin = "12";
402 font = "BerkeleyMono Nerd Font 11";
403 on-button-left = "invoke-default-action";
404 on-button-right = "dismiss";
405 };
406 };
407
408 catppuccin = {
409 enable = true;
410 flavor = "frappe";
411 };
412
413 programs.fuzzel.enable = true;
414
415 programs.direnv.enable = true;
416
417 programs.atuin = {
418 enable = true;
419 enableFishIntegration = true;
420 settings = {
421 filter_mode_shell_up_key_binding = "session";
422 };
423 };
424
425 programs.zellij = {
426 enable = true;
427 settings = {
428 keybinds = {
429 unbind = [
430 "Ctrl q"
431 "Ctrl o"
432 ];
433 normal = {
434 "bind \"Ctrl m\"" = {
435 SwitchToMode = "Session";
436 };
437 };
438 };
439 pane_frames = false;
440 show_startup_tips = false;
441 ui = {
442 pane_frames.hide_session_name = true;
443 };
444 };
445 };
446
447 programs.zen-browser.enable = true;
448 # programs.swww.enable = true;
449 programs.zoxide = {
450 enable = true;
451 enableFishIntegration = true;
452 };
453
454 programs.obs-studio = {
455 enable = true;
456 plugins = with pkgs.obs-studio-plugins; [
457 obs-backgroundremoval
458 ];
459 };
460
461 # Program configurations
462 programs.git = {
463 enable = true;
464 settings = {
465 user = {
466 name = "seanaye";
467 email = "hello@seanaye.ca";
468 };
469 init.defaultBranch = "main";
470 commit.gpgSign = true;
471 gpg.format = "ssh";
472 user.signingKey = "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIOIgEteUEW06dnBHe2z8vNLwz2iMKe8bba6JgMmOUpcBAAAABHNzaDo= sean@framework16";
473 gpg.ssh.allowedSignersFile = "${config.home.homeDirectory}/.ssh/allowed_signers";
474 };
475 };
476 programs.jujutsu = {
477 enable = true;
478 settings = {
479 user = {
480 email = "hello@seanaye.ca";
481 name = "Sean Aye";
482 };
483 signing = {
484 sign-all = true;
485 behavior = "own";
486 backend = "ssh";
487 key = "${config.home.homeDirectory}/.ssh/id_ed25519_sk_rk";
488 backends.ssh.allowed-signers = "${config.home.homeDirectory}/.ssh/allowed_signers";
489 };
490 };
491 };
492
493 programs.home-manager.enable = true;
494
495 programs.fish = {
496 enable = true;
497 shellAliases = {
498 agenix = "agenix -i ~/.config/agenix/yubikey-identity.txt";
499 };
500 interactiveShellInit = ''
501 set fish_greeting
502 # Set 1Password SSH agent socket
503 set -gx SSH_AUTH_SOCK ${config.home.homeDirectory}/.1password/agent.sock
504 # Load 1Password CLI plugins
505 if test -f ~/.config/op/plugins.sh
506 source ~/.config/op/plugins.sh
507 end
508 # Show fastfetch when inside zellij
509 if set -q ZELLIJ
510 fastfetch --logo small
511 end
512
513 function y
514 set tmp (mktemp -t "yazi-cwd.XXXXXX")
515 yazi $argv --cwd-file="$tmp"
516 if read -z cwd < "$tmp"; and [ -n "$cwd" ]; and [ "$cwd" != "$PWD" ]
517 builtin cd -- "$cwd"
518 end
519 rm -f -- "$tmp"
520 end
521 '';
522 functions = {
523 s3edit = ''
524 set file (basename $argv[1])
525 set tmpfile /tmp/$file
526 aws s3 cp $argv[1] $tmpfile
527 and $EDITOR $tmpfile
528 and aws s3 cp $tmpfile $argv[1]
529 '';
530 };
531 };
532
533 programs.starship = {
534 enable = true;
535 enableFishIntegration = true;
536 };
537
538 programs.alacritty = {
539 enable = true;
540 settings = {
541 terminal.shell = {
542 program = "zellij";
543 args = [
544 "options"
545 "--on-force-close"
546 "detach"
547 ];
548 };
549 window = {
550 decorations = "none";
551 opacity = 0.9;
552 };
553 font = {
554 normal = {
555 family = "BerkeleyMono Nerd Font";
556 style = "Regular";
557 };
558 size = 12.0;
559 };
560 };
561
562 };
563
564 programs.helix = {
565 enable = true;
566 settings = {
567 editor = {
568 bufferline = "multiple";
569 file-picker = {
570 hidden = false;
571 git-ignore = true;
572 };
573 cursor-shape = {
574 insert = "bar";
575 normal = "block";
576 select = "underline";
577 };
578 line-number = "relative";
579 cursorline = true;
580 auto-format = true;
581 end-of-line-diagnostics = "hint";
582 soft-wrap = {
583 enable = true;
584 };
585 lsp = {
586 display-inlay-hints = true;
587 display-messages = true;
588 display-progress-messages = true;
589 };
590 inline-diagnostics = {
591 cursor-line = "hint";
592 };
593 };
594 keys = {
595 normal = {
596 esc = [
597 "keep_primary_selection"
598 "collapse_selection"
599 ];
600 };
601
602 };
603 };
604 languages = {
605
606 language-server.rust-analyzer = {
607 config = {
608 check = {
609 command = "clippy";
610 };
611 checkOnSave = true;
612 cargo = {
613 allFeatures = true;
614 };
615 };
616 };
617 language-server.deno-lsp = {
618 command = "deno";
619 args = [ "lsp" ];
620 config.deno.enable = true;
621 };
622
623 language = [
624 {
625 name = "html";
626 formatter = {
627 command = "prettier";
628 args = [
629 "--parser"
630 "html"
631 ];
632 };
633 }
634 {
635 name = "nix";
636 auto-format = true;
637 formatter = {
638 command = "${pkgs.nixfmt}/bin/nixfmt";
639 };
640 }
641 {
642 name = "kotlin";
643 auto-format = true;
644 }
645 {
646 name = "rust";
647 auto-format = true;
648 formatter = {
649 command = "rustfmt";
650 args = [
651 "--edition"
652 "2024"
653 ];
654 };
655 indent = {
656 tab-width = 4;
657 unit = "t";
658 };
659 }
660 {
661 name = "astro";
662 auto-format = true;
663 formatter = {
664 command = "npx";
665 args = [
666 "prettier"
667 "--plugin"
668 "prettier-plugin-astro"
669 "--parser"
670 "astro"
671 ];
672 };
673 }
674 {
675 name = "json";
676 auto-format = true;
677 }
678 {
679 name = "just";
680 auto-format = true;
681 formatter = {
682 command = "just";
683 args = [
684 "--justfile"
685 "/dev/stdin"
686 "--dump"
687 ];
688 };
689 }
690 {
691 name = "toml";
692 auto-format = true;
693 formatter = {
694 command = "taplo";
695 args = [
696 "format"
697 "-"
698 ];
699 };
700 }
701 # {
702 # name = "typescript";
703 # roots = [
704 # "deno.json"
705 # "deno.jsonc"
706 # ];
707 # file-types = [
708 # "ts"
709 # "tsx"
710 # ];
711 # auto-format = true;
712 # language-servers = [ "deno-lsp" ];
713 # }
714 ];
715 };
716 };
717
718 dconf.settings = {
719 "org/gnome/desktop/interface" = {
720 color-scheme = "prefer-dark";
721 enable-hot-corners = false;
722 };
723 };
724
725 # Font rendering configuration
726 fonts.fontconfig = {
727 enable = true;
728 defaultFonts = {
729 monospace = [ "BerkeleyMono Nerd Font" ];
730 sansSerif = [ "Noto Sans" ];
731 serif = [ "Noto Serif" ];
732 };
733 };
734
735 # Cursor configuration
736 home.pointerCursor = {
737 name = "Adwaita";
738 package = pkgs.adwaita-icon-theme;
739 size = 16;
740 x11.enable = true;
741 gtk.enable = true;
742 };
743
744 # Session variables
745 home.sessionVariables = {
746 EDITOR = "hx";
747 VISUAL = "hx";
748 SUDO_EDITOR = "hx";
749 SSH_AUTH_SOCK = "${config.home.homeDirectory}/.1password/agent.sock";
750 };
751
752 # SSH allowed signers for commit signature verification
753 home.file.".ssh/allowed_signers".text = ''
754 hello@seanaye.ca ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCIqgZ7kedxo+mOW7YG73Vp3zel3h180y3GKvHtRsXfGlpIIvRDy7pgCBQ4AGXYD4y78URQmFohYSAPqCPOPaWcU2un3XG9KvCzEsHmsbskPonitUmCiKvrKkb6oW4jCBtd7AEtBn+AiajAQFtPZ7NN2Df3AmTypvR6Irg7R+nxnfc9NTIHmGvxSDyWcbb4pguL20sctUSqGL6xGh8q/bqhdOThSimM+z9bEUNxK/5rPhwkNniMrp4pJcUrUiAh5/4DiRFG6KT+oeg+/myoz/Z1sPvAs7u/8JDQI4RshRD8Hu0oTkRBN6Hxj478q2SXbeBUZlD6IdjP3RhGpmSecoDdtWqKbpuV3eVRtQtba3KL86GBeV/bugaOdJ1Aud+1SOFJreAAuvxzMMKT+cdQZk6oOPP148DA/No+mDm/2S43lcdCXh79wA6YRAmKQ8jmZxTCtPutrvuZK1rguvvUlEoG/vhdNHh7eDa4Td07V6bjCRPUl8qk/e4M0E3pwsTlZc=
755 hello@seanaye.ca sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIOIgEteUEW06dnBHe2z8vNLwz2iMKe8bba6JgMmOUpcBAAAABHNzaDo= sean@framework16
756 '';
757
758 # Yubikey identity for agenix (not secret - just a reference to the hardware key)
759 home.file.".config/agenix/yubikey-identity.txt".text = ''
760 # Serial: 26930059, Slot: 1
761 # Name: agenix
762 # Recipient: age1yubikey1qw64ag5lzvn9ekrflu5ruj4a6ucycscl6ctk39fjzf76jptsay39z442pxv
763 AGE-PLUGIN-YUBIKEY-1304E5QVZZD74FKSP8FMCT
764 '';
765
766 home.file.".config/Yubico/u2f_keys".text = ''
767 sean:6sa1fnimjshdqKgadDlgQXqSXD6qQ7eSOZneMQZNAzO2OVCViQlxAZXHVf8kDOLKQ4uzcrHMj/t3889Sqi3Dyw==,1jm0HwRmNFFRMGu/DsVrwIBZc6HyNDSlvDhwQd73f0f3KWVxHo6PdSu4OUr+7GwOAfASKGakwyyetv73463CQw==,es256,+presence
768 '';
769
770 # Set the state version for Home Manager
771 home.stateVersion = "25.05";
772}