Mess #

First I’m going to address an elephant in the room:

Why aren’t you publishing your dotfiles?

They’re too much of a mess with a bunch personal data entwined in the commit history that I don’t feel comfortable publicly throwing onto the internet.

Nix snippets #

It’s no secret that I’ve picked up NixOS a few years ago. To that end, there are a few things that I don’t think I’ve seen other people do that I would like to share here.

home-manager color scheme #

Some time ago, I came across the nix-colors project and came to the conclusion that it didn’t fit my needs. As a result, I put/hacked a function together that extracts the color schemes from pkgs.kitty-themes. I won’t go into the integration details in my personal config as it’s not exactly something I would call ergonomic. However, this is the function I came up with:

Expand to see a rather lengthy nix expression
{ lib, config, pkgs, ... }:
arg:
with lib;
let cfg = config.colorscheme;

    getKittyTheme = with builtins; (theme:
        let matching = filter (x: x.name == theme)
            (fromJSON
              (readFile
                "${pkgs.kitty-themes}/share/kitty-themes/themes.json"));

         in throwIf (length matching == 0)
            "kitty-themes does not contain a theme named ${theme}"
            "${pkgs.kitty-themes}/share/kitty-themes/${(head matching).file}");

    unwrapKittyTheme = with pkgs; path: let
      theme = runCommand "theme.toml" { nativebuildinputs = [ coreutils ]; } ''
          cat '${path}' | sed -E '/^#|^$/d;s/\s+(.*)/ = "\1"/g' > $out
          '';
    in (builtins.fromTOML (builtins.readFile theme));

    defaultTheme = {
      selection_foreground = "";
      selection_background = "";
      foreground = "";
      background = "";
      color0 = "";
      color1 = "";
      color2 = "";
      color3 = "";
      color4 = "";
      color5 = "";
      color6 = "";
      color7 = "";
      color8 = "";
      color9 = "";
      color10 = "";
      color11 = "";
      color12 = "";
      color13 = "";
      color14 = "";
      color15 = "";
      cursor = "";
      cursor_text_color = "";
      url_color = "";
    };

in defaultTheme // unwrapKittyTheme (getKittyTheme arg)

This function takes a name and matches that against the contents of pkgs.kitty-themes. If it fails to find something, it throws an error; If does find something, it returns that file, converts that to something approach TOML syntax, which is then parsed using builtins.fromTOML and merged with a minimum-viable-colorscheme, ensuring all colors are at the least declared as an empty string; I ran into issues here when using this output in combination with Qutebrowser when referring to colors that didn’t exist in certain colorschemes.

Qutebrowser #

I manage my Qutebrowser config using the home-manager module (obviously). In my Qutebrowser config, I have JavaScript disabled by default and use a list of exception for trusted sites. When I first tried to set this up, I bumped into the obvious problem that Nix doesn’t allow you to bind multiple values to a single name. Luckily, the Qutebrowser module has an extraConfig option which takes a string. At first I had a bunch of lines for those exceptions:

config.set('content.javascript.enabled', True, 'https://hoogle.haskell.org/*')
config.set('content.javascript.enabled', True, 'https://search.nixos.org/*')
config.set('content.javascript.enabled', True, 'https://nix.dev/*')
config.set('content.javascript.enabled', True, 'https://voidcruiser.nl/searx/*')

However, I quickly got sick of the inherent inefficiency and illegibility of this approach, so I wrote a few little functions:

...
programs.qutebrowser.extraConfig = let
  enableJS = url: "config.set('content.javascript.enabled', True, '${url}')";
  javaScriptExceptions = [
    "https://hoogle.haskell.org/*"
    "https://search.nixos.org/*"
    "https://nix.dev/*"
    "https://voidcruiser.nl/searx/*"
  ];
  in ''
    ${with builtins; concatStringSep "\n" (map enableJS javaScriptExceptions)}

    # things I haven't managed to abstract properly yet
  '';
...

This takes the contents supplied in javaScriptExceptions and generates a viable config line based on each item by mapping over it with the enableJS function. Since the Qutebrowser config syntax can’t read the nix list syntax, the items of the resulting list and than concatenated using \n or a newline character.

XMonad #

My XMonad configuration has a bunch of weird shit in it, some of which I’m proud of, some of which still has bugs that I keep telling myself I will get to At Some Pointâ„¢. Here are some of the nicer bits.

Volume control using scroll wheel #

I have bound Super + scroll {up,down} bound to {increase,decrease} volume respectively.

...
    , ((modMask, button4), \_ -> safeSpawn "pulsemixer" ["--change-volume","+1"])
    , ((modMask, button5), \_ -> safeSpawn "pulsemixer" ["--change-volume","-1"])
    , ((modMask .|. shiftMask, button4), \_ -> safeSpawn "pulsemixer" ["--change-volume","+5"])
    , ((modMask .|. shiftMask, button5), \_ -> safeSpawn "pulsemixer" ["--change-volume","-5"])
...

The hard part here was coming to the realisation that using a lambda that discards its argument was the best way to handle interactions while satisfying the type signature.

Other than that, these functions are quite straightforward. button4 is ‘scroll up’ while button5 is ‘scroll down’. I don’t recall where I found this, but it was somewhere in the documentation. As a result, when XMonad registers a combination of modMask and either of the scroll directions, it uses pulsemixer to increase or decrease the volume.

Kitty wrappers #

I use the Kitty terminal. Kitty has a huge feature list, of which I feel like I use about 20% at most. One of these features is the ability to override config options using the -o parameter. This allows you to set a different font(size) or colorscheme or what have you.

I wanted to be able to start a BQN repl with the BQN386 font right from my window manager. So I wrote a few functions that take various arguments and supply them to Kitty without excessive syntax.

The simplest one of these is kittyWithOverrides. It can only take a list of -o parameters.

kittyWithOverrides :: [String] -> X ()
kittyWithOverrides = spawn . ("kitty " ++) . (unwords . map ("-o " ++))

Here is how I use this function to create a BQN repl window function:

largeTextBQN shell = [ "font_size=17"
                     , "font_family='BQN386 Unicode'"
                     , "shell='" ++ shell ++ "'"
                     ]

bqn = kittyWithOverrides $ largeTextBQN "bqn"

Note: I use a largeTextBQN function rather than a where binding, because I use the same settings for a few other repls as well.

This string will evaluate to the following at compile time:

kitty -o font_size=17 -o font_family='BQN386 Unicode' -o shell='/nix/store/6xwwmv4ljbfckkyklh512zxy6l9s0wv4-cbqn-standalone-0.4.0/bin/bqn'

…which will then be fed to spawn, thereby executing it. If I were to spend more time on it, I could figure out a way to do this properly using safeSpawn or one of its variants, but as it stands, I’m too lazy to do so.

For a more general interface I wrote a kittyWithParams function:

kittyWithParams :: [(String,String)] -> X ()
kittyWithParams = spawn . ("kitty " ++) . unwords . map unwrapParams
    where unwrapParams (flag,parameter) = case last flag of
                                            '=' -> flag ++ parameter
                                            _ -> flag ++ " " ++ parameter

Comparatively, it is rather messy. Still, the basic functionality is there and I’m quite happy with how it works.