nixos-config

NixOS configs for my systems
Log | Files | Refs

home.nix (34266B)


      1 {
      2   config,
      3   lib,
      4   pkgs,
      5   inputs,
      6   hostname,
      7   ...
      8 }:
      9 
     10 let
     11   colors = import ./colors.nix;
     12 
     13   isync-oauth2 =
     14     with pkgs;
     15     buildEnv {
     16       # takens from:
     17       # https://github.com/wentasah/nix-conf/raw/master/modules/mail.nix
     18       # https://github.com/NixOS/nixpkgs/issues/108480#issuecomment-1115108802
     19       name = "isync";
     20       paths = [ pkgs.isync ];
     21       pathsToLink = [ "/bin" ];
     22       nativeBuildInputs = [ makeWrapper ];
     23       postBuild = ''
     24         wrapProgram "$out/bin/mbsync" \
     25           --prefix SASL_PATH : "${cyrus_sasl}/lib/sasl2:${cyrus-sasl-xoauth2}/lib/sasl2"
     26       '';
     27     };
     28 
     29   uvtools =
     30     with pkgs;
     31     appimageTools.wrapType2 rec {
     32       pname = "UVtools";
     33       version = "5.0.6";
     34       src = fetchurl {
     35         url = "https://github.com/sn4k3/${pname}/releases/download/v${version}/${pname}_linux-x64_v${version}.AppImage";
     36         hash = "sha256-T1E2+7YocWbspRrpWoexfLmm7LZSb4Nkw8WSBD6zTHs";
     37       };
     38       extraPkgs = pkgs: [ pkgs.icu ];
     39     };
     40 
     41   my-texlive =
     42     with pkgs;
     43     texlive.withPackages (
     44       texlivePackages: with texlivePackages; [
     45         scheme-small
     46         latexmk
     47 
     48         algorithm2e
     49         amsmath
     50         babel
     51         biber
     52         biblatex
     53         biblatex-apa
     54         biblatex-ieee
     55         booktabs
     56         caption
     57         capt-of
     58         circuitikz
     59         cite
     60         collection-fontsrecommended
     61         ec
     62         enumitem
     63         esdiff
     64         hyperref
     65         ifoddpage
     66         microtype
     67         pgfplots
     68         preview
     69         pslatex
     70         relsize
     71         siunitx
     72         url
     73         wrapfig
     74         xcolor
     75       ]
     76     );
     77 
     78 in
     79 {
     80   home = {
     81     username = "vin";
     82     stateVersion = "24.05";
     83 
     84     packages = with pkgs; [
     85       # document writing and viewing
     86       my-texlive
     87 
     88       # messaging
     89       profanity
     90       vesktop
     91 
     92       # email
     93       muchsync
     94       oauth2ms
     95 
     96       # hardware
     97       amdgpu_top
     98 
     99       # encryption
    100       age
    101       oath-toolkit
    102       # TODO actually use my yubikey?
    103       #age-plugin-yubikey
    104       #yubikey-agent
    105       #yubikey-manager
    106 
    107       # system utils
    108       file
    109       htop
    110       mlocate
    111       ncdu
    112       nixfmt-rfc-style
    113       ripgrep
    114       unison
    115 
    116       # X and desktop-related
    117       scrot
    118       sxiv
    119       xclip
    120       xwallpaper
    121 
    122       # desktop-related
    123       dmenu
    124       scrot
    125       xclip
    126       xsel
    127 
    128       # 3d printing
    129       prusa-slicer
    130       uvtools
    131     ];
    132 
    133     preferXdgDirectories = true;
    134 
    135     sessionPath = [
    136       "$HOME/bin"
    137       "$HOME/.local/bin"
    138     ];
    139 
    140     sessionVariables = {
    141       EDITOR = "emacsclient -c";
    142       VISUAL = "emacsclient -t";
    143       PAGER = "less";
    144       LESS = "-iR";
    145       LESSHISTFILE = "/dev/null";
    146       BROWSER = "qutebrowser";
    147 
    148       GOPROXY = "direct";
    149 
    150       AGE_KEY = "$HOME/.ssh/id_ed25519";
    151       AGE_PUB = "$AGE_KEY.pub";
    152       KASI_DIR = if hostname == "demiurge" then "/data/kasi" else "/home/vin/.kasi";
    153       KASI_ENCRYPT = "age -R $AGE_PUB";
    154       KASI_DECRYPT = "age -d -i $AGE_KEY";
    155       KASI_EXT = "age";
    156       KASI_CLIPBOARD = "wl-copy -o";
    157 
    158       # w3m is only used for aerc html preview
    159       W3M_DIR = "/var/empty";
    160 
    161       # backblaze on rclone
    162       RCLONE_FAST_LIST = 1;
    163 
    164       CRAWL_DIR = "$HOME/.local/share/crawl";
    165 
    166       MOZ_ACCELERATED = 1;
    167       MOZ_WEBRENDER = 1;
    168 
    169       # broken qt6.9 rendering
    170       QTWEBENGINE_FORCE_USE_GBM = 0;
    171     };
    172 
    173     shellAliases = {
    174       # shortened commands
    175       md = "mkdir -p";
    176       e = "$EDITOR";
    177       p = "$PAGER";
    178       m = "aerc";
    179       gc = "git clone";
    180       cbc = "wl-copy -c; wl-copy -cp";
    181 
    182       # default arguments to commands
    183       ls = "ls -F --color=auto";
    184       rsync = "rsync --no-inc-recursive";
    185       mpv = "mpv --save-position-on-quit";
    186       wget = "wget --no-hsts";
    187       ytdl = "noglob yt-dlp";
    188 
    189       # shortened nix commands
    190       # the `-c $SHELL` is there because it defaults to bash even if
    191       # that's not my user shell
    192       nd = "nix develop -c $SHELL";
    193       nr = "nix run";
    194       ns = "nix search";
    195       nsh = "nix shell";
    196     };
    197   };
    198 
    199   fonts.fontconfig = {
    200     enable = true;
    201     defaultFonts = {
    202       emoji = [ ];
    203       monospace = [ "Comic Code" ];
    204       sansSerif = [ "Cantarell" ];
    205       serif = [ "CMU Serif" ];
    206     };
    207   };
    208 
    209   gtk = {
    210     enable = true;
    211     font.name = "monospace 11";
    212     iconTheme.name = "rose-pine";
    213     iconTheme.package = pkgs.rose-pine-icon-theme;
    214     theme.name = "Everblush";
    215     theme.package = inputs.everblush-gtk.packages.x86_64-linux.default;
    216   };
    217 
    218   dconf.settings = {
    219     "org/virt-manager/virt-manager/connections" = {
    220       autoconnect = [ "qemu:///system" ];
    221       uris = [ "qemu:///system" ];
    222     };
    223   };
    224 
    225   accounts.email = {
    226     maildirBasePath = "/data/mail";
    227 
    228     accounts = {
    229       university = {
    230         address = "vsureshkumar3892@floridapoly.edu";
    231         realName = "Vineet Kumar";
    232         userName = "vsureshkumar3892@floridapoly.edu";
    233         passwordCommand = "oauth2ms";
    234 
    235         imap.host = "outlook.office365.com";
    236         smtp = {
    237           host = "smtp-mail.outlook.com";
    238           port = 587;
    239           tls.useStartTls = true;
    240         };
    241 
    242         mbsync = {
    243           enable = true;
    244           create = "both";
    245           expunge = "both";
    246           remove = "both";
    247           patterns = [
    248             "*"
    249             "!Calendar*"
    250             "!Contacts*"
    251             "!Conversation*"
    252             "!Notes*"
    253             "!Journal*"
    254             "!Tasks*"
    255           ];
    256         };
    257 
    258         msmtp = {
    259           enable = true;
    260           extraConfig.auth = "xoauth2";
    261         };
    262 
    263         notmuch.enable = true;
    264       };
    265 
    266       vineetk = {
    267         address = "me@vineetk.net";
    268         realName = "Vineet Kumar";
    269         userName = "me@vineetk.net";
    270         passwordCommand = "kasi pass email/vineetk";
    271         primary = true;
    272 
    273         imap.host = "mail.vineetk.net";
    274         smtp.host = "mail.vineetk.net";
    275 
    276         mbsync = {
    277           enable = true;
    278           create = "both";
    279           expunge = "both";
    280           remove = "both";
    281           patterns = [ "*" ];
    282         };
    283 
    284         msmtp.enable = true;
    285         notmuch.enable = true;
    286       };
    287 
    288       thirteen = {
    289         address = "me@13f0.net";
    290         realName = "Kar";
    291         userName = "me@13f0.net";
    292         passwordCommand = "kasi pass email/13f0";
    293 
    294         imap.host = "mail.vineetk.net";
    295         smtp.host = "mail.vineetk.net";
    296 
    297         mbsync = {
    298           enable = true;
    299           create = "both";
    300           expunge = "both";
    301           remove = "both";
    302           patterns = [ "*" ];
    303         };
    304 
    305         msmtp.enable = true;
    306         notmuch.enable = true;
    307       };
    308     };
    309   };
    310 
    311   programs = {
    312     home-manager.enable = true;
    313 
    314     emacs = {
    315       enable = true;
    316       extraPackages =
    317         epkgs: with epkgs; [
    318           # profile startup of init.el
    319           esup
    320 
    321           # the theme I use
    322           kaolin-themes
    323 
    324           # fuzzy searching
    325           orderless
    326           # better vertical completion
    327           vertico
    328 
    329           # easy to identify scopes
    330           rainbow-delimiters
    331           # short descriptions of commands
    332           marginalia
    333           # like a collection of autocomplete commands
    334           consult
    335           # saves pressing C-x u a lot (before I found C-/)
    336           undo-tree
    337 
    338           # highlights TODO/BUG/FIXME/etc. keywords
    339           hl-todo
    340           # very good git
    341           magit
    342           magit-todos
    343 
    344           # better latex mode
    345           auctex
    346           # better pdf viewer
    347           pdf-tools
    348 
    349           # I love org-mode
    350           org
    351           org-pdftools
    352           org-present
    353           org-roam
    354 
    355           # rss reader
    356           elfeed
    357           # email reader
    358           notmuch
    359           # better llm frontend
    360           gptel
    361 
    362           # nix stuff
    363           nix-mode
    364           # ocaml formatting on save
    365           ocamlformat
    366           # rust stuff
    367           rust-mode
    368 
    369           # better terminal emulation
    370           eat
    371 
    372           # project tree templates
    373           skeletor
    374         ];
    375     };
    376 
    377     git = {
    378       enable = true;
    379       settings = {
    380         user = {
    381           email = "git@vineetk.net";
    382           name = "vin";
    383           signingkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEvM0xCLziR+ZT/SYE1aZM6dklbw4fEC17TWqbADIZRH kou@demiurge";
    384         };
    385         sendemail = {
    386           suppresscc = "self";
    387           annotate = true;
    388         };
    389         format.signOff = true;
    390         pull.rebase = true;
    391         init.defaultBranch = "master";
    392         http = {
    393           version = "HTTP/1.1";
    394           postBuffer = 524288000;
    395         };
    396         commit.gpgsign = false;
    397         gpg.format = "ssh";
    398         "gpg \"ssh\"".allowedSignersFile = "/home/vin/.ssh/allowed_signers";
    399         credential = {
    400           helper = "kasi";
    401           "https://git.overleaf.com/".username = "git";
    402         };
    403       };
    404       ignores = [
    405         "*~"
    406         ".*~"
    407         "*.swp"
    408       ];
    409     };
    410 
    411     mbsync = {
    412       enable = true;
    413       package = isync-oauth2;
    414     };
    415 
    416     msmtp.enable = true;
    417 
    418     mpv = {
    419       enable = true;
    420       #      package = pkgs.mpv.override {
    421       #        archiveSupport = false;
    422       #        bluraySupport = false;
    423       #        bs2bSupport = false;
    424       #        cacaSupport = false;
    425       #        cmsSupport = false;
    426       #        dvdnavSupport = false;
    427       #        javascriptSupport = false;
    428       #        pipewireSupport = true;
    429       #        pulseSupport = false;
    430       #        rubberbandSupport = false;
    431       #        waylandSupport = false;
    432       #        zimgSupport = false;
    433       #      };
    434 
    435       bindings = {
    436         # I wish there was a more elegant way of setting Anime4K
    437 
    438         "Ctrl+1" =
    439           "no-osd change-list glsl-shaders set '~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl'; show-text 'Anime4K: Mode A (HQ)'";
    440         "Ctrl+2" =
    441           "no-osd change-list glsl-shaders set '~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_Soft_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl'; show-text 'Anime4K: Mode B (HQ)'";
    442         "Ctrl+3" =
    443           "no-osd change-list glsl-shaders set '~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Upscale_Denoise_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl'; show-text 'Anime4K: Mode C (HQ)'";
    444         "Ctrl+4" =
    445           "no-osd change-list glsl-shaders set '~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_Restore_CNN_M.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl'; show-text 'Anime4K: Mode A+A (HQ)'";
    446         "Ctrl+5" =
    447           "no-osd change-list glsl-shaders set '~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_Soft_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Restore_CNN_Soft_M.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl'; show-text 'Anime4K: Mode B+B (HQ)'";
    448         "Ctrl+6" =
    449           "no-osd change-list glsl-shaders set '~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Upscale_Denoise_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Restore_CNN_M.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl'; show-text 'Anime4K: Mode C+A (HQ)'";
    450 
    451         "Ctrl+0" = "no-osd change-list glsl-shaders clr ''; show-text 'GLSL shaders cleared'";
    452       };
    453 
    454       config = {
    455         # sets default shaders to Anime4K Mode A (HQ). laptop's igpu doesn't like it though
    456         glsl-shaders = "~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl";
    457 
    458         ytdl-format = "bestvideo[height<=?1440]+bestaudio/best";
    459       };
    460     };
    461 
    462     notmuch = {
    463       enable = true;
    464       extraConfig.user = {
    465         name = "Vineet Kumar";
    466         primary_email = "me@vineetk.net";
    467         other_email = "vineet@vineetk.net;vsureshkumar3892@floridapoly.edu;kar@13f0.net";
    468       };
    469       hooks = {
    470         postNew = ''
    471 notmuch tag +13f0 -- tag:unread and to:"*@13f0.net"
    472 notmuch tag +vineetk -- tag:unread and to:"*@vineetk.net"
    473 notmuch tag +university -- tag:unread and to:vsureshkumar3892@floridapoly.edu
    474 notmuch tag +openbsd_announce -inbox -- tag:unread and to:announce@openbsd.org
    475 notmuch tag +openbsd_arm -inbox -- tag:unread and to:arm@openbsd.org
    476 notmuch tag +gameoftrees -inbox -- tag:unread and to:gameoftrees@openbsd.org
    477 notmuch tag +openbsd_misc -inbox -- tag:unread and to:misc@openbsd.org
    478 notmuch tag +openbsd_ports -inbox -- tag:unread and to:ports@openbsd.org
    479 notmuch tag +openbsd_tech -inbox -- tag:unread and to:tech@openbsd.org
    480 notmuch tag +opensmtpd_misc -inbox -- tag:unread and to:misc@opensmtpd.org
    481 notmuch tag +oss_security -inbox -- tag:unread and to:oss-security@lists.openwall.com
    482 notmuch tag +tuhs -inbox -- tag:unread and to:tuhs@tuhs.org
    483         '';
    484         #preNew = "mbsync -a";
    485       };
    486     };
    487 
    488     qutebrowser = {
    489       enable = true;
    490 
    491       quickmarks = {
    492         canvas = "https://floridapolytechnic.instructure.com";
    493         cams = "https://cams.floridapoly.org/student/login.asp";
    494         entertrained = "https://entertrained.app/";
    495         burst = "https://www.burst-type.pro/";
    496         colemakcamp = "https://colemakcamp.github.io/";
    497         colony = "https://cmdr-nowski.github.io/scuffed/#DRmd5xEUGQMOIU5nCVEG";
    498       };
    499 
    500       searchEngines = {
    501         DEFAULT = "https://searx.demiurge.epistemia/search?q={}";
    502         ddg = "https://lite.duckduckgo.com/?q={}";
    503         nw = "https://wiki.nixos.org/index.php?search={}";
    504         aw = "https://wiki.archlinux.org/?search={}";
    505         gw = "https://wiki.gentoo.org/?search={}";
    506       };
    507 
    508       settings = {
    509         content.javascript.enabled = false;
    510 
    511         url = {
    512           default_page = "about:blank";
    513           start_pages = [
    514             "about:blank"
    515           ];
    516         };
    517 
    518         colors.webpage = {
    519           preferred_color_scheme = "dark";
    520           darkmode = {
    521             enabled = true;
    522             policy.images = "never";
    523           };
    524         };
    525 
    526         zoom.default = "133%";
    527       };
    528 
    529       extraConfig = ''
    530         # javascript
    531         config.set('content.javascript.enabled', True, 'chrome-devtools://*')
    532         config.set('content.javascript.enabled', True, 'devtools://*')
    533         config.set('content.javascript.enabled', True, 'chrome://*/*')
    534         config.set('content.javascript.enabled', True, 'qute://*/*')
    535 
    536         config.set('content.javascript.enabled', True, 'floridapolytechnic.instructure.com')
    537         config.set('content.javascript.enabled', True, '*.floridapoly.edu')
    538         config.set('content.javascript.enabled', True, 'login.microsoftonline.com')
    539         config.set('content.javascript.enabled', True, 'www.microsoft365.com')
    540         config.set('content.javascript.enabled', True, 'flpoly-my.sharepoint.com')
    541         config.set('content.javascript.enabled', True, 'flpoly.sharepoint.com')
    542         config.set('content.javascript.enabled', True, 'teams.microsoft.com')
    543         config.set('content.javascript.enabled', True, 'ieeexplore.ieee.org')
    544 
    545         config.set('content.javascript.enabled', True, 'gitlab.com')
    546         config.set('content.javascript.enabled', True, 'cloud.binary.ninja')
    547         config.set('content.javascript.enabled', True, 'console.hetzner.cloud')
    548         config.set('content.javascript.enabled', True, '*.hetzner.com')
    549         config.set('content.javascript.enabled', True, '*.digikey.com')
    550         config.set('content.javascript.enabled', True, '*.demiurge.epistemia')
    551 
    552         # Allow locally loaded documents to access remote URLs.
    553         config.set('content.local_content_can_access_remote_urls', True, 'file:///home/vin/.local/share/qutebrowser/userscripts/*')
    554         config.set('content.local_content_can_access_file_urls', False, 'file:///home/vin/.local/share/qutebrowser/userscripts/*')
    555 
    556         # disable dark mode for some sites
    557         config.set('colors.webpage.darkmode.enabled', False, 'flpoly-my.sharepoint.com')
    558         config.set('colors.webpage.darkmode.enabled', False, 'flpoly.sharepoint.com')
    559         config.set('colors.webpage.darkmode.enabled', False, 'teams.microsoft.com')
    560 
    561         config.set('colors.webpage.darkmode.enabled', False, 'cloud.binary.ninja')
    562         config.set('colors.webpage.darkmode.enabled', False, 'git.vineetk.net')
    563 
    564         # prevent accidental exiting
    565         config.unbind("<Ctrl+q>", mode="normal")
    566 
    567         # source theme from nixos
    568         config.source("theme.py")
    569 
    570         # source redirectors
    571         config.source("redirectors.py")
    572       '';
    573     };
    574 
    575     yt-dlp = {
    576       enable = true;
    577       settings = {
    578         format-sort = "res:1440,vcodec:vp9,acodec:opus";
    579         embed-metadata = true;
    580         embed-subs = true;
    581         embed-thumbnail = true;
    582         merge-output-format = "mkv";
    583         sub-langs = "en,-livechat";
    584         no-windows-filenames = true;
    585         sponsorblock-remove = "sponsor,selfpromo,music_offtopic,-filler,-preview";
    586         sponsorblock-mark = "filler,preview,intro,outro";
    587       };
    588     };
    589 
    590     zsh = {
    591       enable = true;
    592       enableCompletion = true;
    593       defaultKeymap = "emacs";
    594       history = {
    595         ignoreDups = true;
    596         ignoreSpace = true;
    597         save = 1000000; # 10k is inconvenient
    598       };
    599       initContent = ''
    600         # auto-correct commands
    601         setopt correctall
    602 
    603         # fix meta/ctrl-backspace
    604         autoload -Uz select-word-style
    605         select-word-style bash
    606       '';
    607       syntaxHighlighting = {
    608         enable = true;
    609         highlighters = [
    610           "main"
    611           "brackets"
    612           "pattern"
    613         ];
    614         patterns = {
    615           "rm -rf *" = "fg=white,bold,bg=red";
    616         };
    617       };
    618     };
    619   };
    620 
    621   services = {
    622     # batsignal
    623     # borgmatic
    624     # dunst?
    625     # glance?
    626     # gpg-agent with ssh?
    627     # mpd and ncmpcpp?
    628     # pass-secret-service?
    629     # plan9port for plumbing?
    630     # recoll?
    631     # remmina?
    632     # screen-locker
    633     # unison
    634 
    635     poweralertd.enable = true;
    636 
    637     emacs = {
    638       enable = true;
    639       client.enable = true;
    640       startWithUserSession = true;
    641     };
    642 
    643     redshift = {
    644       enable = false;
    645       dawnTime = "07:00";
    646       duskTime = "00:00";
    647       temperature.day = 6500;
    648       temperature.night = 4500;
    649     };
    650 
    651     sxhkd = {
    652       enable = true;
    653       keybindings = {
    654         # reload sxhkd
    655         "super + Escape" = "pkill -USR1 -x sxhkd";
    656 
    657         # BSPWM
    658         # quit/restart bspwm
    659         "super + alt + shift + ctrl + {e,r}" = "bspc {quit,wm -r}";
    660         # close/kill window
    661         "super + shift + {q,k}" = "bspc node -{c,k}";
    662         # toggle between tiled and monocle layout
    663         "super + m" = "bspc desktop -l next";
    664         # send newest marked node to newest preselected node
    665         "super + y" = "bspc node newest.marked.local -n newest.!automatic.local";
    666         # swap current and biggest window
    667         "super + g" = "bspc node -s biggest.window";
    668 
    669         # STATE/FLAGS
    670         # set the window state
    671         "super + {t,shift + t,s,f}" = "bspc node -t {tiled,pseudo_tiled,floating,fullscreen}";
    672         # set the node flags
    673         "super + ctrl + {m,x,y,z}" = "bspc node -g {marked,locked,sticky,private}";
    674 
    675         # FOCUS/SWAP
    676         # focus the node in the given direction
    677         "super + {_,shift + }{n,e,o,i}" = "bspc node -{f,s} {west,south,north,east}";
    678         # focus the node for the given path jump
    679         "super + {p,b,comma,period}" = "bspc node -f @{parent,brother,first,second}";
    680         # focus the next/previous window in the current desktop
    681         "super + {_,shift + }c" = "bspc node -f {next,prev}.local.!hidden.window";
    682         # focus the next/previous desktop in the current monitor
    683         "super + bracket{left,right}" = "bspc desktop -f {prev,next}.local";
    684         # focus the last node/desktop
    685         "super + {grave,Tab}" = "bspc {node,desktop} -f last";
    686         # focus or send to the given desktop
    687         "super + {_,shift + }{1-9,0}" = "bspc {desktop -f,node -d} '^{1-9,10}'";
    688 
    689         # PRESELECT
    690         # preselect the direction
    691         "super + ctrl + {n,e,o,i}" = "bspc node -p {west,south,north,east}";
    692         # preselect the ratio
    693         "super + ctrl + {1-9}" = "bspc node -o 0.{1-9}";
    694         # cancel the preselection for the focused node
    695         "super + ctrl + space" = "bspc node -p cancel";
    696         # cancel the preselection for the focused desktop
    697         "super + ctrl + shift + space" = "bspc query -N -d | xargs -I id -n 1 bspc node id -p cancel";
    698 
    699         # MOVE/RESIZE
    700         # expand a window by moving one of its side outward
    701         "super + alt + {n,e,o,i}" = "bspc node -z {left -20 0,bottom 0 20,top 0 -20,right 20 0}";
    702         # contract a window by moving one of its side inward
    703         "super + alt + shift + {n,e,o,i}" = "bspc node -z {right -20 0,top 0 20,bottom 0 -20,left 20 0}";
    704         # move a floating window
    705         "super + {Left,Down,Up,Right}" = "bspc node -v {-20 0,0 20,0 -20,20 0}";
    706 
    707         # program keybinds
    708         "super + Return" = "emacsclient -c";
    709         "super + shift + Return" = "qutebrowser";
    710         "super + p" = "dmenu_run";
    711         "super + shift + b" = "kasimenu";
    712         "super + shift + u" = "kasimenu user";
    713         "super + shift + t" = "kasimenu_totp";
    714       };
    715     };
    716 
    717     syncthing = {
    718       enable = true;
    719       settings = {
    720         devices = {
    721           demiurge = {
    722             addresses = [
    723               "tcp://192.168.1.2:22000"
    724               "tcp://100.64.0.2:22000"
    725             ];
    726             id = "Y2XNSCK-DQYYKWZ-DCKSCH2-QWRHQJV-5RLNTZF-RX2QOKL-RZ7J67Z-HGMK2Q2";
    727           };
    728 
    729           hastur = {
    730             addresses = [
    731               "tcp://192.168.1.3:22000"
    732               "tcp://100.64.0.3:22000"
    733             ];
    734             id = "ZJ55XB6-VJ62N4F-5LTRNV3-JMIFI3G-TKAI2BZ-42QAFNJ-QDMVJ6I-O7KHQQ6";
    735           };
    736 
    737           iphonebob = {
    738             addresses = [
    739               "tcp://192.168.1.4:22000"
    740               "tcp://100.64.0.4:22000"
    741             ];
    742             id = "ZD43FM5-WIZLODC-PR335XA-FQBSEQK-2ZZKWG3-UHTX57B-6VDVDCX-PPNI4AD";
    743           };
    744         };
    745 
    746         folders = {
    747           "bin" = {
    748             devices = [
    749               "demiurge"
    750               "hastur"
    751             ];
    752             path = if hostname == "demiurge" then "/data/bin" else "/home/vin/bin";
    753           };
    754 
    755           "docs" = {
    756             devices = [
    757               "demiurge"
    758               "hastur"
    759             ];
    760             path = if hostname == "demiurge" then "/data/docs" else "/home/vin/docs";
    761           };
    762 
    763           "emacs.d" = {
    764             devices = [
    765               "demiurge"
    766               "hastur"
    767             ];
    768             path = "/home/vin/.emacs.d";
    769           };
    770 
    771           "kasi" = {
    772             devices = [
    773               "demiurge"
    774               "hastur"
    775             ];
    776             path = if hostname == "demiurge" then "/data/kasi" else "/home/vin/.kasi";
    777           };
    778 
    779           "lit" = {
    780             devices = [
    781               "demiurge"
    782               "hastur"
    783             ];
    784             path = if hostname == "demiurge" then "/data_wd/lit" else "/home/vin/lit";
    785           };
    786 
    787           "pics" = {
    788             devices = [
    789               "demiurge"
    790               "hastur"
    791             ];
    792             path = if hostname == "demiurge" then "/data_wd/pics" else "/home/vin/pics";
    793           };
    794 
    795           "iphone" = {
    796             devices = [
    797               "demiurge"
    798               "hastur"
    799               "iphonebob"
    800             ];
    801             path = if hostname == "demiurge" then "/data_wd/iphone" else "/home/vin/iphone";
    802           };
    803         };
    804       };
    805     };
    806   };
    807 
    808   xdg = {
    809     enable = true;
    810 
    811     configFile = {
    812       "qutebrowser/theme.py".text = ''
    813         # Colors.
    814         bg0     = "${colors.current.primary.background}"
    815         bg1     = "${colors.current.normal.black}"
    816         bg2     = "${colors.current.bright.black}"
    817         fg0     = "${colors.current.primary.foreground}"
    818         fg1     = "${colors.current.normal.white}"
    819         red     = "${colors.current.normal.red}"
    820         blue    = "${colors.current.normal.blue}"
    821         green   = "${colors.current.normal.green}"
    822         yellow  = "${colors.current.normal.yellow}"
    823         cyan    = "${colors.current.normal.cyan}"
    824         magenta = "${colors.current.normal.magenta}"
    825 
    826 # --- Applying colors --- #
    827 
    828 # Completion menu.
    829 c.colors.completion.category.bg                 = bg0
    830 c.colors.completion.category.border.top         = bg0
    831 c.colors.completion.category.border.bottom      = bg0
    832 c.colors.completion.category.fg                 = fg0
    833 c.colors.completion.even.bg                     = bg0
    834 c.colors.completion.odd.bg                      = bg0
    835 c.colors.completion.item.selected.bg            = bg1
    836 c.colors.completion.item.selected.border.bottom = bg1
    837 c.colors.completion.item.selected.border.top    = bg1
    838 c.colors.completion.item.selected.fg            = blue
    839 c.colors.completion.item.selected.match.fg      = blue
    840 c.colors.completion.match.fg                    = red
    841 c.colors.completion.odd.bg                      = bg0
    842 c.colors.completion.scrollbar.fg                = fg0
    843 c.colors.completion.scrollbar.bg                = bg0
    844 
    845 # Statusline.
    846 c.colors.statusbar.caret.bg                     = bg0
    847 c.colors.statusbar.caret.fg                     = magenta
    848 c.colors.statusbar.caret.selection.bg           = bg0
    849 c.colors.statusbar.caret.selection.fg           = red
    850 c.colors.statusbar.command.bg                   = bg0
    851 c.colors.statusbar.command.fg                   = fg0
    852 c.colors.statusbar.insert.bg                    = bg0
    853 c.colors.statusbar.insert.fg                    = green
    854 c.colors.statusbar.normal.bg                    = bg0
    855 c.colors.statusbar.normal.fg                    = fg0
    856 c.colors.statusbar.passthrough.bg               = bg0
    857 c.colors.statusbar.passthrough.fg               = blue
    858 c.colors.statusbar.private.bg                   = bg0
    859 c.colors.statusbar.private.fg                   = fg0
    860 c.colors.statusbar.progress.bg                  = green
    861 c.colors.statusbar.url.error.fg                 = red
    862 c.colors.statusbar.url.fg                       = fg0
    863 c.colors.statusbar.url.hover.fg                 = cyan
    864 c.colors.statusbar.url.success.http.fg          = green
    865 c.colors.statusbar.url.success.https.fg         = green
    866 c.colors.statusbar.url.warn.fg                  = yellow
    867 
    868 # Tabline.
    869 c.colors.tabs.bar.bg                            = bg0
    870 c.colors.tabs.even.bg                           = bg0
    871 c.colors.tabs.even.fg                           = fg0
    872 c.colors.tabs.indicator.error                   = red
    873 c.colors.tabs.indicator.start                   = blue
    874 c.colors.tabs.indicator.stop                    = green
    875 c.colors.tabs.indicator.system                  = "rgb"
    876 c.colors.tabs.odd.bg                            = bg0
    877 c.colors.tabs.odd.fg                            = fg0
    878 c.colors.tabs.pinned.even.bg                    = bg1
    879 c.colors.tabs.pinned.even.fg                    = fg0
    880 c.colors.tabs.pinned.odd.bg                     = bg1
    881 c.colors.tabs.pinned.odd.fg                     = fg0
    882 c.colors.tabs.pinned.selected.even.bg           = bg0
    883 c.colors.tabs.pinned.selected.even.fg           = green
    884 c.colors.tabs.pinned.selected.odd.bg            = bg0
    885 c.colors.tabs.pinned.selected.odd.fg            = green
    886 c.colors.tabs.pinned.selected.even.bg           = bg0
    887 c.colors.tabs.pinned.selected.even.fg           = blue
    888 c.colors.tabs.selected.odd.bg                   = bg0
    889 c.colors.tabs.selected.odd.fg                   = fg1
    890 c.colors.tabs.selected.even.bg                  = bg0
    891 c.colors.tabs.selected.even.fg                  = fg1
    892 
    893 # Hints.
    894 c.colors.hints.bg                               = bg1
    895 c.colors.hints.fg                               = fg0
    896 c.colors.hints.match.fg                         = red
    897 c.hints.border                                  = "2px solid " + bg0
    898 
    899 # Messages.
    900 c.colors.messages.error.bg                      = bg0
    901 c.colors.messages.error.border                  = bg0
    902 c.colors.messages.error.fg                      = red
    903 c.colors.messages.info.bg                       = bg0
    904 c.colors.messages.info.border                   = bg0
    905 c.colors.messages.info.fg                       = blue
    906 c.colors.messages.warning.bg                    = bg0
    907 c.colors.messages.warning.border                = bg0
    908 c.colors.messages.warning.fg                    = yellow
    909 
    910 # Download bar.
    911 c.colors.downloads.bar.bg                       = bg0
    912 c.colors.downloads.error.bg                     = bg1
    913 c.colors.downloads.error.fg                     = red
    914 c.colors.downloads.start.bg                     = bg1
    915 c.colors.downloads.start.fg                     = blue
    916 c.colors.downloads.stop.bg                      = bg1
    917 c.colors.downloads.stop.fg                      = green
    918 
    919 # Keyhints.
    920 c.colors.keyhint.bg                             = bg0
    921 c.colors.keyhint.fg                             = fg0
    922 c.colors.keyhint.suffix.fg                      = yellow
    923 
    924 # Default color for about:blank pages (or if the css didn't specify)
    925 #c.colors.webpage.bg                             = bg0
    926 
    927 # Context menu.
    928 c.colors.contextmenu.disabled.bg                = bg0
    929 c.colors.contextmenu.disabled.fg                = fg0
    930 c.colors.contextmenu.menu.bg                    = bg0
    931 c.colors.contextmenu.menu.fg                    = fg0
    932 c.colors.contextmenu.selected.bg                = bg1
    933 c.colors.contextmenu.selected.fg                = blue
    934 
    935 # Prompts.
    936 c.colors.prompts.bg                             = bg0
    937 c.colors.prompts.border                         = "2px solid " + bg0
    938 c.colors.prompts.fg                             = fg0
    939 c.colors.prompts.selected.bg                    = bg1
    940 c.colors.prompts.selected.fg                    = blue
    941       '';
    942 
    943       "qutebrowser/redirectors.py".text = ''
    944         # I don't remember who I took this from. probably in qutebrowser m$hub issues
    945 
    946         import operator, re, typing
    947         from urllib.parse import urljoin
    948 
    949         from qutebrowser.api import interceptor, message
    950         from PyQt6.QtCore import QUrl
    951 
    952         def _pastebin_redir(url: QUrl) -> bool:
    953                 p = url.path().strip('/')
    954                 if p.isalnum():
    955                         url.setPath(urljoin("/raw/", p))
    956                         return True
    957                 return False
    958 
    959         # Any return value other than a literal 'False' means we redirected
    960         REDIRECT_MAP = {
    961                 "new.reddit.com": operator.methodcaller('setHost', 'redlib.demiurge.epistemia'),
    962                 "np.reddit.com": operator.methodcaller('setHost', 'redlib.demiurge.epistemia'),
    963                 "old.reddit.com": operator.methodcaller('setHost', 'redlib.demiurge.epistemia'),
    964                 "reddit.com": operator.methodcaller('setHost', 'redlib.demiurge.epistemia'),
    965                 "www.reddit.com": operator.methodcaller('setHost', 'redlib.demiurge.epistemia'),
    966 
    967             "www.youtube.com": operator.methodcaller('setHost', 'invidious.demiurge.epistemia'),
    968             "youtube.com": operator.methodcaller('setHost', 'invidious.demiurge.epistemia'),
    969             "youtu.be": operator.methodcaller('setHost', 'invidious.demiurge.epistemia'),
    970 
    971             "imgur.com": operator.methodcaller('setHost', 'rimgo.bloat.cat'),
    972             "i.imgur.com": operator.methodcaller('setHost', 'rimgo.bloat.cat'),
    973 
    974                 # Causes an infinite loop if the paste does not exist...
    975                 "pastebin.com": _pastebin_redir,
    976             } # type: typing.Dict[str, typing.Callable[..., typing.Optional[bool]]]
    977 
    978         def int_fn(info: interceptor.Request):
    979                 """Block the given request if necessary."""
    980                 if (info.resource_type != interceptor.ResourceType.main_frame or
    981                                 info.request_url.scheme() in {"data", "blob"}):
    982                         return
    983                 url = info.request_url
    984                 redir = REDIRECT_MAP.get(url.host())
    985                 if redir is not None and redir(url) is not False:
    986                         message.info("Redirecting to " + url.toString())
    987                         info.redirect(url)
    988 
    989         interceptor.register(int_fn)
    990       '';
    991     };
    992 
    993     mimeApps = {
    994       enable = true;
    995       defaultApplications = {
    996         "application/pdf" = [ "zathura.desktop" ];
    997         "application/xhtml+xml" = [
    998           "qutebrowser.desktop"
    999           "librewolf.desktop"
   1000         ];
   1001         "text/html" = [
   1002           "qutebrowser.desktop"
   1003           "librewolf.desktop"
   1004         ];
   1005         "text/xml" = [
   1006           "qutebrowser.desktop"
   1007           "librewolf.desktop"
   1008         ];
   1009       };
   1010     };
   1011 
   1012     userDirs = {
   1013       createDirectories = false;
   1014       desktop = "$HOME/";
   1015       documents = "$HOME/docs";
   1016       download = "$HOME/tmp";
   1017       music = "$HOME/music";
   1018       pictures = "$HOME/pics";
   1019       publicShare = "$HOME/";
   1020       templates = "$HOME/";
   1021       videos = "$HOME/";
   1022     };
   1023   };
   1024 
   1025   xsession = {
   1026     enable = true;
   1027     profileExtra = ''
   1028       export _JAVA_AWT_WM_NONREPARENTING=1
   1029     '';
   1030     windowManager.bspwm = {
   1031       enable = true;
   1032       monitors = {
   1033         DP-4 = [
   1034           "I"
   1035           "II"
   1036           "III"
   1037           "IV"
   1038           "V"
   1039           "VI"
   1040           "VII"
   1041           "VIII"
   1042           "IX"
   1043           "X"
   1044         ];
   1045         eDP-1 = [
   1046           "I"
   1047           "II"
   1048           "III"
   1049           "IV"
   1050           "V"
   1051           "VI"
   1052           "VII"
   1053           "VIII"
   1054           "IX"
   1055           "X"
   1056         ];
   1057       };
   1058       settings = {
   1059         border_width = 2;
   1060         window_gap = 0;
   1061         gapless_monocle = true;
   1062         split_ratio = 0.52;
   1063       };
   1064       startupPrograms = [
   1065         "emacsclient -r"
   1066         "qutebrowser"
   1067         "xwallpaper --zoom ~/pics/wp/elite_milky_way_below2.png"
   1068         "xsetwacom set \"Wacom One by Wacom S Pen stylus\" MapToOutput 2304x1440+560+0"
   1069       ];
   1070     };
   1071   };
   1072 }