diff --git a/common/themes/default.nix b/common/themes/default.nix
index cabf5d7..8b6916c 100644
--- a/common/themes/default.nix
+++ b/common/themes/default.nix
@@ -4,17 +4,40 @@ let
     catppuccin-mocha = {
       image = ./wallpapers/auto/catppuccin-mocha-rain-world.png;
       base16Scheme = "${inputs.catppuccin-base16}/base16/mocha.yaml";
+      polarity = "dark";
     };
+
+    catppuccin-latte = {
+      image = ./wallpapers/auto/catppuccin-latte-city.png;
+      base16Scheme = "${inputs.catppuccin-base16}/base16/latte.yaml";
+      polarity = "light";
+    };
+
     rosepine-dawn = {
       image = ./wallpapers/rosepine_light_field.png;
       base16Scheme = "${inputs.rosepine-base16}/rose-pine-dawn.yaml";
+      polarity = "light";
+    };
+
+    gpt = {
+      monopurple-light = {
+        image = ./wallpapers/auto/catppuccin-latte-city.png;
+        base16Scheme = ./gpt-themes/monopurple-light.yaml;
+        polarity = "light";
+      };
+
+      purplepink-light = {
+        image = ./wallpapers/auto/catppuccin-latte-city.png;
+        base16Scheme = ./gpt-themes/purplepink-light.yaml;
+        polarity = "light";
+      };
     };
   };
 in
 {
   # Select your current theme here!
   imports = [
-    { stylix = themes.catppuccin-mocha; }
+    { stylix = themes.catppuccin-latte; }
   ];
 
   # Requires me to manually turn targets on!
diff --git a/common/themes/gpt-themes/monopurple-light.yaml b/common/themes/gpt-themes/monopurple-light.yaml
new file mode 100644
index 0000000..3540460
--- /dev/null
+++ b/common/themes/gpt-themes/monopurple-light.yaml
@@ -0,0 +1,20 @@
+scheme: Monopurple Light
+
+author: OpenAI
+
+base00: "#ffffff"  # Background
+base01: "#f4e8fd"  # Current Line
+base02: "#e3c9f3"  # Selection
+base03: "#c9addf"  # Comments
+base04: "#9976b3"  # Variables
+base05: "#7a589e"  # Normal Text
+base06: "#563c6d"  # String
+base07: "#3d2647"  # Function Calls
+base08: "#d33682"  # Keywords
+base09: "#cb48b7"  # Constants
+base0A: "#b62da6"  # Operators
+base0B: "#9b00a0"  # Numbers
+base0C: "#7a007f"  # Punctuation
+base0D: "#59005e"  # Tag/Markup
+base0E: "#430046"  # Function Definition
+base0F: "#2e002e"  # Deprecated/Obsolete
diff --git a/common/themes/gpt-themes/purplepink-light.yaml b/common/themes/gpt-themes/purplepink-light.yaml
new file mode 100644
index 0000000..d31f360
--- /dev/null
+++ b/common/themes/gpt-themes/purplepink-light.yaml
@@ -0,0 +1,20 @@
+scheme: Purplepink Light
+
+author: OpenAI
+
+base00: "#f9f7ff"  # Background
+base01: "#f2e9ff"  # Current line
+base02: "#d9c7eb"  # Selection
+base03: "#b8a4d8"  # Comment
+base04: "#6e579f"  # Foreground
+base05: "#564c74"  # Light Foreground
+base06: "#45385b"  # Dark Foreground
+base07: "#331f33"  # Contrast Foreground
+base08: "#ee8ae5"  # Primary Accent (Pink)
+base09: "#d670c9"  # Secondary Accent (Purple)
+base0A: "#9f70c9"  # Tertiary Accent (Deep Purple)
+base0B: "#61d6cf"  # Bright Aqua
+base0C: "#9fd2e5"  # Light Blue
+base0D: "#4a8ee0"  # Bright Blue
+base0E: "#ed6e61"  # Coral
+base0F: "#e58f79"  # Salmon
diff --git a/common/themes/wallpapers/auto/catppuccin-latte-city.png b/common/themes/wallpapers/auto/catppuccin-latte-city.png
new file mode 100644
index 0000000..8b40844
Binary files /dev/null and b/common/themes/wallpapers/auto/catppuccin-latte-city.png differ
diff --git a/dotfiles/neovim/ftplugin/tex.lua b/dotfiles/neovim/ftplugin/tex.lua
index da1a81c..53be39b 100644
--- a/dotfiles/neovim/ftplugin/tex.lua
+++ b/dotfiles/neovim/ftplugin/tex.lua
@@ -9,6 +9,7 @@ vim.opt.conceallevel = 0
 vim.opt.foldexpr = "nvim_treesitter#foldexpr()"
 vim.opt.foldmethod = "expr"
 
+-- {{{ Older functions for calculating things inside vim
 -- vim.keymap.set("n", "<leader>lg", function()
 --   if not pcall(function()
 --     local a = tonumber(vim.fn.input("A: "))
@@ -28,6 +29,7 @@ vim.opt.foldmethod = "expr"
 --     vim.fn.input("Result: " .. require("my.helpers.math.mod").modinverse(num, class))
 --   end) then vim.fn.input("No results exist") end
 -- end, { buffer = true, desc = "Mod inverse calculator" })
+-- }}}
 
 local abbreviations = {
   -- Greek chars
@@ -41,10 +43,14 @@ local abbreviations = {
   { "theta", "\\theta" },
   { "gamma", "\\gamma" },
   { "lam", "\\lambda" },
+  { "lambda", "\\lambda" },
+  { "omega", "\\omega" },
+  { "Omega", "\\Omega" },
   { "nuls", "\\varnothing" }, -- Other fancy symvols
 
   { "tmat", "^T" }, -- Tranpose of a matrix
   { "cmat", "^*" }, -- Conjugate of a matrix
+  { "sneg", "^C" }, -- Set complement
   { "ortco", "^{\\bot}" }, -- Orthogonal complement
   { "sinter", "^{\\circ}" }, -- Interior of a set
 
@@ -87,9 +93,8 @@ local abbreviations = {
   { "abs", "\\abs" }, -- custom abs command
   { "norm", "\\norm" }, -- custom norm command
   { "iprod", "\\iprod" }, -- custom inner product command
-
-  -- words
-  { "rref", "reduced row echalon form" },
+  { "diprod", "\\dprod" }, -- custom self inner product command
+  { "prob", "\\prob" }, -- custom probability function
 }
 
 -- Todo: convert exponents and subscripts
@@ -102,15 +107,16 @@ local abolishAbbreviations = {
   { "tits", "that is to say," },
   { "wpbd", "we will prove the statement in both directions." },
   { "stam{,s}", "statement{}" },
-  { "nb{,h}{,s}", "neighbour{,hood}{}" },
   { "{ww,tt}{m,i}", "{which,this} {means,implies}" },
   { "cex{,s}", "counterexample{}" },
   { "er{t,s,r}", "{transitivity,symmetry,reflexivity}" },
 
-  -- Calculus
+  -- Calculus & analysis
   { "ib{p,s}", "integration by {parts,substitution}" },
+  { "nb{,h}{,s}", "neighbour{,hood}{}" },
 
   -- Linear algebra
+  { "rref", "reduced row echalon form" },
   { "eg{va,ve,p}{,s}", "eigen{value,vector,pair}{}" },
   { "mx{,s}", "matri{x,ces}" },
   { "dete{,s}", "determinant{}" },
@@ -127,7 +133,7 @@ local abolishAbbreviations = {
 
   -- Graph theory
   { "vx{,s}", "vert{ex,ices}" },
-  { "eg{,s}", "edge{}" },
+  { "edg{,s}", "edge{}" },
 
   -- Linear systems
   -- Note: we must add the space inside the {} in order for capitalization to work!
@@ -219,8 +225,12 @@ local abolishAbbreviations = {
   },
 }
 
+
 local expanded = scrap.expand_many(abolishAbbreviations)
 
+-- Last I checked this contained 1166 abbreviations
+-- print(#abbreviations + #expanded)
+
 A.manyLocalAbbr(abbreviations)
 A.manyLocalAbbr(expanded)
 
diff --git a/dotfiles/vscode-snippets/snippets/latex/core.json b/dotfiles/vscode-snippets/snippets/latex/core.json
index 94353da..6d808dc 100644
--- a/dotfiles/vscode-snippets/snippets/latex/core.json
+++ b/dotfiles/vscode-snippets/snippets/latex/core.json
@@ -23,11 +23,21 @@
     "description": "Norm of a vector",
     "body": "\\norm{$1}$0"
   },
+  "Probability function": {
+    "prefix": "prob",
+    "description": "Probability function applied to some set",
+    "body": "\\prob{$1}$0"
+  },
   "Inner product": {
     "prefix": "iprod",
     "description": "Inner product of two vectors",
     "body": "\\iprod{$1}{$2}$0"
   },
+  "Self inner product": {
+    "prefix": "diprod",
+    "description": "Inner product of a vector with itself",
+    "body": "\\dprod{$1}{$1}$0"
+  },
   "Lemma": {
     "prefix": "lemma",
     "description": "Create a lemma",
diff --git a/home/features/desktop/discord/default.nix b/home/features/desktop/discord/default.nix
index 7c02d44..e7f75e2 100644
--- a/home/features/desktop/discord/default.nix
+++ b/home/features/desktop/discord/default.nix
@@ -13,7 +13,8 @@ in
   home.packages = [ pkgs.discocss ];
 
   xdg.configFile."discocss/custom.css".source =
-    themeMap.${config.lib.stylix.scheme.scheme} or themeMap.default;
+    themeMap.${config.lib.stylix.scheme.scheme}
+      or themeMap.default.${config.stylix.polarity};
 
   satellite.persistence.at.state.apps.Discord.directories = [
     "${config.xdg.configHome}/discord" # Why tf does discord store it's state here 💀
diff --git a/home/features/desktop/discord/themes.nix b/home/features/desktop/discord/themes.nix
index d6f2284..96ae1e5 100644
--- a/home/features/desktop/discord/themes.nix
+++ b/home/features/desktop/discord/themes.nix
@@ -16,5 +16,6 @@ lib.fix (self: {
     url = "https://catppuccin.github.io/discord/dist/catppuccin-macchiato.theme.css";
     sha256 = "1wnphnzgv90r5zgxrr5w36pm1wa5qmkyb72gylj4j1wrk3h7vfvc";
   };
-  default = self."Catppuccin Macchiato";
+  default.dark = self."Catppuccin Macchiato";
+  default.light = self."Catppuccin Latte";
 })
diff --git a/home/features/desktop/spotify.nix b/home/features/desktop/spotify.nix
index a4e1dff..3712e8b 100644
--- a/home/features/desktop/spotify.nix
+++ b/home/features/desktop/spotify.nix
@@ -2,18 +2,23 @@
 let
   spicePkgs = inputs.spicetify-nix.packages.${pkgs.system}.default;
   themeMap = {
+    # TODO: add rosepine themes here
     "Catppuccin Mocha" = spicePkgs.themes.catppuccin-mocha;
     "Catppuccin Latte" = spicePkgs.themes.catppuccin-latte;
     "Catppuccin Frappe" = spicePkgs.themes.catppuccin-frappe;
     "Catppuccin Macchiato" = spicePkgs.themes.catppuccin-macchiato;
-    # TODO: add rosepine themes here
-    default = spicePkgs.themes.catppuccin-mocha;
+
+    default.light = spicePkgs.themes.catppuccin-mocha;
+    default.dark = spicePkgs.themes.catppuccin-latte;
   };
 in
 {
   programs.spicetify = {
     enable = true;
-    theme = themeMap.${config.lib.stylix.scheme.scheme} or themeMap.default;
+
+    theme = themeMap.${config.lib.stylix.scheme.scheme}
+      or themeMap.default.${config.stylix.polarity};
+
     enabledExtensions = with spicePkgs.extensions; [
       fullAppDisplayMod
       shuffle # Working shuffle
diff --git a/home/features/wayland/hyprland/default.nix b/home/features/wayland/hyprland/default.nix
index ba5c75b..b154015 100644
--- a/home/features/wayland/hyprland/default.nix
+++ b/home/features/wayland/hyprland/default.nix
@@ -23,12 +23,13 @@ in
 
   services.hyprpaper = {
     enable = true;
+    systemdTarget = "hyprland-session.target";
+
     preload = [ config.stylix.image ];
-    wallpapers = [
-      { image = config.stylix.image; }
-    ] ++ lib.forEach enabledMonitors ({ name, ... }: {
-      monitor = name;
-      image = config.stylix.image;
-    });
+    wallpapers = [{ inherit (config.stylix) image; }] ++
+      lib.forEach enabledMonitors ({ name, ... }: {
+        monitor = name;
+        image = config.stylix.image;
+      });
   };
 }
diff --git a/hosts/nixos/common/global/wireless/default.nix b/hosts/nixos/common/global/wireless/default.nix
index e7e9140..7bceb23 100644
--- a/hosts/nixos/common/global/wireless/default.nix
+++ b/hosts/nixos/common/global/wireless/default.nix
@@ -12,6 +12,7 @@
     networks = {
       "Neptune".psk = "@PHONE_HOTSPOT_PASS@";
       "Familia-Matei".psk = "@TG_HOME_PASS@";
+      "Familia-Matei-PRO".psk = "@TG_HOME_PASS@";
       "Sailhorse".psk = "@NL_SAILHORSE_PASS@";
 
       "FOSDEM-Dualstack" = { };
@@ -33,11 +34,11 @@
     };
 
     # Imperative
-    # allowAuxiliaryImperativeNetworks = true;
-    # userControlled = {
-    #   enable = true;
-    #   group = "network";
-    # };
+    allowAuxiliaryImperativeNetworks = true;
+    userControlled = {
+      enable = true;
+      group = "network";
+    };
   };
 
   # Ensure group exists
diff --git a/hosts/nixos/common/users/adrielus.nix b/hosts/nixos/common/users/adrielus.nix
index 29c2f6a..53378f4 100644
--- a/hosts/nixos/common/users/adrielus.nix
+++ b/hosts/nixos/common/users/adrielus.nix
@@ -24,7 +24,7 @@
         "lp" # Printers
         "audio" # Audio devices
         "video" # Webcam and the like
-        "network" # for wireless stuff (???)
+        "network" # wpa_supplicant
       ];
 
       openssh.authorizedKeys.keyFiles =
diff --git a/modules/home-manager/hyprpaper.nix b/modules/home-manager/hyprpaper.nix
index 55c89c9..3c35d62 100644
--- a/modules/home-manager/hyprpaper.nix
+++ b/modules/home-manager/hyprpaper.nix
@@ -25,9 +25,9 @@ in
       '';
     };
 
+    # TODO: what should the default value be for this?
     systemdTarget = mkOption {
       type = types.str;
-      default = "graphical-session.target";
       description = ''
         Systemd target to bind to.
       '';
@@ -90,8 +90,8 @@ in
 
     systemd.user.services.hyprpaper = {
       Unit = {
-        Description = "Wayland wallpaper service";
-        PartOf = [ "graphical-session.target" ];
+        Description = "Hyprland wallpaper daemon";
+        Requires = [ "graphical-session.target" ];
       };
 
       Service = {
diff --git a/overlays/default.nix b/overlays/default.nix
index 5098e85..1384144 100644
--- a/overlays/default.nix
+++ b/overlays/default.nix
@@ -23,6 +23,17 @@
     #   };
     # });
     # }}}
+    # {{{ Discordchatexporter
+    discordchatexporter-cli = prev.discordchatexporter-cli.overrideAttrs (_: rec {
+      version = "unstable-2023-06-21";
+      src = prev.fetchFromGitHub {
+        owner = "tyrrrz";
+        repo = "discordchatexporter";
+        rev = "bd4cfcdaf6abe0bd8863d5a4b3f2df2da838aea4";
+        sha256 = "05j6y033852nm0fxhyv4mr4hnqc87nnkk85bw6sgf9gryjpxdcrq";
+      };
+    });
+    # }}}
   };
 
   # Wayland version of plover
diff --git a/pkgs/discordchatexporter-cli/default.nix b/pkgs/discordchatexporter-cli/default.nix
new file mode 100644
index 0000000..33b072d
--- /dev/null
+++ b/pkgs/discordchatexporter-cli/default.nix
@@ -0,0 +1,43 @@
+{ lib
+, buildDotnetModule
+, fetchFromGitHub
+, dotnetCorePackages
+, testers
+, discordchatexporter-cli
+}:
+
+buildDotnetModule rec {
+  pname = "discordchatexporter-cli";
+  version = "2.36.1";
+
+  src = fetchFromGitHub {
+    owner = "tyrrrz";
+    repo = "discordchatexporter";
+    rev = version;
+    sha256 = "svBVXny8ZsZnXG5cDPDKlR2dNhPzPOW4VGaOZkLrRNA=";
+  };
+
+  projectFile = "DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj";
+  nugetDeps = ./deps.nix;
+
+  postFixup = ''
+    ln -s $out/bin/DiscordChatExporter.Cli $out/bin/discordchatexporter-cli
+  '';
+
+  passthru = {
+    updateScript = ./updater.sh;
+    tests.version = testers.testVersion {
+      package = discordchatexporter-cli;
+      version = "v${version}";
+    };
+  };
+
+  meta = with lib; {
+    description = "A tool to export Discord chat logs to a file";
+    homepage = "https://github.com/Tyrrrz/DiscordChatExporter";
+    license = licenses.gpl3Plus;
+    changelog = "https://github.com/Tyrrrz/DiscordChatExporter/blob/${version}/Changelog.md";
+    maintainers = [ maintainers.ivar ];
+    platforms = [ "x86_64-linux" ];
+  };
+}
diff --git a/pkgs/discordchatexporter-cli/deps.nix b/pkgs/discordchatexporter-cli/deps.nix
new file mode 100644
index 0000000..3eeae7e
--- /dev/null
+++ b/pkgs/discordchatexporter-cli/deps.nix
@@ -0,0 +1,16 @@
+# This file was automatically generated by passthru.fetch-deps.
+# Please dont edit it manually, your changes might get overwritten!
+
+{ fetchNuGet }: [
+  (fetchNuGet { pname = "AdvancedStringBuilder"; version = "0.1.0"; sha256 = "1lpv5sggdxza0bmcqmzf5r4i340f0m7nr5073lac18naj5697q5g"; })
+  (fetchNuGet { pname = "CliFx"; version = "2.3.0"; sha256 = "0dxxd5hm7gnc1lhq7k266nkcl84w0844r3cdxdcksvcc786f43vp"; })
+  (fetchNuGet { pname = "DotnetRuntimeBootstrapper"; version = "2.3.1"; sha256 = "0zsicyizachdam64mjm1brh5a3nzf7j8nalyhwnw26wk3v3rgmc9"; })
+  (fetchNuGet { pname = "Gress"; version = "2.0.1"; sha256 = "00xhyfkrlc38nbl6aymr7zwxc3kj0rxvx5gwk6fkfrvi1pzgq0wc"; })
+  (fetchNuGet { pname = "JsonExtensions"; version = "1.2.0"; sha256 = "0g54hibabbqqfhxjlnxwv1rxagpali5agvnpymp2w3dk8h6q66xy"; })
+  (fetchNuGet { pname = "MiniRazor.CodeGen"; version = "2.2.2"; sha256 = "11mxv1p7ahjzpf3sgacfx6szv1xwwk33vpz1r6wb2nch5dx93vdx"; })
+  (fetchNuGet { pname = "MiniRazor.Runtime"; version = "2.2.2"; sha256 = "1bjnqx06gzc13kpbhyndzfrvwgmxi7j0nbaxm7cmb1g7zq06vzrb"; })
+  (fetchNuGet { pname = "Polly"; version = "7.2.3"; sha256 = "1iws4jd5iqj5nlfp16fg9p5vfqqas1si0cgh8xcj64y433a933cv"; })
+  (fetchNuGet { pname = "Spectre.Console"; version = "0.44.0"; sha256 = "0f4q52rmib0q3vg7ij6z73mnymyas7c7wrm8dfdhrkdzn53zwl6p"; })
+  (fetchNuGet { pname = "Superpower"; version = "3.0.0"; sha256 = "0p6riay4732j1fahc081dzgs9q4z3n2fpxrin4zfpj6q2226dhz4"; })
+  (fetchNuGet { pname = "WebMarkupMin.Core"; version = "2.12.0"; sha256 = "1v4dcrpz2icm73w1pfrcjanx0x4j1khi65pyf1xd712lfpm7gpyd"; })
+]
diff --git a/pkgs/discordchatexporter-cli/update.sh b/pkgs/discordchatexporter-cli/update.sh
new file mode 100755
index 0000000..6628cce
--- /dev/null
+++ b/pkgs/discordchatexporter-cli/update.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env nix-shell
+#!nix-shell -I nixpkgs=./. -i bash -p curl jq common-updater-scripts
+set -eo pipefail
+cd "$(dirname "${BASH_SOURCE[0]}")"
+
+deps_file="$(realpath "./deps.nix")"
+
+new_version="$(curl -s "https://api.github.com/repos/tyrrrz/DiscordChatExporter/releases?per_page=1" | jq -r '.[0].name')"
+old_version="$(sed -nE 's/\s*version = "(.*)".*/\1/p' ./default.nix)"
+if [[ "$new_version" == "$old_version" ]]; then
+  echo "Up to date"
+  exit 0
+fi
+
+cd ../../../..
+update-source-version discordchatexporter-cli "$new_version"
+$(nix-build -A discordchatexporter-cli.fetch-deps --no-out-link) "$deps_file"