├── .github
├── FUNDING.yml
├── dependabot.yml
└── workflows
│ ├── check-flake.yml
│ ├── github-pages.yml
│ └── update-flake.yml
├── .gitignore
├── README.md
├── conf.d
└── recyclarr.yml
├── docs
├── .envrc
├── .gitignore
├── book.toml
├── default.nix
├── flake.lock
├── flake.nix
└── src
│ ├── README.md
│ ├── SUMMARY.md
│ ├── articles
│ ├── declarative-desktop-env-config-with-nix.md
│ ├── github-neovim-1password-cli.md
│ └── linux-isnt-for-nerds-anymore.md
│ ├── avatar.png
│ ├── custom.css
│ ├── my-dotfiles.md
│ ├── special-characters.md
│ └── ublock-filters.md
├── flake.lock
├── flake.nix
├── home-manager
├── components
│ ├── _1password-shell.nix
│ ├── discord.png
│ ├── espanso.nix
│ ├── fish.nix
│ ├── fzf.nix
│ ├── git.nix
│ ├── gnome
│ │ ├── dconf.nix
│ │ └── default.nix
│ ├── jujutsu.nix
│ ├── nvim.nix
│ ├── recyclarr.nix
│ ├── ssh.nix
│ ├── starship.nix
│ ├── terminal.nix
│ ├── tokyonight_palette.nix
│ ├── vencord.nix
│ └── zellij.nix
├── home.nix
├── server.nix
└── shared.nix
├── hosts
├── darwin
│ ├── default.nix
│ └── settings.nix
├── laptop
│ ├── default.nix
│ └── hardware-configuration.nix
├── pc
│ ├── default.nix
│ └── hardware-configuration.nix
└── server
│ ├── adguard.nix
│ ├── cleanuperr.nix
│ ├── containers.nix
│ ├── default.nix
│ ├── docmost.nix
│ ├── duckdns.nix
│ ├── hardware-configuration.nix
│ ├── homeassistant.nix
│ ├── ip.nix
│ ├── media.nix
│ ├── nas.nix
│ ├── nixosModules
│ └── nginx.nix
│ ├── observability.nix
│ ├── paperless.nix
│ ├── torrent_client.nix
│ ├── vikunja.nix
│ └── wireguard.nix
├── nixos
├── _1password.nix
├── allowed-unfree.nix
├── common.nix
├── desktop_environment.nix
├── nix-conf.nix
├── sshd.nix
└── theme.nix
├── nvim
├── .luacheckrc
├── .luarc.json
├── ftplugin
│ ├── markdown.lua
│ └── nix.lua
├── init.lua
├── lua
│ └── my
│ │ ├── cmd-palette.lua
│ │ ├── configure
│ │ ├── autopairs.lua
│ │ ├── colorizer.lua
│ │ ├── colorscheme.lua
│ │ ├── comments.lua
│ │ ├── completion.lua
│ │ ├── flash.lua
│ │ ├── git.lua
│ │ ├── grug_far.lua
│ │ ├── heirline
│ │ │ ├── conditions.lua
│ │ │ ├── init.lua
│ │ │ ├── separators.lua
│ │ │ ├── shared.lua
│ │ │ ├── statusline.lua
│ │ │ └── winbar.lua
│ │ ├── init.lua
│ │ ├── language-support.lua
│ │ ├── markdown.lua
│ │ ├── mini_bracketed.lua
│ │ ├── mini_files.lua
│ │ ├── mini_move.lua
│ │ ├── neotest.lua
│ │ ├── noice.lua
│ │ ├── op.lua
│ │ ├── rustaceanvim.lua
│ │ ├── smart-splits.lua
│ │ ├── snacks_picker.lua
│ │ ├── sql.lua
│ │ ├── treesitter.lua
│ │ └── which_key.lua
│ │ ├── lsp
│ │ ├── filetypes.lua
│ │ ├── go.lua
│ │ ├── lua.lua
│ │ ├── nix.lua
│ │ ├── snippets.lua
│ │ └── typescript.lua
│ │ ├── plugins.lua
│ │ ├── settings.lua
│ │ └── utils
│ │ ├── clipboard.lua
│ │ ├── editor.lua
│ │ ├── lsp.lua
│ │ └── path.lua
└── stylua.toml
├── packages
├── default.nix
└── vim-zellij-navigator.nix
├── secrets.nix
├── secrets
├── cleanuperr_env.age
├── cloudflare_certbot_token.age
├── docmost_env.age
├── duckdns_token.age
├── gatus_discord_webhook_env.age
├── homarr_env.age
├── mullvad_wireguard.age
├── paperless_admin_pw.age
└── wireguard_server.age
└── ublock-mdbook
├── .envrc
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── default.nix
├── flake.lock
├── flake.nix
└── src
├── main.rs
└── ublock-filters.yml
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: mrjones2014
2 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 |
4 | - package-ecosystem: github-actions
5 | directory: "/"
6 | schedule:
7 | interval: weekly
8 | time: '00:00'
9 | timezone: UTC
10 | open-pull-requests-limit: 10
11 | commit-message:
12 | prefix: "chore"
13 | include: "scope"
14 |
--------------------------------------------------------------------------------
/.github/workflows/check-flake.yml:
--------------------------------------------------------------------------------
1 | name: Check Nix flake
2 | on:
3 | pull_request:
4 | push:
5 | branches:
6 | - master
7 | workflow_dispatch: # allow manual trigger
8 | jobs:
9 | check-flake:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | - uses: cachix/install-nix-action@v31
14 | - uses: cachix/cachix-action@v16
15 | with:
16 | name: mrjones2014-dotfiles
17 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
18 | - name: Run nix flake check
19 | run: nix flake check
20 |
--------------------------------------------------------------------------------
/.github/workflows/github-pages.yml:
--------------------------------------------------------------------------------
1 | name: github pages
2 | on:
3 | push:
4 | branches:
5 | - master
6 | workflow_dispatch: # allow manual trigger
7 | jobs:
8 | deploy:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 | - uses: cachix/install-nix-action@v31
13 | - name: Build mdbook site
14 | run: nix build ./docs/
15 | - name: Deploy
16 | uses: peaceiris/actions-gh-pages@v4
17 | with:
18 | github_token: ${{ secrets.GITHUB_TOKEN }}
19 | publish_branch: gh-pages
20 | publish_dir: ./result/
21 |
--------------------------------------------------------------------------------
/.github/workflows/update-flake.yml:
--------------------------------------------------------------------------------
1 | name: Update flake dependencies
2 |
3 | on:
4 | schedule:
5 | - cron: '0 16 * * 5'
6 | workflow_dispatch: # for allowing manual triggers of the workflow
7 |
8 | jobs:
9 | update-dependencies:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | - uses: cachix/install-nix-action@v31
14 | - uses: cachix/cachix-action@v16
15 | with:
16 | name: mrjones2014-dotfiles
17 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
18 | - name: Add nix-community cache
19 | run: cachix use nix-community
20 | - name: update flake.lock
21 | run: nix flake update
22 | - name: Create Pull Request
23 | uses: peter-evans/create-pull-request@v7
24 | id: create-pr
25 | with:
26 | commit-message: "flake: update dependencies"
27 | title: "[automation] update flake dependencies"
28 | branch: "automation/update-flake-dependencies"
29 | token: ${{ secrets.UPDATE_FLAKE_TOKEN }}
30 | - name: Enable PR auto-merge on CI success
31 | run: GH_TOKEN="${{ secrets.UPDATE_FLAKE_TOKEN }}" gh pr merge --squash --delete-branch --auto ${{ steps.create-pr.outputs.pull-request-number }}
32 | - name: Ping me in a comment on the PR
33 | run: |
34 | PR_NUMBER=${{ steps.create-pr.outputs.pull-request-number }}
35 | COMMENT="Ping @mrjones2014"
36 | GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
37 | COMMENT_URL="https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments"
38 | curl -s -H "Authorization: token ${GITHUB_TOKEN}" -X POST $COMMENT_URL -d "{\"body\":\"$COMMENT\"}"
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .netrwhist
3 | result
4 | # I know I'm supposed to commit this file but I don't really care about it, I've never used it
5 | nvim/lazy-lock.json
6 | book/
7 | # mdbook theme, only used at build time in CI
8 | docs/theme/
9 | ublock-mdbook/.direnv
10 | .direnv
11 |
12 | # recyclarr tmp file created by `op inject`
13 | recyclarr-tmp.yml
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dotfiles
2 |
3 | These are my NixOS and macOS dotfiles, packaged as a Nix flake, using [`home-manager`](https://github.com/nix-community/home-manager).
4 |
5 | For info on how to set up my Nix flake, see the [setup instructions](https://mjones.network/my-dotfiles.html).
6 |
7 | ## Caveats
8 |
9 | I try my best to make things work for both macOS and Linux (NixOS), please let me know if something does not work.
10 |
--------------------------------------------------------------------------------
/docs/.envrc:
--------------------------------------------------------------------------------
1 | use flake
2 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | book
2 | .direnv
3 |
--------------------------------------------------------------------------------
/docs/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | authors = ["Mat Jones"]
3 | language = "en"
4 | multilingual = false
5 | src = "src"
6 | title = "mjones.network"
7 |
8 | [output.html]
9 | default-theme = "Ayu"
10 | preferred-dark-theme = "Ayu"
11 | additional-css = ["./src/custom.css"]
12 | cname = "mjones.network"
13 | site-url = "https://mjones.network"
14 |
15 | [output.html.print]
16 | enable = false
17 |
18 | [preprocessor.ublock-mdbook]
19 | command = "ublock-mdbook"
20 |
--------------------------------------------------------------------------------
/docs/default.nix:
--------------------------------------------------------------------------------
1 | { pkgs, ublock-mdbook, ... }:
2 | let
3 | highlightJsNix = pkgs.fetchFromGitHub {
4 | owner = "mrjones2014";
5 | repo = "highlight-js-nix";
6 | rev = "67aebab";
7 | hash = "sha256-EmeiP7TAdb7n6KOHJ8qhGzSL0TGkoVuQIkijnrAj5RQ=";
8 | };
9 | in
10 | pkgs.stdenv.mkDerivation {
11 | pname = "mdbook-docs-site";
12 | version = "0.1.0";
13 | src = pkgs.lib.cleanSource ./.;
14 | buildInputs = [
15 | pkgs.mdbook
16 | ublock-mdbook
17 | ];
18 | buildPhase = ''
19 | mkdir $out
20 | mdbook build -d $out
21 | rm $out/highlight.js && cp ${highlightJsNix}/highlight.js $out/highlight.js
22 | ublock-mdbook gen-filter-list $out/ublock-filters.txt
23 | '';
24 | }
25 |
--------------------------------------------------------------------------------
/docs/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-utils": {
4 | "inputs": {
5 | "systems": "systems"
6 | },
7 | "locked": {
8 | "lastModified": 1710146030,
9 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
10 | "owner": "numtide",
11 | "repo": "flake-utils",
12 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
13 | "type": "github"
14 | },
15 | "original": {
16 | "owner": "numtide",
17 | "repo": "flake-utils",
18 | "type": "github"
19 | }
20 | },
21 | "nixpkgs": {
22 | "locked": {
23 | "lastModified": 1714906307,
24 | "narHash": "sha256-UlRZtrCnhPFSJlDQE7M0eyhgvuuHBTe1eJ9N9AQlJQ0=",
25 | "owner": "nixos",
26 | "repo": "nixpkgs",
27 | "rev": "25865a40d14b3f9cf19f19b924e2ab4069b09588",
28 | "type": "github"
29 | },
30 | "original": {
31 | "owner": "nixos",
32 | "ref": "nixos-unstable",
33 | "repo": "nixpkgs",
34 | "type": "github"
35 | }
36 | },
37 | "root": {
38 | "inputs": {
39 | "flake-utils": "flake-utils",
40 | "nixpkgs": "nixpkgs"
41 | }
42 | },
43 | "systems": {
44 | "locked": {
45 | "lastModified": 1681028828,
46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
47 | "owner": "nix-systems",
48 | "repo": "default",
49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
50 | "type": "github"
51 | },
52 | "original": {
53 | "owner": "nix-systems",
54 | "repo": "default",
55 | "type": "github"
56 | }
57 | }
58 | },
59 | "root": "root",
60 | "version": 7
61 | }
62 |
--------------------------------------------------------------------------------
/docs/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
4 | flake-utils.url = "github:numtide/flake-utils";
5 | };
6 |
7 | outputs =
8 | { nixpkgs, flake-utils, ... }:
9 | flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-darwin" ] (
10 | system:
11 | let
12 | pkgs = nixpkgs.legacyPackages.${system};
13 | ublock-mdbook = import ../ublock-mdbook { inherit pkgs; };
14 | in
15 | {
16 | packages.default = pkgs.callPackage ./. {
17 | inherit pkgs;
18 | inherit system;
19 | inherit ublock-mdbook;
20 | };
21 | devShells.default = pkgs.mkShell {
22 | buildInputs = [
23 | pkgs.mdbook
24 | ublock-mdbook
25 | ];
26 | };
27 | }
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/docs/src/README.md:
--------------------------------------------------------------------------------
1 | {{#title mjones.network}}
2 |
3 |
4 |
5 |

6 |
7 | # Hi there, I'm Mat 👋
8 |
9 |
10 |
11 |
12 | I’m currently working as a developer at 1Password. I'm a software engineer with a passion for developer experience, user privacy and sovereignty, open source, Rust, and NixOS.
13 |
14 | My favorite programming languages currently are Rust, Nix, and Lua (for Neovim plugins). I like making Neovim plugins, command line tools, and other developer tools.
15 |
16 | I manage my [dotfiles](https://github.com/mrjones2014/dotfiles) as a Nix flake, and maintain [`legendary.nvim`](https://github.com/mrjones2014/legendary.nvim) and
17 | [`smart-splits.nvim`](https://github.com/mrjones2014/smart-splits.nvim) (Neovim plugins), as well as some other side projects.
18 |
19 | - [Email](mailto:mat@mjones.network)
20 | - [GitHub](https://github.com/mrjones2014)
21 | - [Sponsor](https://github.com/sponsors/mrjones2014)
22 |
--------------------------------------------------------------------------------
/docs/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | [README.md](./README.md)
4 | [My Dotfiles](./my-dotfiles.md)
5 | [Useful uBlock Origin Filters](./ublock-filters.md)
6 |
7 | # Articles
8 |
9 | - [Bringing my GitHub workflow into Neovim using 1Password CLI](./articles/github-neovim-1password-cli.md)
10 | - [Declarative Desktop Environment Configuration on NixOS](./articles/declarative-desktop-env-config-with-nix.md)
11 | - [It's 2024 and Linux Isn't Just for Nerds Anymore](./articles/linux-isnt-for-nerds-anymore.md)
12 |
--------------------------------------------------------------------------------
/docs/src/articles/declarative-desktop-env-config-with-nix.md:
--------------------------------------------------------------------------------
1 | # Declarative Configuration for Desktop Environments on NixOS
2 |
3 | As soon as I tried out Nix, I wanted to use Nix for _everything._ With Nix, all your configuration is _reproducible_ (as long as you
4 | stay in pure mode, that is), which is amazing if you need to share configurations across devices.
5 |
6 | For the sake of this article, I'll assume you have a working knowledge of Nix and NixOS.
7 |
8 | ## Getting Started
9 |
10 | Building on top of Nix, [`home-manager`](https://github.com/nix-community/home-manager) is great for managing non-global packages
11 | and dotfiles. It also has a module called `dconf` that allows you to declaratively define [`gsettings`](https://wiki.gnome.org/HowDoI/GSettings), a settings system for GNOME and related desktop environments.
12 |
13 | Using the `dconf` module, you can configure everything from your system dark theme, desktop background image, and global keyboard
14 | shortcuts declaratively using Nix.
15 |
16 | First you need to enable `dconf`; in your `/etc/nixos/configuration.nix` or `flake.nix` add:
17 |
18 | ```nix
19 | { pkgs, ... }: { programs.dconf.enable = true; }
20 | ```
21 |
22 | Then, in your `home-manager` configuration, you can start configuring your desktop environment using `dconf`.
23 |
24 | ```nix
25 | { pkgs, ... }: {
26 | dconf.settings = {
27 | # place your dconf settings here
28 | "some/settings/namespace" = { some-setting = "some-value"; };
29 | };
30 | }
31 | ```
32 |
33 | ## Finding the Correct Settings Namespaces and Keys
34 |
35 | Now that you've enabled `dconf` in your NixOS configuration, we can use `dconf` from the command line to figure out what the
36 | namespaces and keys are for the settings you want to set declaratively in Nix. To do this, open a new terminal window and run
37 | `dconf watch /`. This will start a process which will output any settings value that changes.
38 |
39 | Now, change your desired settings from the settings GUI, and observe the output from `dconf watch /`. For example,
40 | changing your wallpaper image from the settings GUI should output something like:
41 |
42 | ```
43 | /org/gnome/desktop/background/picture-uri
44 | 'file:///home/mat/.local/share/backgrounds/2023-06-15-14-59-58-wallpaper.jpg'
45 | ```
46 |
47 | ## Declarative Settings
48 |
49 | With this information, you can set your desktop wallpaper declaratively within your `home-manager` configuration. You can use
50 | `pkgs.fetchurl` to fetch the desired image file and set it as your desktop wallpaper:
51 |
52 | ```nix
53 | { pkgs, ... }:
54 | let
55 | wallpaperImg = pkgs.fetchurl {
56 | url = "https://url/to/your/image";
57 | # replace this with the SHA256 hash of the image file
58 | hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
59 | };
60 | in {
61 | dconf.settings = {
62 | "org/gnome/desktop/background" = {
63 | picture-uri = "file://${wallpaperImg}";
64 | };
65 | };
66 | }
67 | ```
68 |
69 | With this added, applying your `home-manager` configuration will change your desktop wallpaper!
70 | You can use this method to find just about all the settings you'll need, although some of them might
71 | require some fiddling with.
72 |
73 | For example, creating custom keyboard shortcuts is slightly more complex. First, you have to specify
74 | each custom shortcut group that should exist, then you can specify the shortcuts themselves. For example:
75 |
76 | ```nix
77 | { pkgs, ... }: {
78 | dconf.settings = {
79 | "org/gnome/settings-daemon/plugins/media-keys" = {
80 | # specify that custom shortcut "custom0" should exist and be included
81 | custom-keybindings = [
82 | "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/"
83 | ];
84 | terminal = [ "" ];
85 | };
86 | # now you can specify the "custom0" shortcut itself
87 | "org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0" =
88 | {
89 | binding = "Print"; # printscrn key
90 | command = "flameshot gui";
91 | name = "flameshot";
92 | };
93 | };
94 | }
95 | ```
96 |
97 | Feel free to take a look at all the current settings I use at time of writing [in my dotfiles repo](https://github.com/mrjones2014/dotfiles/tree/e3d1dc94b8cbfd108fa22e9bf58e77dca48bc70c/home-manager/modules/gnome).
98 |
--------------------------------------------------------------------------------
/docs/src/articles/linux-isnt-for-nerds-anymore.md:
--------------------------------------------------------------------------------
1 | # Opinion: It's 2024 and Linux Isn't Just for Nerds Anymore
2 |
3 | Linux was once, and to some extent, still is, considered an operating system meant only for tech-savvy individuals and computer enthusiasts.
4 | But, the truth is, Linux is now more user-friendly and easy to use than ever, and there are plenty of reasons you might want to replace Windows
5 | with an alternative operating system.
6 |
7 | ## The Rise of User-Friendly Linux Distributions
8 |
9 | An incredible amount of effort has gone towards making Linux easy to use for everyone, even beginners who aren't comfortable
10 | using the terminal or command line interfaces. Fedora, Mint, Manjaro, and Pop!\_OS (my personal go-to recommendation) are just a few
11 | of many examples of such distributions. Pop!\_OS comes with the Pop! Shop, a graphical software "store" (nearly all of the software there
12 | is free) from which you can install much of the software that basic users might need, without ever touching the command line.
13 |
14 | It's 2024 and today it's completely reasonable to use Linux without ever touching the terminal or any command line interface.
15 |
16 | ## Gaming on Linux: A Massive Leap Forward
17 |
18 | In just the past few years, Valve's development of [Proton](https://github.com/ValveSoftware/Proton) has made incredible progress
19 | for making gaming on Linux incredibly seamless. Simply [enable Steam Play](https://steamcommunity.com/games/221410/announcements/detail/1696055855739350561)
20 | in the Steam client for Linux, and tens or hundreds of thousands of titles will "just work" on Linux.
21 |
22 | Anecdotally, I've been gaming on Linux exclusively, both on desktop and [Steam Deck](https://store.steampowered.com/steamdeck) (the world's greatest
23 | portable gaming device) for several years now, and I can probably count on one hand the number of games I wanted to play that I couldn't get working
24 | on Linux.
25 |
26 | ## A Friendlier Alternative to Microsoft's Windows
27 |
28 | It seems like almost a weekly occurance now that I see some tech news article about a new place that Microsoft has managed to
29 | shove ads into the Windows interface; the [start menu](https://www.theverge.com/2024/4/24/24138949/microsoft-windows-11-start-menu-ads-recommendations-setting-disable),
30 | [system notifications](https://web.archive.org/web/20231118121625/https://old.reddit.com/r/assholedesign/comments/16opnfo/windows_11_gives_you_ads_as_notifications_in_the/),
31 | even _[full screen popup ads on your desktop!](https://www.extremetech.com/computing/microsoft-displaying-full-screen-windows-11-ads-in-windows-10)_
32 |
33 | With this knowledge, it will probably come as no surprise that Windows is spying on you in order to serve you these targeted ads; by default,
34 | Windows reports any time you launch an app to Microsoft via a "feature" called "Windows Activity History". In fact, there is so much of this
35 | type of spyware in Windows (what Microsoft calls "telemetry") that privacy tools like [NextDNS](https://nextdns.io/), a privacy and security
36 | enhancing DNS server, offer native Windows tracker blocking functionality.
37 |
38 | 
39 |
40 | The Free Software Foundation even has [a page dedicated to all the pieces of malware Microsoft has introduced as "features" to Windows over the years](https://www.gnu.org/proprietary/malware-microsoft.html).
41 |
42 | > "If you do want to clean your computer of malware, the first software to delete is Windows." - _The Free Software Foundation_
43 |
44 | Most Linux distrubutions, by contrast, give the user full freedom and control over their system; it doesn't abuse the user, doesn't spam them
45 | with ads on the desktop of their own computer, and doesn't share your app activity with Microsoft. It's important to note, however,
46 | that not all Linux distributions are created equal.
47 |
48 | For example, I highly recommend _against_ using Ubuntu, as the developer, Canonical,
49 | has lost user trust by behaving in a similar manner to Microsoft; the most infamous incident is a "feature" called
50 | [Amazon Lens](https://www.omgubuntu.co.uk/2012/11/how-to-install-a-dedicated-amazon-shopping-lens-in-ubuntu) that, by default,
51 | sends what you type into the system launcher (basically like Linux's version of the start menu) to Amazon. While I used to recommend
52 | Ubuntu as the best beginner-friendly Linux distribution, over the years I've changed my recommendation to [Pop!\_OS](https://pop.system76.com/).
53 |
54 | ---
55 |
56 | It's clear that as we enter 2024 and beyond, Linux isn't just a niche operating system for nerds anymore.
57 | Today, there are plenty of beginner-friendly, very easy to use graphical Linux distributions to choose from
58 | to take back your freedom and privacy from Microsoft. You don't have to tolerate their abuse anymore; using Linux
59 | is easy, even for beginners.
60 |
61 | It's time to end your abusive relationship with Microsoft.
62 |
--------------------------------------------------------------------------------
/docs/src/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrjones2014/dotfiles/412867a9f6b0e7e40ad5ea5f8d22c3e2102de6b8/docs/src/avatar.png
--------------------------------------------------------------------------------
/docs/src/custom.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --content-max-width: 900px;
3 | }
4 |
--------------------------------------------------------------------------------
/docs/src/my-dotfiles.md:
--------------------------------------------------------------------------------
1 | # Installing on a New System
2 |
3 | ```bash
4 | mkdir -p "$HOME/git"
5 | cd "$HOME/git"
6 | git clone git@github.com:mrjones2014/dotfiles.git
7 | ```
8 |
9 | ## NixOS
10 |
11 | If you're installing on a server, see the [NixOS manual](https://nixos.org/manual/nixos/stable/#ch-installation) for headless NixOS installation instructions first.
12 | Once you've got your disks partitioned and NixOS installed, come back here and continue. For desktop computers, you can just use the graphical GNOME NixOS installer,
13 | since we'll be installing GNOME anyway.
14 |
15 | Simply run `NIX_CONFIG="experimental-features = nix-command flakes" sudo nixos-rebuild switch ~/git/dotfiles/.#pc` (replacing `.#pc` with your desired flake output target).
16 | After the first time, you can just run `sudo nixos-rebuild switch --flake $HOME/git/dotfiles/.#pc` (or the `nix-apply` shell alias).
17 |
18 | ## macOS
19 |
20 | On macOS, for the first install, you'll need to run `nix-darwin` via `nix run`:
21 |
22 | ```bash
23 | nix run nix-darwin/master#darwin-rebuild -- switch --extra-experimental-features "nix-command flakes" --flake $HOME/git/dotfiles
24 | ```
25 |
26 | After that, you can just run `darwin-rebuild switch --flake $HOME/git/dotfiles` (or the `nix-apply` shell alias).
27 |
28 | ## Managing Dotfiles
29 |
30 | Dotfiles are managed via [Nix](https://nixos.org/), using `flake.nix` and [`home-manager`](https://github.com/nix-community/home-manager).
31 | On NixOS, the `home-manager` configuration is managed as a NixOS module, so simply rebuilding your NixOS config also applies the new
32 | `home-manager` config. On macOS, the same behavior is achieved via [nix-darwin](https://github.com/nix-darwin/nix-darwin).
33 |
--------------------------------------------------------------------------------
/docs/src/special-characters.md:
--------------------------------------------------------------------------------
1 | # Special Unicode Characters
2 |
3 | I can never find these characters to copy/paste so here they are.
4 |
5 | - chevron right:
6 | - chevron left:
7 | - block: █
8 | - terminal icon:
9 | - circumscribed arrow:
10 | - vim icon:
11 | - git branch icon:
12 | - lock icon:
13 | - border characters: "─", "│", "─", "│", "╭", "╮", "╯", "╰"
14 |
15 | See also:
16 |
17 | - [ShapeCatcher](https://shapecatcher.com)
18 | - [Image to ASCII](https://505e06b2.github.io/Image-to-Braille/)
19 |
--------------------------------------------------------------------------------
/docs/src/ublock-filters.md:
--------------------------------------------------------------------------------
1 | # Useful uBlock Origin Filters
2 |
3 | Have some useful filters you'd like to contribute? Please [add them here](https://github.com/mrjones2014/dotfiles/edit/master/ublock-mdbook/src/ublock-filters.yml)!
4 |
5 |
6 | Expand to copy all filters
7 |
8 | {{#ublockfilters-all}}
9 |
10 |
11 |
12 | Or add the full list as a filter list in uBlock Origin: [ublock-filters.txt](https://mjones.network/ublock-filters.txt)
13 |
14 | {{#ublockfilters-toc}}
15 |
16 | {{#ublockfilters}}
17 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "My dotfiles managed with nix as a flake";
3 |
4 | inputs = {
5 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
6 | tokyonight.url = "github:mrjones2014/tokyonight.nix";
7 | zjstatus.url = "github:dj95/zjstatus";
8 | nix-darwin = {
9 | url = "github:nix-darwin/nix-darwin/master";
10 | inputs.nixpkgs.follows = "nixpkgs";
11 | };
12 | home-manager = {
13 | url = "github:nix-community/home-manager";
14 | inputs.nixpkgs.follows = "nixpkgs";
15 | };
16 | _1password-shell-plugins = {
17 | url = "github:1Password/shell-plugins";
18 | inputs.nixpkgs.follows = "nixpkgs";
19 | };
20 | agenix = {
21 | url = "github:ryantm/agenix";
22 | inputs.nixpkgs.follows = "nixpkgs";
23 | };
24 | zen-browser = {
25 | url = "github:0xc000022070/zen-browser-flake";
26 | inputs.nixpkgs.follows = "nixpkgs";
27 | };
28 | };
29 |
30 | outputs =
31 | inputs@{
32 | nixpkgs,
33 | nix-darwin,
34 | home-manager,
35 | agenix,
36 | ...
37 | }:
38 | {
39 | nixosConfigurations = {
40 | server = nixpkgs.lib.nixosSystem {
41 | specialArgs = {
42 | inherit inputs;
43 | isServer = true;
44 | isLinux = true;
45 | isThinkpad = false;
46 | isDarwin = false;
47 | };
48 | system = "x86_64-linux";
49 | modules = [
50 | home-manager.nixosModules.home-manager
51 | agenix.nixosModules.default
52 | {
53 | environment.systemPackages = [ agenix.packages.x86_64-linux.default ];
54 | }
55 | ./nixos/common.nix
56 | ./hosts/server
57 | {
58 | home-manager = {
59 | backupFileExtension = "backup";
60 | useUserPackages = true;
61 | users.mat = import ./home-manager/server.nix;
62 | extraSpecialArgs = {
63 | inherit inputs;
64 | isServer = true;
65 | isLinux = true;
66 | isThinkpad = false;
67 | isDarwin = false;
68 | };
69 | };
70 | }
71 | ];
72 | };
73 | pc = nixpkgs.lib.nixosSystem {
74 | specialArgs = {
75 | inherit inputs;
76 | isServer = false;
77 | isDarwin = false;
78 | isLinux = true;
79 | isThinkpad = false;
80 | };
81 | system = "x86_64-linux";
82 | modules = [
83 | ./nixos/common.nix
84 | ./hosts/pc
85 | home-manager.nixosModules.home-manager
86 | {
87 | home-manager = {
88 | backupFileExtension = "backup";
89 | useUserPackages = true;
90 | users.mat = import ./home-manager/home.nix;
91 | extraSpecialArgs = {
92 | inherit inputs;
93 | isServer = false;
94 | isDarwin = false;
95 | isLinux = true;
96 | isThinkpad = false;
97 | };
98 | };
99 | }
100 | ];
101 | };
102 | laptop = nixpkgs.lib.nixosSystem {
103 | specialArgs = {
104 | inherit inputs;
105 | isServer = false;
106 | isDarwin = false;
107 | isLinux = true;
108 | isThinkpad = true;
109 | };
110 | system = "x86_64-linux";
111 | modules = [
112 | ./nixos/common.nix
113 | ./hosts/laptop
114 | home-manager.nixosModules.home-manager
115 | {
116 | home-manager = {
117 | backupFileExtension = "backup";
118 | useUserPackages = true;
119 | users.mat = import ./home-manager/home.nix;
120 | extraSpecialArgs = {
121 | inherit inputs;
122 | isServer = false;
123 | isDarwin = false;
124 | isLinux = true;
125 | isThinkpad = true;
126 | };
127 | };
128 | }
129 | ];
130 | };
131 | };
132 | darwinConfigurations."Mats-MacBook-Pro" =
133 | let
134 | specialArgs = {
135 | inherit inputs;
136 | isServer = false;
137 | isDarwin = true;
138 | isLinux = false;
139 | isThinkpad = false;
140 | };
141 | in
142 | nix-darwin.lib.darwinSystem {
143 | inherit specialArgs;
144 | pkgs = nixpkgs.legacyPackages."aarch64-darwin";
145 | modules = [
146 | ./hosts/darwin
147 | home-manager.darwinModules.default
148 | {
149 | home-manager = {
150 | users.mat = import ./home-manager/home.nix;
151 | extraSpecialArgs = specialArgs;
152 | };
153 | }
154 | ];
155 | };
156 | };
157 | }
158 |
--------------------------------------------------------------------------------
/home-manager/components/_1password-shell.nix:
--------------------------------------------------------------------------------
1 | { inputs, pkgs, ... }:
2 | let
3 | op_sudo_password_script = pkgs.writeScript "opsudo.bash" ''
4 | #!${pkgs.bash}/bin/bash
5 | # TODO figure out a way to do this without silently depending on `op` being on $PATH
6 | # using `$\{pkgs._1password}/bin/op` results in unable to connect to desktop app
7 | PASSWORD="$(op read "op://Private/System Password/password")"
8 | if [[ -z "$PASSWORD" ]]; then
9 | echo "Failed to get password from 1Password."
10 | read -s -p "Password: " PASSWORD
11 | fi
12 |
13 | echo $PASSWORD
14 | '';
15 | in
16 | {
17 | home.packages = with pkgs; [ _1password-cli ];
18 | imports = [ inputs._1password-shell-plugins.hmModules.default ];
19 | programs = {
20 | fish = {
21 | interactiveShellInit = ''
22 | export SUDO_ASKPASS="${op_sudo_password_script}"
23 | alias sudo="sudo -A"
24 | '';
25 | };
26 | _1password-shell-plugins = {
27 | enable = true;
28 | plugins = with pkgs; [
29 | gh
30 | glab
31 | ];
32 | };
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/home-manager/components/discord.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrjones2014/dotfiles/412867a9f6b0e7e40ad5ea5f8d22c3e2102de6b8/home-manager/components/discord.png
--------------------------------------------------------------------------------
/home-manager/components/espanso.nix:
--------------------------------------------------------------------------------
1 | { isServer, ... }:
2 | {
3 | services.espanso = {
4 | enable = !isServer;
5 | configs.default = {
6 | search_shortcut = "OFF";
7 | show_notifications = false;
8 | };
9 | matches = {
10 | base = {
11 | matches = [
12 | {
13 | trigger = ":shrug";
14 | replace = "¯\\_(ツ)_/¯";
15 | }
16 | {
17 | trigger = ":tflip";
18 | replace = "(╯°□°)╯︵ ┻━┻";
19 | }
20 |
21 | {
22 | trigger = ":fingerguns";
23 | replace = "(☞ ͡° ͜ʖ ͡°)☞";
24 | }
25 | ];
26 | };
27 | };
28 | };
29 | }
30 |
--------------------------------------------------------------------------------
/home-manager/components/fzf.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | lib,
4 | ...
5 | }:
6 | let
7 | key-bindings = [
8 | # you can use the command `fish_key_reader` to get the key codes to use
9 | {
10 | lhs = "ctrl-e";
11 | rhs = "fzf-vim-widget";
12 | }
13 | {
14 | lhs = "ctrl-g";
15 | rhs = "fzf-project-widget";
16 | }
17 | {
18 | lhs = "ctrl-f";
19 | rhs = "fzf-file-widget-wrapped";
20 | }
21 | {
22 | lhs = "ctrl-r";
23 | rhs = "fzf-history-widget-wrapped";
24 | }
25 | {
26 | lhs = "ctrl-p";
27 | rhs = "fzf-jj-bookmarks";
28 | }
29 | ];
30 | in
31 | {
32 | programs.fzf = {
33 | enable = true;
34 | defaultCommand = "${pkgs.ripgrep}/bin/rg --files";
35 | defaultOptions = [
36 | "--prompt=' '"
37 | "--marker=''"
38 | "--marker=' '"
39 | # this keybind should match the telescope ones in nvim config
40 | ''--bind="ctrl-d:preview-down,ctrl-f:preview-up"''
41 | ];
42 | fileWidgetCommand = "${pkgs.ripgrep}/bin/rg --files";
43 | fileWidgetOptions = [
44 | # Preview files with bat
45 | "--preview '${pkgs.bat}/bin/bat --color=always {}'"
46 | "--layout default"
47 | ];
48 | };
49 | programs.fish = {
50 | interactiveShellInit = ''
51 | for mode in insert default normal
52 | ${lib.concatMapStrings (keybind: ''
53 | bind -M $mode ${keybind.lhs} ${keybind.rhs}
54 | '') key-bindings}
55 | end
56 | '';
57 | functions = {
58 |
59 | fzf-history-widget-wrapped = ''
60 | history merge # make FZF search history from all sessions
61 | fzf-history-widget
62 | _prompt_move_to_bottom
63 | '';
64 | fzf-file-widget-wrapped = ''
65 | fzf-file-widget
66 | _prompt_move_to_bottom
67 | '';
68 | fzf-project-widget = ''
69 | function _project_jump_get_icon
70 | set -l remote "$(git --work-tree $argv[1] --git-dir $argv[1]/.git ls-remote --get-url 2> /dev/null)"
71 | if string match -r "github.com" "$remote" >/dev/null
72 | set_color --bold normal
73 | echo -n
74 | else if string match -r gitlab "$remote" >/dev/null
75 | set_color --bold FC6D26
76 | echo -n
77 | else
78 | set_color --bold F74E27
79 | echo -n
80 | end
81 | end
82 |
83 | function _project_jump_format_project
84 | set -l repo "$HOME/git/$argv[1]"
85 | set -l branch $(git --work-tree $repo --git-dir $repo/.git branch --show-current)
86 | set_color --bold cyan
87 | echo -n "$argv[1]"
88 | echo -n " $(_project_jump_get_icon $repo)"
89 | set_color --bold f74e27
90 | echo " $branch"
91 | end
92 |
93 | function _project_jump_parse_project
94 | # check args
95 | set -f selected $argv[1]
96 |
97 | # if not coming from args
98 | if [ "$selected" = "" ]
99 | # check pipe
100 | read -f selected
101 | end
102 |
103 | # if still empty, return
104 | if [ "$selected" = "" ]
105 | return
106 | end
107 |
108 | set -l dir $(string trim "$(string match -r ".*(?=\s*||)" "$selected")")
109 | echo "$HOME/git/$dir"
110 | end
111 |
112 | function _project_jump_get_projects
113 | # make sure to use built-in ls, not exa
114 | for dir in $(command ls "$HOME/git")
115 | if test -d "$HOME/git/$dir"
116 | echo "$(_project_jump_format_project $dir)"
117 | end
118 | end
119 | end
120 |
121 | function _project_jump_get_readme
122 | set -l dir $(_project_jump_parse_project "$argv[1]")
123 | if test -f "$dir/README.md"
124 | CLICOLOR_FORCE=1 COLORTERM=truecolor ${pkgs.glow}/bin/glow -p -s dark -w 150 "$dir/README.md"
125 | else
126 | echo
127 | echo $(set_color --bold) "README.md not found"
128 | echo
129 | ${pkgs.lsd}/bin/lsd -F $dir
130 | end
131 | end
132 |
133 | argparse 'format=' -- $argv
134 | if set -ql _flag_format
135 | _project_jump_get_readme $_flag_format
136 | else
137 | set -l selected $(_project_jump_get_projects | fzf --ansi --preview-window 'right,70%' --preview "fzf-project-widget --format {}")
138 | if test -n "$selected"
139 | cd $(_project_jump_parse_project "$selected")
140 | end
141 | commandline -f repaint
142 | end
143 | '';
144 | fzf-vim-widget = ''
145 | # modified from fzf-file-widget
146 | set -l commandline $(__fzf_parse_commandline)
147 | set -l dir $commandline[1]
148 | set -l fzf_query $commandline[2]
149 | set -l prefix $commandline[3]
150 |
151 | # "-path \$dir'*/\\.*'" matches hidden files/folders inside $dir but not
152 | # $dir itself, even if hidden.
153 | test -n "$FZF_CTRL_T_COMMAND"; or set -l FZF_CTRL_T_COMMAND "
154 | command find -L \$dir -mindepth 1 \\( -path \$dir'*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \
155 | -o -type f -print \
156 | -o -type d -print \
157 | -o -type l -print 2> /dev/null | sed 's@^\./@@'"
158 |
159 | test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
160 | begin
161 | set -lx FZF_DEFAULT_OPTS "--height $FZF_TMUX_HEIGHT --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS"
162 | eval "$FZF_CTRL_T_COMMAND | "(__fzfcmd)' -m --query "'$fzf_query'"' | while read -l r
163 | set result $result $r
164 | end
165 | end
166 | if [ -z "$result" ]
167 | _prompt_move_to_bottom
168 | commandline -f repaint
169 | return
170 | end
171 | set -l filepath_result
172 | for i in $result
173 | set filepath_result "$filepath_result$prefix"
174 | set filepath_result "$filepath_result$(string escape $i)"
175 | set filepath_result "$filepath_result "
176 | end
177 | _prompt_move_to_bottom
178 | commandline -f repaint
179 | $EDITOR $result
180 | '';
181 | fzf-jj-bookmarks = ''
182 | set -l selected_bookmark (jj bookmark list | fzf --height 40%)
183 | if test -n "$selected_bookmark"
184 | # parse the bookmark name out of the full bookmark info line
185 | set -l bookmark_name (string split ":" "$selected_bookmark" | head -n 1 | string trim)
186 | commandline -i " $bookmark_name "
187 | end
188 | commandline -f repaint
189 | '';
190 | };
191 | };
192 | }
193 |
--------------------------------------------------------------------------------
/home-manager/components/git.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | isDarwin,
4 | isLinux,
5 | isServer,
6 | ...
7 | }:
8 | let
9 | git_checkout_fzf_script = pkgs.writeScript "git-ch.bash" ''
10 | #!${pkgs.bash}/bin/bash
11 | if test "$#" -ne 0; then
12 | if [[ "$*" = "master" ]] || [[ "$*" = "main" ]]; then
13 | git checkout "$(git branch --format '%(refname:short)' --sort=-committerdate --list master main | head -n1)"
14 | else
15 | git checkout "$@"
16 | fi
17 | else
18 | git branch -a --format="%(refname:short)" | sed 's|origin/||g' | grep -v "HEAD" | grep -v "origin" | sort | uniq | ${pkgs.fzf}/bin/fzf | xargs git checkout
19 | fi
20 | '';
21 | in
22 | {
23 | programs.git = {
24 | enable = true;
25 | package = pkgs.git.override {
26 | guiSupport = false; # gui? never heard of her.
27 | };
28 | ignores = [
29 | "Session.vim"
30 | ".DS_Store"
31 | ".direnv/"
32 | ];
33 | aliases = {
34 | s = "status";
35 | newbranch = "checkout -b";
36 | commit-amend = "commit --amend --no-edit";
37 | prune-branches = ''!git branch --merged | grep -v \"master\" | grep -v \"main\" | grep -v \"$(git branch --show-current)\" | grep -v "[*]" >/tmp/merged-branches && vim /tmp/merged-branches && xargs git branch -d \\K.*$') && git diff --name-only $MASTER_BRANCH | ${pkgs.fzf}/bin/fzf --ansi --preview 'git diff --color=always $MASTER_BRANCH {}' --bind 'enter:become($EDITOR {})'";
47 | };
48 | userName = "Mat Jones";
49 | userEmail = "mat@mjones.network";
50 | signing = {
51 | key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDsT6GLG7sY8YKX7JM+jqS3EAti3YMzwHKWViveqkZvu";
52 | signByDefault = true;
53 | };
54 | delta = {
55 | enable = true;
56 | options = {
57 | line-numbers = true;
58 | navigate = true;
59 | };
60 | };
61 | extraConfig = {
62 | rerere.enabled = true;
63 | pull.rebase = false;
64 | push.autoSetupRemote = true;
65 | commit.gpgsign = true;
66 | gpg =
67 | {
68 | format = "ssh";
69 | }
70 | // pkgs.lib.optionalAttrs (!isServer) {
71 | ssh = {
72 | program =
73 | if isLinux then
74 | "/run/current-system/sw/bin/op-ssh-sign"
75 | else
76 | "/Applications/1Password.app/Contents/MacOS/op-ssh-sign";
77 | };
78 | };
79 | core = {
80 | autocrlf = false;
81 | fsmonitor = true;
82 | untrackedcache = true;
83 | };
84 | init.defaultBranch = "master";
85 | color = {
86 | ui = true;
87 | "diff-highlight" = {
88 | oldNormal = "red bold";
89 | oldHighlight = "red bold 52";
90 | newNormal = "green bold";
91 | newHighlight = "green bold 22";
92 | };
93 | diff = {
94 | meta = "11";
95 | frag = "magenta bold";
96 | func = "146 bold";
97 | commit = "yellow bold";
98 | old = "red bold";
99 | new = "green bold";
100 | whitespace = "red reverse";
101 | };
102 | };
103 | fetch.prune = true;
104 | checkout.defaultRemote = "origin";
105 | # faster git server communications
106 | # https://git-scm.com/docs/protocol-v2
107 | protocol.version = 2;
108 | url = {
109 | "git@gitlab.1password.io:" = {
110 | insteadOf = "https://gitlab.1password.io/";
111 | };
112 | # Use HTTPS for cargo updates
113 | "https://github.com/rust-lang/crates.io-index" = {
114 | insteadOf = "https://github.com/rust-lang/crates.io-index";
115 | };
116 | };
117 | };
118 | };
119 | }
120 |
--------------------------------------------------------------------------------
/home-manager/components/gnome/dconf.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | lib,
4 | isThinkpad,
5 | ...
6 | }:
7 | let
8 | wallpaperImg = pkgs.fetchurl {
9 | url = "https://w.wallhaven.cc/full/z8/wallhaven-z8g6wv.jpg";
10 | hash = "sha256-AJLXmM86rnuoT0I93ewFocxFKwikIOt1h+JDOmWzQzg=";
11 | };
12 | gnomeExtensions = with pkgs.gnomeExtensions; [
13 | dash-to-dock
14 | tray-icons-reloaded
15 | gtile
16 | search-light
17 | user-themes
18 | quick-settings-tweaker
19 | ];
20 | enabled-extensions = map (ext: ext.extensionUuid) gnomeExtensions;
21 | in
22 | {
23 | home.packages = gnomeExtensions;
24 | # see https://github.com/wimpysworld/nix-config/blob/b8a260ddea1bbf088461f7272382d99acbf86ce7/home-manager/_mixins/desktop/pantheon.nix
25 | dconf.settings = {
26 | "org/gnome/desktop/wm/preferences" = {
27 | # If this doesn't seem to be working, check `gsettings get org.gnome.settings-daemon.plugins.xsettings overrides`
28 | # window button order may be in there
29 | button-layout = "close,minimize,maximize,appmenu:";
30 | };
31 | "org/gnome/shell" = {
32 | disable-user-extensions = false;
33 | disable-extension-version-validation = true;
34 | favorite-apps = [
35 | "org.gnome.Settings.desktop"
36 | "org.gnome.Nautilus.desktop"
37 | "spotify.desktop"
38 | "zen-beta.desktop"
39 | "com.mitchellh.ghostty.desktop"
40 | "standard-notes.desktop"
41 | "1password.desktop"
42 | "signal.desktop"
43 | "vesktop.desktop"
44 | ] ++ lib.lists.optionals (!isThinkpad) [ "steam.desktop" ];
45 | inherit enabled-extensions;
46 | };
47 | "org/gnome/desktop/interface" = {
48 | color-scheme = "prefer-dark";
49 | enable-hot-corners = false;
50 | clock-show-weekday = true;
51 | };
52 | "org/gnome/shell/extensions/gtile" = {
53 | auto-close-keyboard-shortcut = [ true ];
54 | grid-sizes = "11x1";
55 | # right 2/3rds of screen
56 | resize1 = "11x1 5:1 11:1";
57 | preset-resize-1 = [ "l" ];
58 | # left 1/3rd of screen
59 | resize2 = "11x1 1:1 4:1";
60 | preset-resize-2 = [ "h" ];
61 | # center 2/3rds of screen
62 | resize3 = "5x1 2:1 4:1";
63 | preset-resize-3 = [ "k" ];
64 | };
65 | "org/gnome/shell/extensions/dash-to-dock" = {
66 | dash-max-icon-size = 48;
67 | intellihide-mode = "ALL_WINDOWS";
68 | disable-overview-on-startup = true;
69 | show-show-apps-button = false;
70 | running-indicator-style = "DOTS";
71 | apply-custom-theme = false;
72 | };
73 | "org/gnome/shell/extensions/trayIconsReloaded".icons-limit = 10;
74 | "org/gnome/shell/extensions/quick-settings-tweaks" = {
75 | output-show-selected = true;
76 | input-show-selected = true;
77 | input-always-show = true;
78 | volume-mixer-enabled = true;
79 | };
80 | "org/gnome/desktop/background" = {
81 | picture-uri = "file://${wallpaperImg}";
82 | picture-uri-dark = "file://${wallpaperImg}";
83 | };
84 | "org/gnome/desktop/datetime".automatic-timezone = true;
85 | "org/gnome/desktop/wm/keybindings" = {
86 | move-to-monitor-left = [ "h" ];
87 | move-to-monitor-right = [ "l" ];
88 | move-to-monitor-up = [ "k" ];
89 | move-to-monitor-down = [ "j" ];
90 | };
91 | "org/gnome/desktop/peripherals/touchpad".send-events = "enabled";
92 | "org/gnome/shell/extensions/search-light" = {
93 | shortcut-search = [ "space" ];
94 | };
95 | "org/gnome/mutter/keybindings" = {
96 | switch-monitor = [ ]; # disable stupid ass default +p defautl shortcut
97 | };
98 | };
99 | }
100 |
--------------------------------------------------------------------------------
/home-manager/components/gnome/default.nix:
--------------------------------------------------------------------------------
1 | { isLinux, lib, ... }:
2 | { }
3 | // lib.attrsets.optionalAttrs isLinux {
4 | imports = [ ./dconf.nix ];
5 | xdg.configFile = {
6 | # workaround for https://github.com/nix-community/home-manager/issues/3447
7 | # autostart 1Password in the background so I can use the SSH agent without manually opening the app first
8 | "autostart/1password.desktop".text = ''
9 | [Desktop Entry]
10 | Name=1Password
11 | Exec=1password --silent
12 | Terminal=false
13 | Type=Application
14 | Icon=1password
15 | StartupWMClass=1Password
16 | Comment=Password manager and secure wallet
17 | MimeType=x-scheme-handler/onepassword;
18 | Categories=Office;
19 | '';
20 | # autostart Signal
21 | "autostart/signal-desktop.desktop".text = ''
22 | [Desktop Entry]
23 | Name=Signal
24 | Exec=signal-desktop --no-sandbox --start-in-tray %U
25 | Terminal=false
26 | Type=Application
27 | Icon=signal-desktop
28 | StartupWMClass=Signal
29 | Comment=Private messaging from your desktop
30 | MimeType=x-scheme-handler/sgnl;x-scheme-handler/signalcaptcha;
31 | Categories=Network;InstantMessaging;Chat;
32 | '';
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/home-manager/components/jujutsu.nix:
--------------------------------------------------------------------------------
1 | { config, lib, ... }:
2 | let
3 | palette = import ./tokyonight_palette.nix { inherit lib; };
4 | in
5 | {
6 | programs.jujutsu = {
7 | enable = true;
8 | settings = {
9 | git = {
10 | private-commits = lib.mkDefault "description(glob:'wip:*') | description(glob:'private:*')";
11 | push-bookmark-prefix = lib.mkDefault "mrj/";
12 | };
13 | revset-aliases = lib.mkDefault {
14 | # The `trunk().. &` bit is an optimization to scan for non-`mine()` commits
15 | # only among commits that are not in `trunk()`
16 | # This prevents me from mutating any commit that isn't authored by me
17 | "immutable_heads()" = "builtin_immutable_heads() | (trunk().. & ~mine())";
18 | };
19 | colors = {
20 | # lighten these up a bit for statusline integration
21 | "change_id rest" = palette.dark3;
22 | "commit_id rest" = palette.dark3;
23 | };
24 | aliases = {
25 | # https://shaddy.dev/notes/jj-tug/
26 | tug = [
27 | "bookmark"
28 | "move"
29 | "--from"
30 | "heads(::@- & bookmarks())"
31 | "--to"
32 | "@-"
33 | ];
34 | };
35 | user = {
36 | name = config.programs.git.userName;
37 | email = config.programs.git.userEmail;
38 | };
39 | signing = {
40 | inherit (config.programs.git.signing) key;
41 | behavior = "force";
42 | backend = "ssh";
43 | backends.ssh.program = config.programs.git.extraConfig.gpg.ssh.program;
44 | };
45 | };
46 | };
47 | }
48 |
--------------------------------------------------------------------------------
/home-manager/components/nvim.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | pkgs,
4 | isLinux,
5 | ...
6 | }:
7 | {
8 | home.sessionVariables.MANPAGER = "nvim -c 'Man!' -o -";
9 | xdg.configFile = {
10 | ripgrep_ignore.text = ''
11 | .git/
12 | yarn.lock
13 | package-lock.json
14 | packer_compiled.lua
15 | .DS_Store
16 | .netrwhist
17 | dist/
18 | node_modules/
19 | **/node_modules/
20 | wget-log
21 | wget-log.*
22 | /vendor
23 | '';
24 | nvim = {
25 | source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/git/dotfiles/nvim";
26 | recursive = true;
27 | };
28 | };
29 |
30 | programs.neovim = {
31 | enable = true;
32 | viAlias = true;
33 | vimAlias = true;
34 | vimdiffAlias = true;
35 | withNodeJs = false;
36 | withRuby = false;
37 | withPython3 = false;
38 | defaultEditor = true;
39 | coc.enable = false;
40 |
41 | extraWrapperArgs =
42 | [
43 | "--set"
44 | "NVIM_RUST_ANALYZER"
45 | "${pkgs.rust-analyzer}/bin/rust-analyzer"
46 | "--set"
47 | "LIBSQLITE"
48 | ]
49 | ++ (
50 | if isLinux then
51 | [ "${pkgs.sqlite.out}/lib/libsqlite3.so" ]
52 | else
53 | [ "${pkgs.sqlite.out}/lib/libsqlite3.dylib" ]
54 | );
55 |
56 | extraLuaPackages = ps: [ ps.jsregexp ];
57 | extraPackages = with pkgs; [
58 | # for compiling Treesitter parsers
59 | gcc
60 |
61 | # debuggers
62 | lldb # comes with lldb-vscode
63 |
64 | # formatters and linters
65 | nixfmt-rfc-style
66 | rustfmt
67 | shfmt
68 | stylua
69 | statix
70 | luajitPackages.luacheck
71 | prettierd
72 |
73 | # LSP servers
74 | nixd
75 | nil
76 | rust-analyzer
77 | cargo # sometimes required for rust-analyzer to work
78 | taplo
79 | gopls
80 | lua
81 | shellcheck
82 | marksman
83 | sumneko-lua-language-server
84 | nodePackages_latest.typescript-language-server
85 | yaml-language-server
86 | bash-language-server
87 |
88 | # this includes css-lsp, html-lsp, json-lsp, eslint-lsp
89 | nodePackages_latest.vscode-langservers-extracted
90 |
91 | # other utils and plugin dependencies
92 | gnumake
93 | src-cli
94 | ripgrep
95 | fd
96 | sqlite
97 | lemmy-help
98 | fzf
99 | cargo
100 | cargo-nextest
101 | clippy
102 | glow
103 | mariadb
104 | imagemagick
105 | ];
106 | };
107 | }
108 |
--------------------------------------------------------------------------------
/home-manager/components/recyclarr.nix:
--------------------------------------------------------------------------------
1 | { pkgs, ... }:
2 | {
3 | home.packages = [
4 | pkgs.recyclarr
5 | (pkgs.writeScriptBin "recyclarr-sync" ''
6 | op inject -i ~/git/dotfiles/conf.d/recyclarr.yml -o ~/git/dotfiles/recyclarr-tmp.yml \
7 | && recyclarr sync --config ~/git/dotfiles/recyclarr-tmp.yml
8 | rm -f ~/git/dotfiles/recyclarr-tmp.yml
9 | '')
10 | ];
11 | }
12 |
--------------------------------------------------------------------------------
/home-manager/components/ssh.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | isLinux,
4 | isServer,
5 | lib,
6 | ...
7 | }:
8 | let
9 | sshAuthSock = "${config.home.homeDirectory}/${
10 | if isLinux then ".1password" else "Library/Group Containers/2BUA8C4S2C.com.1password/t"
11 | }/agent.sock";
12 | in
13 | {
14 | home.sessionVariables = { } // lib.optionalAttrs (!isServer) { SSH_AUTH_SOCK = sshAuthSock; };
15 | programs.ssh = {
16 | enable = true;
17 | forwardAgent = true;
18 | matchBlocks =
19 | { }
20 | // lib.optionalAttrs (!isServer) {
21 | # allow SSH_AUTH_SOCK to be forwarded on server from SSH client
22 | "*".extraOptions.IdentityAgent = ''"${sshAuthSock}"'';
23 | };
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/home-manager/components/starship.nix:
--------------------------------------------------------------------------------
1 | { lib, isServer, ... }:
2 | let
3 | palette = import ./tokyonight_palette.nix { inherit lib; };
4 |
5 | git_bg = palette.fg_gutter;
6 | git_bg_ansi = palette.hexToAnsiRgb git_bg;
7 | git_fg = palette.fg;
8 | dir_bg = palette.dark5;
9 | server_bg = palette.teal;
10 | server_sep = color: if isServer then "[](fg:${server_bg} bg:${color})" else "";
11 | in
12 | with palette;
13 | {
14 | programs.starship = {
15 | enable = true;
16 | enableTransience = true;
17 | settings = {
18 | command_timeout = 200;
19 | format = lib.concatStrings [
20 | (
21 | if isServer then
22 | "[ ](bg:${server_bg})[](bg:${server_bg} fg:${bg_dark})[ ](bg:${server_bg})"
23 | else
24 | ""
25 | )
26 | "$character"
27 | "$directory"
28 | "\${env_var.IN_NIX_SHELL}"
29 | "\${custom.dir_sep_no_git}"
30 | "\${custom.git_server_icon}"
31 | "$git_branch"
32 | "\${custom.jj_log}"
33 | "$git_status"
34 | "$line_break"
35 | "$shlvl"
36 | "[❯](bg:${bg_dark} fg:${green}) "
37 | ];
38 | right_format = "$cmd_duration";
39 | shlvl = {
40 | disabled = false;
41 | symbol = "❯";
42 | format = "[$symbol]($style)";
43 | repeat = true;
44 | repeat_offset = 1;
45 | threshold = 0;
46 | };
47 | character = {
48 | format = "$symbol";
49 | success_symbol = "${server_sep green}[ ](bold bg:${green} fg:${bg_dark})[](fg:${green} bg:${dir_bg})";
50 | error_symbol = "${server_sep red}[ ](bold bg:${red} fg:${bg_dark})[](fg:${red} bg:${dir_bg})";
51 | vicmd_symbol = "${server_sep blue}[ ](bold bg:${blue} fg:${bg_dark})[](fg:${blue} bg:${dir_bg})";
52 | vimcmd_replace_one_symbol = "${server_sep purple}[ ](bold bg:${purple} fg:${bg_dark})[](fg:${purple} bg:${dir_bg})";
53 | vimcmd_replace_symbol = "${server_sep purple}[ ](bold bg:${purple} fg:${bg_dark})[](fg:${purple} bg:${dir_bg})";
54 | vimcmd_visual_symbol = "${server_sep yellow}[ ](bold bg:${yellow} fg:${bg_dark})[](fg:${yellow} bg:${dir_bg})";
55 | };
56 | cmd_duration.format = "[ $duration](bold ${dark3})";
57 | directory.format = "[ ](bg:${dir_bg})[$path](bold bg:${dir_bg} fg:${green})";
58 | env_var.IN_NIX_SHELL.format = "[ ](bg:${dir_bg})[](bg:${dir_bg} fg:${blue5})[ ](bg:${dir_bg})";
59 | git_status.format = "[$all_status$ahead_behind](bg:${git_bg} fg:${yellow})[](fg:${git_bg} bg:${bg_dark})";
60 | git_branch = {
61 | format = "([ ](bg:${git_bg})[$branch](bg:${git_bg} fg:${git_fg})[ ](bg:${git_bg}))";
62 | # detatched HEAD mode means probably we're using jj
63 | ignore_branches = [ "HEAD" ];
64 | };
65 | custom = {
66 | jj_log = {
67 | description = "Show info from jj about current change set";
68 | when = true;
69 | require_repo = true;
70 | # NB: --ignore-working-copy, do not snapshot the repo when generating status for prompt, its too slow and not necessary;
71 | # all my git operations/changes will be from manually run jj commands which will snapshot the repo at that time
72 | # The `sed` part is to modify the ANSI color codes so that the background color matches
73 | # I'll need to update this
74 | command = ''jj log --ignore-working-copy -r @- -n 1 --no-graph --no-pager --color always -T "separate(' ', format_short_change_id(self.change_id()), self.bookmarks())" | sed "s/\x1b\[\([0-9;]*\)m/\x1b[\1;48;2;${git_bg_ansi}m/g"'';
75 | # Render ANSI colors directly from the `jj log` output
76 | unsafe_no_escape = true;
77 | format = "([ ](bg:${git_bg})[$output](bg:${git_bg})[ ](bg:${git_bg}))";
78 | shell = [
79 | "bash"
80 | "--noprofile"
81 | "--norc"
82 | ];
83 | };
84 | git_server_icon = {
85 | description = "Show a GitLab or GitHub icon depending on current git remote";
86 | when = "git rev-parse --is-inside-work-tree 2> /dev/null";
87 | command = ''GIT_REMOTE=$(git ls-remote --get-url 2> /dev/null); if [[ "$GIT_REMOTE" =~ "github" ]]; then printf ""; elif [[ "$GIT_REMOTE" =~ "gitlab" ]]; then echo ""; else echo ""; fi'';
88 | shell = [
89 | "bash"
90 | "--noprofile"
91 | "--norc"
92 | ];
93 | format = "([](fg:${dir_bg} bg:${git_bg})[ ](bg:${git_bg})[$output](bg:${git_bg} fg:${git_fg})[ ](bg:${git_bg}))";
94 | };
95 | dir_sep_no_git = {
96 | description = "Show rounded separator when not in a git repo";
97 | format = "[](fg:${dir_bg} bg:${bg_dark})";
98 | when = "! git rev-parse --is-inside-work-tree 2> /dev/null";
99 | shell = [
100 | "bash"
101 | "--noprofile"
102 | "--norc"
103 | ];
104 | };
105 | };
106 | };
107 | };
108 | }
109 |
--------------------------------------------------------------------------------
/home-manager/components/terminal.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | config,
4 | isThinkpad,
5 | isDarwin,
6 | ...
7 | }:
8 | {
9 | imports = [ ./zellij.nix ];
10 | home.packages = [ pkgs.victor-mono ];
11 | programs.ghostty = {
12 | enable = true;
13 | # TODO remove eventually; Ghostty nixpkgs is broken
14 | # on macOS but I still want to generate the config with nix.
15 | # I'll install manually on macOS and use Nix to generate the config.
16 | # For now, shim the package with pkgs.emptyDirectory to trick nix
17 | # into still generating the config.
18 | package = if isDarwin then null else pkgs.ghostty;
19 | installBatSyntax = false;
20 | clearDefaultKeybinds = true;
21 | settings = {
22 | title = "Ghostty";
23 | "font-family" = "Victor Mono Semibold";
24 | "font-family-italic" = "Victor Mono Medium Oblique";
25 | "font-family-bold-italic" = "Victor Mono Bold Oblique";
26 | "font-feature" = "ss02,ss06";
27 | "font-size" = "16";
28 | "cursor-style" = "block";
29 | "cursor-style-blink" = false;
30 | "macos-option-as-alt" = true;
31 | "shell-integration-features" = "no-cursor";
32 | "mouse-hide-while-typing" = true;
33 | "link-url" = true;
34 | # NB: workaround for zellij not having the right PATH on macOS
35 | "command" =
36 | ''env EDITOR="nvim" PATH="$PATH:${config.home.homeDirectory}/.nix-profile/bin" ${pkgs.zellij}/bin/zellij'';
37 | "maximize" = isThinkpad;
38 | "keybind" = [
39 | "super+v=paste_from_clipboard"
40 | "super+c=copy_to_clipboard"
41 | "super+q=quit"
42 | ];
43 | };
44 | };
45 | }
46 |
--------------------------------------------------------------------------------
/home-manager/components/tokyonight_palette.nix:
--------------------------------------------------------------------------------
1 | { lib, ... }:
2 | rec {
3 | bg_dark = "#1a1b26";
4 | bg = "#24283b";
5 | bg_highlight = "#292e42";
6 | terminal_black = "#414868";
7 | fg = "#c0caf5";
8 | fg_dark = "#a9b1d6";
9 | fg_gutter = "#3b4261";
10 | dark3 = "#545c7e";
11 | comment = "#565f89";
12 | dark5 = "#737aa2";
13 | blue0 = "#3d59a1";
14 | blue = "#7aa2f7";
15 | cyan = "#7dcfff";
16 | blue1 = "#2ac3de";
17 | blue2 = "#0db9d7";
18 | blue5 = "#89ddff";
19 | blue6 = "#b4f9f8";
20 | blue7 = "#394b70";
21 | magenta = "#bb9af7";
22 | magenta2 = "#ff007c";
23 | purple = "#9d7cd8";
24 | orange = "#ff9e64";
25 | yellow = "#e0af68";
26 | green = "#9ece6a";
27 | green1 = "#73daca";
28 | green2 = "#41a6b5";
29 | teal = "#1abc9c";
30 | red = "#f7768e";
31 | red1 = "#db4b4b";
32 |
33 | # Convert hex digit to decimal
34 | hexDigitToInt =
35 | c:
36 | if c == "0" then
37 | 0
38 | else if c == "1" then
39 | 1
40 | else if c == "2" then
41 | 2
42 | else if c == "3" then
43 | 3
44 | else if c == "4" then
45 | 4
46 | else if c == "5" then
47 | 5
48 | else if c == "6" then
49 | 6
50 | else if c == "7" then
51 | 7
52 | else if c == "8" then
53 | 8
54 | else if c == "9" then
55 | 9
56 | else if c == "a" || c == "A" then
57 | 10
58 | else if c == "b" || c == "B" then
59 | 11
60 | else if c == "c" || c == "C" then
61 | 12
62 | else if c == "d" || c == "D" then
63 | 13
64 | else if c == "e" || c == "E" then
65 | 14
66 | else if c == "f" || c == "F" then
67 | 15
68 | else
69 | throw "Invalid hex digit: ${c}";
70 |
71 | # Convert 2-digit hex string to decimal
72 | hexToInt =
73 | hex:
74 | let
75 | chars = lib.stringToCharacters (lib.toLower hex);
76 | high = hexDigitToInt (builtins.elemAt chars 0);
77 | low = hexDigitToInt (builtins.elemAt chars 1);
78 | in
79 | high * 16 + low;
80 |
81 | # Function to convert hex color to RGB ANSI background escape sequence
82 | hexToAnsiRgb =
83 | hexColor:
84 | let
85 | # Remove the # prefix and extract RGB components
86 | cleanHex = builtins.substring 1 6 hexColor;
87 | r = hexToInt (builtins.substring 0 2 cleanHex);
88 | g = hexToInt (builtins.substring 2 2 cleanHex);
89 | b = hexToInt (builtins.substring 4 2 cleanHex);
90 | in
91 | "${toString r};${toString g};${toString b}";
92 | }
93 |
--------------------------------------------------------------------------------
/home-manager/components/vencord.nix:
--------------------------------------------------------------------------------
1 | {
2 | isLinux,
3 | pkgs,
4 | lib,
5 | ...
6 | }:
7 | {
8 | xdg.dataFile."icons/discord.png".source = ./discord.png;
9 | home.packages = lib.lists.optionals isLinux [
10 | # vesktop discord client, I don't like
11 | # vesktop's icon, so override it
12 | (pkgs.vesktop.overrideAttrs (oldAttrs: {
13 | desktopItems = [
14 | (pkgs.makeDesktopItem {
15 | name = "vesktop";
16 | desktopName = "Discord";
17 | exec = "vesktop %U";
18 | icon = "discord";
19 | startupWMClass = "Vesktop";
20 | genericName = "Internet Messenger";
21 | keywords = [
22 | "discord"
23 | "vencord"
24 | "electron"
25 | "chat"
26 | ];
27 | categories = [
28 | "Network"
29 | "InstantMessaging"
30 | "Chat"
31 | ];
32 | })
33 | ];
34 | }))
35 | ];
36 | }
37 |
--------------------------------------------------------------------------------
/home-manager/home.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs,
3 | config,
4 | pkgs,
5 | lib,
6 | isDarwin,
7 | isLinux,
8 | isThinkpad,
9 | ...
10 | }:
11 | {
12 | nixpkgs.overlays = [
13 | (
14 | final: prev:
15 | (import ../packages {
16 | inherit inputs;
17 | inherit pkgs;
18 | inherit (prev) system;
19 | })
20 | )
21 | ];
22 | home = {
23 | username = "mat";
24 | homeDirectory = if isLinux then "/home/mat" else "/Users/mat";
25 |
26 | # This value determines the Home Manager release that your configuration is
27 | # compatible with. This helps avoid breakage when a new Home Manager release
28 | # introduces backwards incompatible changes.
29 | #
30 | # You should not change this value, even if you update Home Manager. If you do
31 | # want to update the value, then make sure to first check the Home Manager
32 | # release notes.
33 | stateVersion = "22.11";
34 | packages =
35 | with pkgs;
36 | [
37 | spotify
38 | gnumake
39 | ]
40 | ++ lib.lists.optionals isDarwin [
41 | # put macOS specific packages here
42 | darwin-rebuild
43 | bash # macOS ships with a very old version of bash for whatever reason
44 | ]
45 | ++ lib.lists.optionals isLinux [
46 | # put Linux specific packages here
47 | signal-desktop
48 | vlc
49 | parsec-bin
50 | ungoogled-chromium
51 | ]
52 | ++ lib.lists.optionals isThinkpad [ ]
53 | ++ lib.lists.optionals (isLinux && (!isThinkpad)) [
54 | # desktop only packages
55 | obs-studio
56 | r2modman
57 | qbittorrent
58 | ];
59 | file."${config.home.homeDirectory}/.xprofile".text = ''
60 | export XDG_DATA_DIRS="$XDG_DATA_DIRS:/home/mat/.nix-profile/share"
61 | '';
62 | };
63 | xdg.enable = true;
64 |
65 | imports = [
66 | ./shared.nix
67 | ./components/terminal.nix
68 | ./components/_1password-shell.nix
69 | ./components/espanso.nix
70 | ./components/gnome
71 | ./components/recyclarr.nix
72 | ./components/jujutsu.nix
73 | ./components/vencord.nix
74 | ../nixos/allowed-unfree.nix
75 | inputs.zen-browser.homeModules.default
76 | ];
77 |
78 | programs = {
79 | zen-browser.enable = !isDarwin;
80 | nix-index.enable = true;
81 | # Let Home Manager install and manage itself.
82 | home-manager.enable = true;
83 | # Direnv integration for flakes
84 | direnv.enable = true;
85 | direnv.nix-direnv.enable = true;
86 | };
87 | }
88 |
--------------------------------------------------------------------------------
/home-manager/server.nix:
--------------------------------------------------------------------------------
1 | { inputs, pkgs, ... }:
2 | {
3 | nixpkgs.overlays = [
4 | (
5 | final: prev:
6 | (import ../packages {
7 | inherit inputs;
8 | inherit pkgs;
9 | inherit (prev) system;
10 | })
11 | )
12 | ];
13 | home = {
14 | username = "mat";
15 | homeDirectory = "/home/mat";
16 | sessionVariables = {
17 | TERM = "xterm-256color";
18 | COLORTERM = "truecolor";
19 | };
20 | packages = [ pkgs.wireguard-tools ];
21 | # This value determines the Home Manager release that your configuration is
22 | # compatible with. This helps avoid breakage when a new Home Manager release
23 | # introduces backwards incompatible changes.
24 | #
25 | # You should not change this value, even if you update Home Manager. If you do
26 | # want to update the value, then make sure to first check the Home Manager
27 | # release notes.
28 | stateVersion = "22.11";
29 | };
30 | xdg.enable = true;
31 | imports = [
32 | ./shared.nix
33 | ./components/zellij.nix
34 | ];
35 | }
36 |
--------------------------------------------------------------------------------
/home-manager/shared.nix:
--------------------------------------------------------------------------------
1 | { pkgs, ... }:
2 | {
3 | home.packages = [ pkgs.nix-search-cli ];
4 | imports = [
5 | ../nixos/nix-conf.nix
6 | ../nixos/theme.nix
7 | ./components/fish.nix
8 | ./components/nvim.nix
9 | ./components/ssh.nix
10 | ./components/starship.nix
11 | ./components/git.nix
12 | ./components/fzf.nix
13 | ];
14 | }
15 |
--------------------------------------------------------------------------------
/hosts/darwin/default.nix:
--------------------------------------------------------------------------------
1 | { config, pkgs, ... }:
2 | {
3 | imports = [
4 | ../../nixos/nix-conf.nix
5 | ./settings.nix
6 | ];
7 | nixpkgs.hostPlatform = "aarch64-darwin";
8 | programs.fish.enable = true;
9 | users.users.mat = {
10 | name = "mat";
11 | home = "/Users/mat";
12 | shell = pkgs.fish;
13 | };
14 | environment.variables.HOMEBREW_NO_ANALYTICS = "1";
15 | environment.systemPath = [ "/opt/homebrew/bin" ];
16 | homebrew = {
17 | enable = true;
18 | taps = [ "kiraum/tap" ];
19 | brews = [ "kiraum/tap/cody" ];
20 | };
21 | system.primaryUser = config.users.users.mat.name;
22 | system.stateVersion = 6;
23 | }
24 |
--------------------------------------------------------------------------------
/hosts/darwin/settings.nix:
--------------------------------------------------------------------------------
1 | { config, ... }:
2 | {
3 | system.defaults = {
4 | screencapture = {
5 | # store screenshots in ~/Downloads by default
6 | location = "${config.users.users.mat.home}/Downloads";
7 | # only copy screenshots to clipboard by default
8 | target = "clipboard";
9 | };
10 | NSGlobalDomain = {
11 | # expanded save panel by default
12 | NSNavPanelExpandedStateForSaveMode = true;
13 | NSNavPanelExpandedStateForSaveMode2 = true;
14 | # do not save to icloud by default
15 | NSDocumentSaveNewDocumentsToCloud = false;
16 | # Display ASCII control characters using caret notation in standard text views
17 | # Try e.g. `cd /tmp; unidecode "\x{0000}" > cc.txt; open -e cc.txt`
18 | NSTextShowsControlCharacters = true;
19 | # Disable "natural" scrolling (it should be called unnatural scrolling)
20 | "com.apple.swipescrolldirection" = false;
21 | # Trackpad: enable tap to click
22 | "com.apple.mouse.tapBehavior" = 1;
23 | # Enable full keyboard access for all controls
24 | # (e.g. enable Tab in modal dialogs)
25 | AppleKeyboardUIMode = 3;
26 | };
27 | finder = {
28 | AppleShowAllFiles = true;
29 | AppleShowAllExtensions = true;
30 | ShowPathbar = true;
31 | };
32 | dock = {
33 | show-process-indicators = false;
34 | show-recents = false;
35 | # Don't show dashboard as a space
36 | dashboard-in-overlay = true;
37 | # Don't rearrange spaces based on most recently used
38 | mru-spaces = false;
39 | autohide = true;
40 | # Disable all hot corners
41 | wvous-tl-corner = 1;
42 | wvous-tr-corner = 1;
43 | wvous-bl-corner = 1;
44 | wvous-br-corner = 1;
45 | };
46 | ActivityMonitor = {
47 | OpenMainWindow = true;
48 | # show CPU usage graph on icon
49 | IconType = 5;
50 | # Show all processes
51 | ShowCategory = 100;
52 | };
53 | };
54 | }
55 |
--------------------------------------------------------------------------------
/hosts/laptop/default.nix:
--------------------------------------------------------------------------------
1 | { ... }:
2 | {
3 | networking = {
4 | hostName = "nixos-laptop";
5 | networkmanager.enable = true;
6 | firewall = {
7 | # if packets are still dropped, they will show up in dmesg
8 | logReversePathDrops = true;
9 | # wireguard trips rpfilter up
10 | extraCommands = ''
11 | ip46tables -t mangle -I nixos-fw-rpfilter -p udp -m udp --sport 9999 -j RETURN
12 | ip46tables -t mangle -I nixos-fw-rpfilter -p udp -m udp --dport 9999 -j RETURN
13 | '';
14 | extraStopCommands = ''
15 | ip46tables -t mangle -D nixos-fw-rpfilter -p udp -m udp --sport 9999 -j RETURN || true
16 | ip46tables -t mangle -D nixos-fw-rpfilter -p udp -m udp --dport 9999 -j RETURN || true
17 | '';
18 | };
19 | };
20 |
21 | imports = [
22 | ../../nixos/desktop_environment.nix
23 | ../../nixos/_1password.nix
24 | ../../nixos/allowed-unfree.nix
25 | ./hardware-configuration.nix
26 | ];
27 |
28 | programs = {
29 | fish.enable = true;
30 |
31 | neovim = {
32 | enable = true;
33 | defaultEditor = true;
34 | };
35 | };
36 |
37 | environment.variables = {
38 | SUDO_EDITOR = "nvim";
39 | EDITOR = "nvim";
40 | };
41 |
42 | services = {
43 | xserver.enable = true;
44 | mullvad-vpn.enable = true;
45 | flatpak.enable = true;
46 | };
47 |
48 | boot = {
49 | loader = {
50 | systemd-boot.enable = true;
51 | efi.canTouchEfiVariables = true;
52 | };
53 | };
54 |
55 | # This option defines the first version of NixOS you have installed on this particular machine,
56 | # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
57 | #
58 | # Most users should NEVER change this value after the initial install, for any reason,
59 | # even if you've upgraded your system to a new NixOS release.
60 | #
61 | # This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
62 | # so changing it will NOT upgrade your system.
63 | #
64 | # This value being lower than the current NixOS release does NOT mean your system is
65 | # out of date, out of support, or vulnerable.
66 | #
67 | # Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
68 | # and migrated your data accordingly.
69 | #
70 | # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
71 | system.stateVersion = "25.05";
72 | }
73 |
--------------------------------------------------------------------------------
/hosts/laptop/hardware-configuration.nix:
--------------------------------------------------------------------------------
1 | # Do not modify this file! It was generated by ‘nixos-generate-config’
2 | # and may be overwritten by future invocations. Please make changes
3 | # to /etc/nixos/configuration.nix instead.
4 | {
5 | config,
6 | lib,
7 | pkgs,
8 | modulesPath,
9 | ...
10 | }:
11 |
12 | {
13 | imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
14 |
15 | boot.initrd.availableKernelModules = [
16 | "xhci_pci"
17 | "nvme"
18 | "usb_storage"
19 | "sd_mod"
20 | "rtsx_pci_sdmmc"
21 | ];
22 | boot.initrd.kernelModules = [ ];
23 | boot.kernelModules = [ ];
24 | boot.extraModulePackages = [ ];
25 |
26 | fileSystems."/" = {
27 | device = "/dev/disk/by-uuid/71f0a300-d3af-4d9c-bcae-4da7b2cb7cdd";
28 | fsType = "ext4";
29 | };
30 |
31 | boot.initrd.luks.devices."luks-478962df-c7d5-4e0c-9f30-f5435e27612a".device =
32 | "/dev/disk/by-uuid/478962df-c7d5-4e0c-9f30-f5435e27612a";
33 |
34 | fileSystems."/boot" = {
35 | device = "/dev/disk/by-uuid/EFA5-597E";
36 | fsType = "vfat";
37 | options = [
38 | "fmask=0077"
39 | "dmask=0077"
40 | ];
41 | };
42 |
43 | swapDevices = [ ];
44 |
45 | # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
46 | # (the default) this is the recommended approach. When using systemd-networkd it's
47 | # still possible to use this option, but it's recommended to use it in conjunction
48 | # with explicit per-interface declarations with `networking.interfaces..useDHCP`.
49 | networking.useDHCP = lib.mkDefault true;
50 | # networking.interfaces.enp0s31f6.useDHCP = lib.mkDefault true;
51 | # networking.interfaces.wlp4s0.useDHCP = lib.mkDefault true;
52 | # networking.interfaces.wwp0s20f0u6i12.useDHCP = lib.mkDefault true;
53 |
54 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
55 | hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
56 |
57 | # enable VAAPI
58 | nixpkgs.config.packageOverrides = pkgs: {
59 | vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; };
60 | };
61 | hardware.graphics = {
62 | enable = true;
63 | extraPackages = with pkgs; [
64 | intel-media-driver
65 | intel-vaapi-driver # previously vaapiIntel
66 | vaapiVdpau
67 | intel-compute-runtime # OpenCL filter support (hardware tonemapping and subtitle burn-in)
68 | vpl-gpu-rt # QSV on 11th gen or newer
69 | ];
70 | };
71 | }
72 |
--------------------------------------------------------------------------------
/hosts/pc/default.nix:
--------------------------------------------------------------------------------
1 | { pkgs, ... }:
2 | {
3 | networking.hostName = "nixos-pc";
4 | imports = [
5 | ../../nixos/desktop_environment.nix
6 | ../../nixos/_1password.nix
7 | ../../nixos/allowed-unfree.nix
8 | ../../nixos/sshd.nix
9 | ./hardware-configuration.nix
10 | ];
11 | boot.loader.efi.efiSysMountPoint = "/boot";
12 | powerManagement.cpuFreqGovernor = "performance";
13 | hardware = {
14 | graphics = {
15 | enable = true;
16 | enable32Bit = true;
17 | };
18 |
19 | amdgpu.amdvlk = {
20 | enable = true;
21 | support32Bit.enable = true;
22 | };
23 |
24 | # setup udev rules for ZSA keyboard firmware flashing
25 | keyboard.zsa.enable = true;
26 |
27 | # logitech mouse support
28 | logitech.wireless = {
29 | enable = true;
30 | # installs solaar for configuring mouse buttons
31 | enableGraphical = true;
32 | };
33 |
34 | };
35 |
36 | programs = {
37 | fish.enable = true;
38 | coolercontrol.enable = true;
39 | gamemode.enable = true;
40 | gamescope.enable = true;
41 | steam = {
42 | enable = true;
43 | remotePlay.openFirewall = true;
44 | dedicatedServer.openFirewall = true;
45 | protontricks.enable = true;
46 | gamescopeSession.enable = true;
47 | };
48 |
49 | neovim = {
50 | enable = true;
51 | defaultEditor = true;
52 | };
53 | };
54 |
55 | environment.variables = {
56 | SUDO_EDITOR = "nvim";
57 | EDITOR = "nvim";
58 | };
59 |
60 | environment.systemPackages = with pkgs; [
61 | steam-run
62 | winetricks
63 | steamtinkerlaunch
64 | parsec-bin
65 | mullvad-vpn
66 | prismlauncher
67 | wally-cli
68 | protonup-qt
69 | dolphin-emu
70 | # rpcs3 # broken right now
71 | ];
72 |
73 | services = {
74 | xserver.enable = true;
75 | mullvad-vpn.enable = true;
76 | flatpak.enable = true;
77 | };
78 |
79 | # for dolphin: https://nixos.wiki/wiki/Dolphin_Emulator
80 | # boot.extraModulePackages = [ config.boot.kernelPackages.gcadapter-oc-kmod ];
81 |
82 | # to autoload at boot:
83 | boot.kernelModules = [ "gcadapter_oc" ];
84 | # services.udev.packages = [ pkgs.dolphinEmu ];
85 |
86 | # This value determines the NixOS release from which the default
87 | # settings for stateful data, like file locations and database versions
88 | # on your system were taken. It‘s perfectly fine and recommended to leave
89 | # this value at the release version of the first install of this system.
90 | # Before changing this value read the documentation for this option
91 | # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
92 | system.stateVersion = "22.11"; # Did you read the comment?
93 | }
94 |
--------------------------------------------------------------------------------
/hosts/pc/hardware-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | lib,
3 | modulesPath,
4 | ...
5 | }:
6 |
7 | {
8 | imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
9 | boot = {
10 | initrd.availableKernelModules = [
11 | "xhci_pci"
12 | "ahci"
13 | "nvme"
14 | "usbhid"
15 | ];
16 | initrd.kernelModules = [ ];
17 | kernelModules = [
18 | "kvm-amd"
19 | "coretemp"
20 | ];
21 | extraModulePackages = [ ];
22 | };
23 | fileSystems = {
24 | "/" = {
25 | device = "/dev/disk/by-uuid/d6f1cc32-5216-43eb-a22c-339f1e0ebabf";
26 | fsType = "ext4";
27 | };
28 |
29 | "/boot" = {
30 | device = "/dev/disk/by-uuid/264C-1D31";
31 | fsType = "vfat";
32 | options = [
33 | "fmask=0077"
34 | "dmask=0077"
35 | ];
36 | };
37 |
38 | "/mnt/storage" = {
39 | device = "/dev/disk/by-uuid/aad56a7c-b586-4e16-b91f-58fbd796f400";
40 | fsType = "ext4";
41 | };
42 | };
43 |
44 | swapDevices = [ ];
45 | networking = {
46 | # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
47 | # (the default) this is the recommended approach. When using systemd-networkd it's
48 | # still possible to use this option, but it's recommended to use it in conjunction
49 | # with explicit per-interface declarations with `networking.interfaces..useDHCP`.
50 | useDHCP = lib.mkDefault true;
51 | networkmanager = {
52 | enable = true;
53 | wifi.powersave = false;
54 | };
55 | };
56 | # networking.interfaces.eno2.useDHCP = lib.mkDefault true;
57 | # networking.interfaces.wlo1.useDHCP = lib.mkDefault true;
58 |
59 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
60 | powerManagement.cpuFreqGovernor = "performance";
61 | powerManagement.powertop.enable = true;
62 |
63 | hardware = {
64 | cpu.amd.updateMicrocode = true;
65 | enableRedistributableFirmware = true;
66 | enableAllHardware = true;
67 | };
68 | }
69 |
--------------------------------------------------------------------------------
/hosts/server/adguard.nix:
--------------------------------------------------------------------------------
1 | let
2 | filterLists = [
3 | # The Big List of Hacked Malware Web Sites
4 | "https://adguardteam.github.io/HostlistsRegistry/assets/filter_9.txt"
5 | # malicious url blocklist
6 | "https://adguardteam.github.io/HostlistsRegistry/assets/filter_11.txt"
7 | # https://oisd.nl, "passes the girlfriend test"
8 | "https://big.oisd.nl"
9 | # Native trackers (Windows, Apple, etc.)
10 | "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/pro.plus.txt"
11 | ];
12 | webuiPort = 9003;
13 | in
14 | {
15 | networking.firewall = {
16 | allowedTCPPorts = [ 53 ];
17 | allowedUDPPorts = [ 53 ];
18 | };
19 | services.nginx.subdomains.adguard.port = webuiPort;
20 | services.adguardhome = {
21 | enable = true;
22 | port = webuiPort;
23 | settings = {
24 | users = [
25 | {
26 | name = "mat";
27 | # generated with `nix-shell -p apacheHttpd` followed by `htpasswd -B -C 10 -n -b mat [password here]`
28 | # NB: Remember to put a space before the command so it doesn't go into shell history!
29 | password = "$2y$10$BKjlLZTCAgsfEO1L/TJFG.BiirZaHCE8NximCOdD7U5gCq9cz1x1C";
30 | }
31 | ];
32 | dns = {
33 | upstream_dns = [
34 | "https://dns.quad9.net/dns-query"
35 | "https://base.dns.mullvad.net/dns-query"
36 | ];
37 | anonymize_client_ip = false;
38 | };
39 | tls = {
40 | enabled = true;
41 | server_name = "adguard.mjones.network";
42 | force_https = true;
43 | # since its behind a reverse proxy, nginx takes care of encryption
44 | allow_unencrypted_doh = true;
45 | };
46 | filtering = {
47 | protection_enabled = true;
48 | filtering_enabled = true;
49 | rewrites = [
50 | {
51 | domain = "*.mjones.network";
52 | answer = import ./ip.nix;
53 | }
54 | ];
55 | };
56 | filters = map (url: {
57 | inherit url;
58 | enabled = true;
59 | }) filterLists;
60 | user_rules = [
61 | "||comparative-mollusk-y0a4rcrnmuyekxc7u0ajsvh7.herokudns.com^"
62 | "||telemetry.affine.run^"
63 | ];
64 | };
65 | };
66 | }
67 |
--------------------------------------------------------------------------------
/hosts/server/cleanuperr.nix:
--------------------------------------------------------------------------------
1 | { config, ... }:
2 | let
3 | envFile = config.age.secrets.cleanuperr_env.path;
4 | in
5 | {
6 | age.secrets.cleanuperr_env.file = ../../secrets/cleanuperr_env.age;
7 | virtualisation.oci-containers.containers.cleanuperr = {
8 | autoStart = true;
9 | image = "ghcr.io/flmorg/cleanuperr:latest";
10 | environmentFiles = [ envFile ];
11 | };
12 | }
13 |
--------------------------------------------------------------------------------
/hosts/server/containers.nix:
--------------------------------------------------------------------------------
1 | { pkgs, ... }:
2 | let
3 | update-containers = pkgs.writeShellScriptBin "update-containers" ''
4 | SUDO=""
5 | if [[ $(id -u) -ne 0 ]]; then
6 | SUDO="sudo"
7 | fi
8 |
9 | images=$($SUDO ${pkgs.podman}/bin/podman ps -a --format="{{.Image}}" | sort -u)
10 |
11 | for image in $images
12 | do
13 | $SUDO ${pkgs.podman}/bin/podman pull $image
14 | done
15 |
16 | # restart all running containers
17 | $SUDO ${pkgs.podman} container restart --running
18 | '';
19 | in
20 | {
21 | virtualisation.oci-containers.backend = "podman";
22 | virtualisation.podman.defaultNetwork.settings.dns_enabled = true;
23 | environment.systemPackages = [ update-containers ];
24 | # update oci-containers every Monday
25 | systemd = {
26 | timers.updatecontainers = {
27 | timerConfig = {
28 | Unit = "updatecontainers.service";
29 | OnCalendar = "Mon 02:00";
30 | };
31 | wantedBy = [ "timers.target" ];
32 | };
33 | services = {
34 | updatecontainers = {
35 | serviceConfig = {
36 | Type = "oneshot";
37 | ExecStart = "update-containers";
38 | };
39 | };
40 | };
41 | };
42 | }
43 |
--------------------------------------------------------------------------------
/hosts/server/default.nix:
--------------------------------------------------------------------------------
1 | { ... }:
2 | {
3 | # This option defines the first version of NixOS you have installed on this particular machine,
4 | # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
5 | #
6 | # Most users should NEVER change this value after the initial install, for any reason,
7 | # even if you've upgraded your system to a new NixOS release.
8 | #
9 | # This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
10 | # so changing it will NOT upgrade your system.
11 | #
12 | # This value being lower than the current NixOS release does NOT mean your system is
13 | # out of date, out of support, or vulnerable.
14 | #
15 | # Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
16 | # and migrated your data accordingly.
17 | #
18 | # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
19 | system.stateVersion = "23.11";
20 |
21 | imports = [
22 | ./hardware-configuration.nix
23 | ./nixosModules/nginx.nix
24 | ./media.nix
25 | ./nas.nix
26 | ./containers.nix
27 | ./wireguard.nix
28 | ./observability.nix
29 | ./vikunja.nix
30 | ./docmost.nix
31 | ./duckdns.nix
32 | ./paperless.nix
33 | ./adguard.nix
34 | ./homeassistant.nix
35 | ../../nixos/sshd.nix
36 | ];
37 |
38 | powerManagement.cpuFreqGovernor = "performance";
39 | boot = {
40 | # less aggressive swap usage
41 | kernel.sysctl."vm.swappiness" = 25;
42 | loader = {
43 | systemd-boot.enable = true;
44 | efi.canTouchEfiVariables = true;
45 | efi.efiSysMountPoint = "/boot";
46 | };
47 | };
48 |
49 | programs.neovim = {
50 | enable = true;
51 | defaultEditor = true;
52 | };
53 |
54 | # enable vaapi on OS-level
55 | nixpkgs.config.packageOverrides = pkgs: {
56 | vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; };
57 | };
58 | programs = {
59 | fish.enable = true;
60 | dconf.enable = true; # TODO this shouldn't be needed but home-manager complains without it
61 | };
62 | }
63 |
--------------------------------------------------------------------------------
/hosts/server/docmost.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | pkgs,
4 | ...
5 | }:
6 | let
7 | port = 3000;
8 | storage_location = "/var/lib/docmost/storage";
9 | db_data_location = "/var/lib/docmost/db-data";
10 | redis_data_location = "/var/lib/docmost/redis-data";
11 | podman_network = "docmost";
12 | podman_dns_port = 8053;
13 | in
14 | {
15 | systemd = {
16 | # Create required directories
17 | tmpfiles.rules = [
18 | "d ${storage_location} 0777 root podman - -"
19 | "d ${db_data_location} 0777 root podman - -"
20 | "d ${redis_data_location} 0777 root podman - -"
21 | ];
22 |
23 | # Create network for containers
24 | services.docmost-podman-network-create = {
25 | serviceConfig.Type = "oneshot";
26 | wantedBy = [
27 | "podman-docmost-app.service"
28 | "podman-docmost-db.service"
29 | "podman-docmost-redis.service"
30 | ];
31 | script = ''
32 | ${pkgs.podman}/bin/podman network inspect ${podman_network} > /dev/null 2>&1 || ${pkgs.podman}/bin/podman network create ${podman_network}
33 | '';
34 | };
35 | };
36 |
37 | age.secrets.docmost_env.file = ../../secrets/docmost_env.age;
38 | services.nginx.subdomains.docs.port = port;
39 | virtualisation.containers.containersConf.settings.network.dns_bind_port = podman_dns_port;
40 | virtualisation.oci-containers.containers = {
41 | docmost-db = {
42 | autoStart = true;
43 | image = "postgres:16-alpine";
44 | volumes = [ "${db_data_location}:/var/lib/postgresql/data" ];
45 | networks = [ podman_network ];
46 | environmentFiles = [ config.age.secrets.docmost_env.path ];
47 | environment = {
48 | POSTGRES_DB = "docmost";
49 | POSTGRES_USER = "docmost";
50 | };
51 | extraOptions = [
52 | "--health-cmd=pg_isready -U docmost -d docmost"
53 | "--health-interval=10s"
54 | "--health-timeout=5s"
55 | "--health-retries=5"
56 | ];
57 | };
58 |
59 | docmost-redis = {
60 | autoStart = true;
61 | image = "redis:7.2-alpine";
62 | volumes = [ "${redis_data_location}:/data" ];
63 | networks = [ podman_network ];
64 | extraOptions = [
65 | "--health-cmd=redis-cli --raw incr ping"
66 | "--health-interval=10s"
67 | "--health-timeout=5s"
68 | "--health-retries=5"
69 | ];
70 | };
71 |
72 | docmost-app = {
73 | autoStart = true;
74 | image = "docmost/docmost:latest";
75 | volumes = [ "${storage_location}:/app/data/storage" ];
76 | ports = [ "${toString port}:${toString port}" ];
77 | networks = [ podman_network ];
78 | environmentFiles = [ config.age.secrets.docmost_env.path ];
79 | environment = {
80 | APP_URL = "http://localhost:${toString port}";
81 | REDIS_URL = "redis://docmost-redis:6379";
82 | DISABLE_TELEMETRY = "true";
83 | };
84 | dependsOn = [
85 | "docmost-db"
86 | "docmost-redis"
87 | ];
88 | };
89 | };
90 | }
91 |
--------------------------------------------------------------------------------
/hosts/server/duckdns.nix:
--------------------------------------------------------------------------------
1 | { config, ... }:
2 | {
3 | age.secrets.duckdns_token.file = ../../secrets/duckdns_token.age;
4 | services.duckdns = {
5 | enable = true;
6 | tokenFile = config.age.secrets.duckdns_token.path;
7 | domains = [ "mjonesnetwork" ];
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/hosts/server/hardware-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | config,
4 | lib,
5 | modulesPath,
6 | ...
7 | }:
8 |
9 | {
10 | imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
11 | boot = {
12 | initrd = {
13 | luks.devices = {
14 | "cryptroot" = {
15 | device = "/dev/disk/by-partlabel/root"; # Use partition label, not filesystem label
16 | preLVM = true;
17 | };
18 |
19 | "cryptswap" = {
20 | device = "/dev/disk/by-partlabel/swap"; # Partition label for swap
21 | preLVM = true;
22 | };
23 | };
24 |
25 | availableKernelModules = [
26 | "xhci_pci"
27 | "ahci"
28 | "usbhid"
29 | "usb_storage"
30 | "sd_mod"
31 | "sr_mod"
32 | ];
33 | kernelModules = [ ];
34 | };
35 | kernelModules = [ "kvm-intel" ];
36 | extraModulePackages = [ ];
37 | };
38 | fileSystems = {
39 | "/" = {
40 | device = "/dev/disk/by-label/nixos";
41 | fsType = "ext4";
42 | };
43 | "/boot" = {
44 | device = "/dev/disk/by-label/boot";
45 | fsType = "vfat";
46 | options = [
47 | "fmask=0077"
48 | "dmask=0077"
49 | "uid=0"
50 | "gid=0"
51 | ];
52 | };
53 | "/mnt/fileshare" = {
54 | device = "/dev/disk/by-label/fileshare";
55 | fsType = "ext4";
56 | };
57 | "/export/fileshare" = {
58 | device = "/mnt/fileshare";
59 | options = [ "bind" ];
60 | };
61 | "/mnt/jellyfin" = {
62 | device = "/dev/disk/by-label/media";
63 | fsType = "ext4";
64 | };
65 | };
66 |
67 | swapDevices = [ { device = "/dev/disk/by-label/swap"; } ];
68 | networking = {
69 | # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
70 | # (the default) this is the recommended approach. When using systemd-networkd it's
71 | # still possible to use this option, but it's recommended to use it in conjunction
72 | # with explicit per-interface declarations with `networking.interfaces..useDHCP`.
73 | useDHCP = lib.mkDefault true;
74 | hostName = "homelab";
75 | defaultGateway = "192.168.1.1";
76 | nameservers = [
77 | # TODO temporary. Works around a stupid ass issue with AmpliFi router.
78 | # Remove when I get my new UniFi router replacement.
79 | "127.0.0.1"
80 | config.networking.defaultGateway.address
81 | ];
82 | # static IP on ethernet interface
83 | interfaces.enp0s31f6.ipv4.addresses = [
84 | {
85 | address = import ./ip.nix;
86 | prefixLength = 24;
87 | }
88 | ];
89 | };
90 |
91 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
92 | hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
93 |
94 | # enable hardware transcoding stuff for jellyfin
95 | nixpkgs.config.packageOverrides = pkgs: {
96 | vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; };
97 | };
98 | hardware.graphics = {
99 | enable = true;
100 | extraPackages = with pkgs; [
101 | intel-media-driver
102 | intel-vaapi-driver # previously vaapiIntel
103 | vaapiVdpau
104 | intel-compute-runtime # OpenCL filter support (hardware tonemapping and subtitle burn-in)
105 | vpl-gpu-rt # QSV on 11th gen or newer
106 | ];
107 | };
108 | }
109 |
--------------------------------------------------------------------------------
/hosts/server/homeassistant.nix:
--------------------------------------------------------------------------------
1 | { config, ... }:
2 | {
3 | services = {
4 | nginx.subdomains.home.port = config.services.home-assistant.config.http.server_port;
5 | home-assistant = {
6 | enable = true;
7 | extraPackages =
8 | ps: with ps; [
9 | psycopg2
10 | pyatv
11 | universal-silabs-flasher
12 | ];
13 | extraComponents = [
14 | "default_config"
15 | "met"
16 | "esphome"
17 | "ring"
18 | "homekit_controller"
19 | "apple_tv"
20 | "brother"
21 | "adguard"
22 | "sonos"
23 | "nanoleaf"
24 | "api"
25 | ];
26 | config = {
27 | default_config = { };
28 | recorder.db_url = "postgresql://@/hass";
29 | homeassistant = {
30 | unit_system = "us_customary";
31 | time_zone = "America/New_York";
32 | };
33 | http = {
34 | trusted_proxies = [ "127.0.0.1" ];
35 | use_x_forwarded_for = true;
36 | };
37 | };
38 | };
39 | postgresql = {
40 | enable = true;
41 | ensureDatabases = [ "hass" ];
42 | ensureUsers = [
43 | {
44 | name = "hass";
45 | ensureDBOwnership = true;
46 | }
47 | ];
48 | };
49 | };
50 | }
51 |
--------------------------------------------------------------------------------
/hosts/server/ip.nix:
--------------------------------------------------------------------------------
1 | "192.168.1.6"
2 |
--------------------------------------------------------------------------------
/hosts/server/media.nix:
--------------------------------------------------------------------------------
1 | { config, ... }:
2 | let
3 | huntarr_port = 9705;
4 | huntarr_data = "/var/lib/huntarr";
5 | in
6 | {
7 | imports = [
8 | ./torrent_client.nix
9 | ./cleanuperr.nix
10 | ];
11 | services.nginx.subdomains = {
12 | # jellyfin doesn't let you configure port via Nix, so just use the default value here
13 | # see: https://jellyfin.org/docs/general/networking/index.html
14 | jellyfin.port = 8096;
15 | jellyseerr.port = config.services.jellyseerr.port;
16 | prowlarr = {
17 | inherit (config.services.prowlarr.settings.server) port;
18 | useLongerTimeout = true;
19 | };
20 | sonarr = {
21 | inherit (config.services.sonarr.settings.server) port;
22 | useLongerTimeout = true;
23 | };
24 | radarr = {
25 | inherit (config.services.radarr.settings.server) port;
26 | useLongerTimeout = true;
27 | };
28 | bazarr = {
29 | port = config.services.bazarr.listenPort;
30 | useLongerTimeout = true;
31 | };
32 | huntarr = {
33 | port = huntarr_port;
34 | useLongerTimeout = true;
35 | };
36 | };
37 | services = {
38 | jellyfin.enable = true;
39 | jellyseerr.enable = true;
40 | prowlarr.enable = true;
41 | sonarr.enable = true;
42 | radarr.enable = true;
43 | bazarr.enable = true;
44 | };
45 | # TODO remove this when this is resolved https://github.com/NixOS/nixpkgs/issues/360592
46 | nixpkgs.config.permittedInsecurePackages = [
47 | "aspnetcore-runtime-6.0.36"
48 | "aspnetcore-runtime-wrapped-6.0.36"
49 | "dotnet-sdk-6.0.428"
50 | "dotnet-sdk-wrapped-6.0.428"
51 | ];
52 |
53 | systemd.tmpfiles.rules = [
54 | "d ${huntarr_data} 0777 root podman -"
55 | ];
56 | virtualisation.oci-containers.containers.huntarr = {
57 | image = "huntarr/huntarr:latest";
58 | autoStart = true;
59 | ports = [ "${toString huntarr_port}:${toString huntarr_port}" ];
60 | volumes = [ "${huntarr_data}:/config" ];
61 | environment.TZ = "America/New_York";
62 | };
63 | }
64 |
--------------------------------------------------------------------------------
/hosts/server/nas.nix:
--------------------------------------------------------------------------------
1 | {
2 | # these are NOT exposed to the internet
3 | services = {
4 | # samba share, allow guest users full access
5 | # it's only reachable via LAN anyway
6 | samba = {
7 | enable = true;
8 | openFirewall = true;
9 | settings = {
10 | global = {
11 | "guest account" = "nobody";
12 | "map to guest" = "Bad User";
13 | "load printers" = "no";
14 | "printcap name" = "/dev/null";
15 | };
16 | };
17 | settings = {
18 | fileshare = {
19 | path = "/export/fileshare";
20 | browseable = "yes";
21 | writable = "yes";
22 | public = "yes";
23 | "read only" = "no";
24 | "force user" = "nobody";
25 | "force group" = "users";
26 | "force directory mode" = "2770";
27 | };
28 | };
29 | };
30 | samba-wsdd = {
31 | enable = true;
32 | openFirewall = true;
33 | };
34 | };
35 | }
36 |
--------------------------------------------------------------------------------
/hosts/server/nixosModules/nginx.nix:
--------------------------------------------------------------------------------
1 | { config, lib, ... }:
2 | let
3 | # Define the subdomain configuration options
4 | subdomainType = lib.types.submodule {
5 | options = {
6 | port = lib.mkOption {
7 | type = lib.types.port;
8 | description = "Port to proxy to.";
9 | };
10 | default = lib.mkOption {
11 | type = lib.types.bool;
12 | default = false;
13 | description = "Whether this is the default subdomain.";
14 | };
15 | useLongerTimeout = lib.mkOption {
16 | type = lib.types.bool;
17 | default = false;
18 | description = "Whether to use longer proxy timeout settings for this subdomain.";
19 | };
20 | };
21 | };
22 |
23 | cfg = config.services.nginx.subdomains;
24 |
25 | # Get all subdomain names where default = true
26 | defaultSubdomains = lib.filterAttrs (_: v: v.default) cfg;
27 | in
28 | {
29 | options.services.nginx.subdomains = lib.mkOption {
30 | type = lib.types.attrsOf subdomainType;
31 | description = ''
32 | Proxy the given subdomain to the specified port.
33 | Configure with an attribute set containing port and optional settings.
34 | '';
35 | example = {
36 | "api" = {
37 | port = 8080;
38 | useLongerTimeout = true;
39 | };
40 | "myapp" = {
41 | port = 9090;
42 | default = true;
43 | };
44 | };
45 | };
46 |
47 | config = lib.mkIf (cfg != { }) {
48 | assertions = [
49 | {
50 | assertion = (lib.length (lib.attrNames defaultSubdomains)) <= 1;
51 | message = "Only one subdomain may have default = true.";
52 | }
53 | ];
54 |
55 | networking.firewall = {
56 | allowedTCPPorts = [
57 | 80
58 | 443
59 | ];
60 | allowedUDPPorts = [
61 | 80
62 | 443
63 | ];
64 | };
65 | age.secrets.cloudflare_certbot_token.file = ../../../secrets/cloudflare_certbot_token.age;
66 | services.nginx = {
67 | enable = true;
68 | recommendedProxySettings = true;
69 | recommendedTlsSettings = true;
70 | proxyTimeout = "180s";
71 |
72 | virtualHosts = lib.foldl' (
73 | acc: subdomain:
74 | acc
75 | // {
76 | "${subdomain}.mjones.network" = {
77 | inherit (cfg.${subdomain}) default;
78 | forceSSL = true;
79 | useACMEHost = "mjones.network";
80 | locations."/" = {
81 | proxyPass = "http://127.0.0.1:${toString cfg.${subdomain}.port}";
82 | proxyWebsockets = true;
83 | extraConfig = lib.optionalString cfg.${subdomain}.useLongerTimeout ''
84 | proxy_read_timeout 120s;
85 | proxy_connect_timeout 60s;
86 | proxy_send_timeout 60s;
87 | '';
88 | };
89 | };
90 | }
91 | ) { } (lib.attrNames cfg);
92 | };
93 | security.acme = {
94 | acceptTerms = true;
95 | certs."mjones.network" = {
96 | inherit (config.services.nginx) group;
97 | email = "certs@mjones.network";
98 | domain = "*.mjones.network";
99 | dnsProvider = "cloudflare";
100 | dnsResolver = "1.1.1.1:53";
101 | environmentFile = config.age.secrets.cloudflare_certbot_token.path;
102 | };
103 | };
104 | };
105 | }
106 |
--------------------------------------------------------------------------------
/hosts/server/observability.nix:
--------------------------------------------------------------------------------
1 | { config, lib, ... }:
2 | let
3 | homarrStateDir = "/var/lib/homarr";
4 | homarrPort = 9090;
5 | dashdotPort = 9091;
6 | gatusPort = 3001;
7 |
8 | # dynamically configure gatus status pages
9 | toTitleCase =
10 | str:
11 | let
12 | firstChar = builtins.substring 0 1 str;
13 | restChars = builtins.substring 1 (builtins.stringLength str) str;
14 | in
15 | lib.strings.toUpper firstChar + restChars;
16 | applyOverrides =
17 | overrides: subdomain:
18 | if lib.hasAttr subdomain overrides then overrides.${subdomain} else toTitleCase subdomain;
19 | nginxSubdomains = config.services.nginx.subdomains;
20 | subdomainOverrides = {
21 | # By default it will just capitalize the first letter
22 | # of the subdomain. Customize subdomain -> Title mapping here.
23 | qbittorrent = "qBitTorrent";
24 | home = "Home Assistant";
25 | };
26 |
27 | # Generate the Gatus endpoints configuration
28 | gatusEndpoints = lib.attrsets.mapAttrsToList (
29 | name: _:
30 | let
31 | title = applyOverrides subdomainOverrides name;
32 | in
33 | {
34 | name = title;
35 | url = "https://${name}.mjones.network";
36 | interval = "2m";
37 | conditions = [
38 | "[STATUS] == 200"
39 | "[RESPONSE_TIME] < 1500"
40 | ];
41 | alerts = [
42 | {
43 | enabled = true;
44 | type = "discord";
45 | failure-threshold = 4;
46 | success-threshold = 2;
47 | send-on-resolved = true;
48 | description = title;
49 | }
50 | ];
51 | }
52 | ) nginxSubdomains;
53 | in
54 | {
55 | services = {
56 | nginx.subdomains = {
57 | uptime.port = gatusPort;
58 | dashdot.port = dashdotPort;
59 | glances.port = config.services.glances.port;
60 | homarr = {
61 | port = homarrPort;
62 | default = true;
63 | };
64 | };
65 |
66 | gatus = {
67 | enable = true;
68 | environmentFile = config.age.secrets.gatus_discord_webhook_env.path;
69 | settings = {
70 | web.port = gatusPort;
71 | endpoints = gatusEndpoints;
72 | alerting.discord.webhook-url = "\${DISCORD_WEBHOOK_URL}";
73 | };
74 | };
75 |
76 | glances.enable = true;
77 | };
78 |
79 | age.secrets.gatus_discord_webhook_env.file = ../../secrets/gatus_discord_webhook_env.age;
80 |
81 | systemd.tmpfiles.rules = [ "d ${homarrStateDir} 0750 root root -" ];
82 | age.secrets.homarr_env.file = ../../secrets/homarr_env.age;
83 | virtualisation.oci-containers.containers.homarr = {
84 | autoStart = true;
85 | image = "ghcr.io/homarr-labs/homarr:latest";
86 | ports = [ "${builtins.toString homarrPort}:7575" ];
87 | volumes = [ "${homarrStateDir}:/appdata" ];
88 | environment.DEFAULT_COLOR_SCHEME = "dark";
89 | environmentFiles = [ config.age.secrets.homarr_env.path ];
90 | };
91 |
92 | virtualisation.oci-containers.containers.dashdot = {
93 | image = "mauricenino/dashdot";
94 | ports = [ "${builtins.toString dashdotPort}:3001" ];
95 | volumes = [ "/:/mnt/host:ro" ];
96 | extraOptions = [ "--privileged=true" ];
97 | environment = {
98 | DASHDOT_PAGE_TITLE = "Dashboard";
99 | DASHDOT_USE_IMPERIAL = "true";
100 | DASHDOT_ALWAYS_SHOW_PERCENTAGES = "true";
101 | DASHDOT_OVERRIDE_OS = "NixOS";
102 | DASHDOT_OVERRIDE_ARCH = "x86";
103 | DASHDOT_CUSTOM_HOST = config.networking.hostName;
104 | DASHDOT_SHOW_HOST = "true";
105 | };
106 | };
107 | }
108 |
--------------------------------------------------------------------------------
/hosts/server/paperless.nix:
--------------------------------------------------------------------------------
1 | { config, ... }:
2 | {
3 | services.nginx.subdomains.paperless.port = config.services.paperless.port;
4 | age.secrets.paperless_admin_pw.file = ../../secrets/paperless_admin_pw.age;
5 | services.paperless = {
6 | enable = true;
7 | passwordFile = config.age.secrets.paperless_admin_pw.path;
8 | database.createLocally = true;
9 | settings.PAPERLESS_URL = "https://paperless.mjones.network";
10 | };
11 | }
12 |
--------------------------------------------------------------------------------
/hosts/server/torrent_client.nix:
--------------------------------------------------------------------------------
1 | { config, pkgs, ... }:
2 | let
3 | configDir = "/var/lib/qbittorrentvpn";
4 | wireguardConfigPath = config.age.secrets.mullvad_wireguard.path;
5 | qbittorrent_port = 8080;
6 | vuetorrent_port = 8081;
7 | tcpPorts = [
8 | qbittorrent_port
9 | 8118
10 | 9118
11 | 58946
12 | ];
13 | podman_network = "qbittorrent";
14 | in
15 | {
16 | age.secrets.mullvad_wireguard.file = ../../secrets/mullvad_wireguard.age;
17 | systemd.tmpfiles.rules = [
18 | "d ${configDir} 055 qbittorrentvpn qbittorrentvpn - -"
19 | "d ${configDir}/wireguard 055 qbittorrentvpn qbittorrentvpn - -"
20 | ];
21 | system.activationScripts.copyWireguardConfigIntoContainer.text = ''
22 | mkdir -p ${configDir}/wireguard && cp ${wireguardConfigPath} ${configDir}/wireguard/mullvad_wireguard.conf
23 | '';
24 | systemd.services.qbittorrent-podman-network-create = {
25 | serviceConfig.Type = "oneshot";
26 | wantedBy = [
27 | "podman-qbittorrentvpn.service"
28 | "podman-vuetorrent.service"
29 | ];
30 | script = ''
31 | ${pkgs.podman}/bin/podman network inspect ${podman_network} > /dev/null 2>&1 || ${pkgs.podman}/bin/podman network create ${podman_network}
32 | '';
33 | };
34 | services.nginx.subdomains.qbittorrent.port = qbittorrent_port;
35 | virtualisation.oci-containers.containers.qbittorrentvpn = {
36 | autoStart = true;
37 | image = "ghcr.io/binhex/arch-qbittorrentvpn";
38 | networks = [ podman_network ];
39 | extraOptions = [
40 | "--sysctl=net.ipv4.conf.all.src_valid_mark=1"
41 | "--privileged=true"
42 | ];
43 | ports = builtins.map (port: "${builtins.toString port}:${builtins.toString port}") tcpPorts;
44 | volumes = [
45 | "/mnt/jellyfin:/data"
46 | "${configDir}:/config"
47 | "/etc/localtime:/etc/localtime:ro"
48 | ];
49 | environment = {
50 | VPN_ENABLED = "yes";
51 | VPN_PROV = "custom";
52 | VPN_CLIENT = "wireguard";
53 | USERSPACE_WIREGUARD = "no";
54 | STRICT_PORT_FORWARD = "yes";
55 | ENABLE_PRIVOXY = "yes";
56 | LAN_NETWORK = "192.168.1.0/24";
57 | NAME_SERVERS = "1.1.1.1,1.0.0.1";
58 | ENABLE_STARTUP_SCRIPTS = "yes";
59 | ENABLE_SOCKS = "yes";
60 | VPN_INPUT_PORTS = "";
61 | VPN_OUTPUT_PORTS = "";
62 | DEBUG = "false";
63 | UMASK = "000";
64 | PUID = "0";
65 | PGID = "0";
66 | };
67 | };
68 | # prettier web UI
69 | services.nginx.subdomains.vuetorrent.port = vuetorrent_port;
70 | virtualisation.oci-containers.containers.vuetorrent = {
71 | autoStart = true;
72 | image = "ghcr.io/vuetorrent/vuetorrent-backend:latest";
73 | networks = [ podman_network ];
74 | ports = [ "${toString vuetorrent_port}:${toString vuetorrent_port}" ];
75 | environment = {
76 | PORT = toString vuetorrent_port;
77 | QBIT_BASE = "http://qbittorrentvpn:${toString qbittorrent_port}";
78 | RELEASE_TYPE = "stable";
79 | # every Sunday
80 | UPDATE_VT_CRON = "0 0 * * 0";
81 | };
82 | };
83 | }
84 |
--------------------------------------------------------------------------------
/hosts/server/vikunja.nix:
--------------------------------------------------------------------------------
1 | { config, ... }:
2 | {
3 | services.nginx.subdomains.vikunja.port = config.services.vikunja.port;
4 | services.vikunja = {
5 | enable = true;
6 | frontendScheme = "http";
7 | frontendHostname = import ./ip.nix;
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/hosts/server/wireguard.nix:
--------------------------------------------------------------------------------
1 | { pkgs, config, ... }:
2 | let
3 | wireguard_port = 9999;
4 | wireguard_interface = "wgvpn";
5 | external_interface = "enp0s31f6";
6 | in
7 | {
8 | age.secrets.wireguard_server.file = ../../secrets/wireguard_server.age;
9 | boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
10 | networking = {
11 | # Enable NAT
12 | nat = {
13 | enable = true;
14 | externalInterface = external_interface;
15 | internalInterfaces = [ wireguard_interface ];
16 | };
17 | firewall.allowedUDPPorts = [ wireguard_port ];
18 | wg-quick.interfaces = {
19 | # the network interface name. You can name the interface arbitrarily.
20 | "${wireguard_interface}" = {
21 | # Determines the IP address and subnet of the client's end of the tunnel interface
22 | address = [ "10.0.0.1/24" ];
23 | # The port that WireGuard listens to - recommended that this be changed from default
24 | listenPort = wireguard_port;
25 | # Path to the server's private key
26 | privateKeyFile = config.age.secrets.wireguard_server.path;
27 |
28 | # This allows the wireguard server to route your traffic to the internet and hence be like a VPN
29 | postUp = ''
30 | ${pkgs.iptables}/bin/iptables -A FORWARD -i ${wireguard_interface} -j ACCEPT
31 | ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.0.0.1/24 -o ${external_interface} -j MASQUERADE
32 | # Force all DNS traffic through local AdGuard Home
33 | ${pkgs.iptables}/bin/iptables -t nat -A PREROUTING -i ${wireguard_interface} -p udp --dport 53 -j DNAT --to-destination 10.0.0.1:53
34 | ${pkgs.iptables}/bin/iptables -t nat -A PREROUTING -i ${wireguard_interface} -p tcp --dport 53 -j DNAT --to-destination 10.0.0.1:53
35 | '';
36 |
37 | # Undo the above
38 | preDown = ''
39 | ${pkgs.iptables}/bin/iptables -D FORWARD -i ${wireguard_interface} -j ACCEPT
40 | ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.0.0.1/24 -o ${external_interface} -j MASQUERADE
41 |
42 | # Remove DNS redirection rules
43 | ${pkgs.iptables}/bin/iptables -t nat -D PREROUTING -i ${wireguard_interface} -p udp --dport 53 -j DNAT --to-destination 10.0.0.1:53
44 | ${pkgs.iptables}/bin/iptables -t nat -D PREROUTING -i ${wireguard_interface} -p tcp --dport 53 -j DNAT --to-destination 10.0.0.1:53
45 | '';
46 |
47 | peers = [
48 | {
49 | publicKey = "klCZN17QW/0ZtAlmj24R6ftBRozXUGn3hvWjF9ledC4=";
50 | allowedIPs = [ "10.0.0.2/32" ];
51 | }
52 | # Robby/Megan
53 | {
54 | publicKey = "0XXP3UgA67bcImCB4UOvyno3fhiBx7v6ufd4y4MH1xE=";
55 | allowedIPs = [ "10.0.0.3/32" ];
56 | }
57 | # Andrew
58 | {
59 | publicKey = "30hYSNSsFVjTmi4553kdbucg5laEkGvERgHxgqnDGkE=";
60 | allowedIPs = [ "10.0.0.4/32" ];
61 | }
62 | ];
63 | };
64 | };
65 | };
66 | }
67 |
--------------------------------------------------------------------------------
/nixos/_1password.nix:
--------------------------------------------------------------------------------
1 | {
2 | programs._1password.enable = true;
3 | programs._1password-gui = {
4 | enable = true;
5 | polkitPolicyOwners = [ "mat" ];
6 | };
7 | environment.etc."1password/custom_allowed_browsers" = {
8 | text = ''
9 | zen
10 | '';
11 | mode = "0755";
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/nixos/allowed-unfree.nix:
--------------------------------------------------------------------------------
1 | { lib, ... }:
2 | {
3 | nixpkgs.config.allowUnfreePredicate =
4 | pkg:
5 | let
6 | name = lib.getName pkg;
7 | in
8 | builtins.elem name [
9 | "spotify"
10 | "1password"
11 | "1password-cli"
12 | "steam"
13 | "steam-run"
14 | "steam-original"
15 | "steam-unwrapped"
16 | "parsec-bin"
17 | # This is required for pkgs.nodePackages_latest.vscode-langservers-extracted on NixOS
18 | # however VS Code should NOT be installed on this system!
19 | # Use VS Codium instead: https://github.com/VSCodium/vscodium
20 | "vscode"
21 | ];
22 | }
23 |
--------------------------------------------------------------------------------
/nixos/common.nix:
--------------------------------------------------------------------------------
1 | { pkgs, ... }:
2 | {
3 | imports = [ ../nixos/nix-conf.nix ];
4 |
5 | environment.systemPackages = [ pkgs.mullvad-vpn ];
6 |
7 | security.rtkit.enable = true;
8 | networking = {
9 | wireguard.enable = true;
10 | firewall = {
11 | enable = true;
12 | allowPing = true;
13 | };
14 | };
15 | # enable mDNS for stuff like NAS and SSH
16 | services.avahi = {
17 | enable = true;
18 | nssmdns4 = true;
19 | openFirewall = true;
20 | publish = {
21 | enable = true;
22 | userServices = true;
23 | };
24 | };
25 | services = {
26 | mullvad-vpn.enable = true;
27 | # going to use pipewire instead
28 | pulseaudio.enable = false;
29 |
30 | # Configure keymap in X11
31 | xserver = {
32 | xkb = {
33 | layout = "us";
34 | variant = "";
35 | };
36 | };
37 |
38 | # Enable CUPS to print documents.
39 | printing.enable = true;
40 | pipewire = {
41 | enable = true;
42 | alsa.enable = true;
43 | alsa.support32Bit = true;
44 | pulse.enable = true;
45 | # If you want to use JACK applications, uncomment this
46 | #jack.enable = true;
47 |
48 | # use the example session manager (no others are packaged yet so this is enabled by default,
49 | # no need to redefine it in your config for now)
50 | #media-session.enable = true;
51 | };
52 | };
53 |
54 | boot = {
55 | loader = {
56 | # bootloader
57 | systemd-boot.enable = true;
58 | efi.canTouchEfiVariables = true;
59 | };
60 | };
61 |
62 | systemd.extraConfig = ''
63 | DefaultTimeoutStopSec=10s
64 | '';
65 | systemd.user.extraConfig = ''
66 | DefaultTimeoutStopSec=10s
67 | '';
68 |
69 | security = {
70 | sudo.enable = true;
71 | pam = {
72 | sshAgentAuth.enable = true;
73 | # sshAgentAuth.authorizedKeysFiles =
74 | # lib.mkForce [ "/etc/ssh/authorized_keys.d/%u" ];
75 | services.sudo.sshAgentAuth = true;
76 | };
77 | };
78 |
79 | # Set your time zone.
80 | time.timeZone = "America/New_York";
81 |
82 | # Select internationalisation properties.
83 | i18n.defaultLocale = "en_US.UTF-8";
84 |
85 | i18n.extraLocaleSettings = {
86 | LC_ADDRESS = "en_US.UTF-8";
87 | LC_IDENTIFICATION = "en_US.UTF-8";
88 | LC_MEASUREMENT = "en_US.UTF-8";
89 | LC_MONETARY = "en_US.UTF-8";
90 | LC_NAME = "en_US.UTF-8";
91 | LC_NUMERIC = "en_US.UTF-8";
92 | LC_PAPER = "en_US.UTF-8";
93 | LC_TELEPHONE = "en_US.UTF-8";
94 | LC_TIME = "en_US.UTF-8";
95 | };
96 |
97 | users = {
98 | mutableUsers = false;
99 | users = {
100 | mat = {
101 | shell = pkgs.fish;
102 | isNormalUser = true;
103 | # generated by `mkpasswd`
104 | hashedPassword = "$y$j9T$L.RrmE3CRSB.lQayiw2ZN/$vA4XkSR13yL016t3HaZ11uCN/sCmXqBcuUcSBxMjiPD";
105 | home = "/home/mat";
106 | extraGroups = [
107 | "wheel"
108 | "networkmanager"
109 | "oci"
110 | "podman"
111 | ];
112 | };
113 | };
114 | };
115 | }
116 |
--------------------------------------------------------------------------------
/nixos/desktop_environment.nix:
--------------------------------------------------------------------------------
1 | { pkgs, ... }:
2 | {
3 | nixpkgs.overlays = [
4 | # GNOME 46: triple-buffering-v4-46
5 | # See:
6 | # - https://nixos.wiki/wiki/GNOME#Dynamic_triple_buffering
7 | # - https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1441
8 | (final: prev: {
9 | gnome = prev.gnome.overrideScope (
10 | gnomeFinal: gnomePrev: {
11 | mutter = gnomePrev.mutter.overrideAttrs (old: {
12 | src = pkgs.fetchFromGitLab {
13 | domain = "gitlab.gnome.org";
14 | owner = "vanvugt";
15 | repo = "mutter";
16 | rev = "triple-buffering-v4-46";
17 | hash = "sha256-C2VfW3ThPEZ37YkX7ejlyumLnWa9oij333d5c4yfZxc=";
18 | };
19 | });
20 | }
21 | );
22 | })
23 | ];
24 |
25 | # Make electron apps detect wayland properly
26 | environment.sessionVariables.NIXOS_OZONE_WL = "1";
27 | services = {
28 | gnome.gnome-keyring.enable = true;
29 | displayManager.defaultSession = "gnome";
30 | desktopManager.gnome.enable = true;
31 | displayManager.gdm.enable = true;
32 | };
33 | environment = {
34 | # don't install GNOME crap like Contacts, Photos, etc.
35 | gnome.excludePackages = with pkgs; [
36 | gnome-photos
37 | gnome-tour
38 | cheese # webcam tool
39 | epiphany # web browser
40 | geary # email reader
41 | yelp # Help view
42 | seahorse # password/ssh manager, I use 1Password SSH
43 | gnome-calendar
44 | gnome-music
45 | gnome-characters
46 | tali # poker game
47 | iagno # go game
48 | hitori # sudoku game
49 | atomix # puzzle game
50 | gnome-contacts
51 | gnome-initial-setup
52 | gnome-software
53 | ];
54 | };
55 |
56 | programs.dconf.enable = true;
57 | }
58 |
--------------------------------------------------------------------------------
/nixos/nix-conf.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | lib,
4 | inputs,
5 | isLinux,
6 | ...
7 | }:
8 | {
9 | nix = {
10 | package = lib.mkDefault pkgs.lix;
11 | settings = {
12 | substituters = [
13 | "https://cache.nixos.org"
14 | "https://nix-community.cachix.org"
15 | ];
16 | trusted-public-keys = [
17 | "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
18 | "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
19 | ];
20 | trusted-users = [
21 | "root"
22 | "mat"
23 | ];
24 | keep-outputs = true;
25 | keep-derivations = true;
26 | auto-optimise-store = if isLinux then true else false; # https://github.com/NixOS/nix/issues/7273
27 |
28 | experimental-features = "nix-command flakes";
29 | };
30 | # enable `nix-shell -p nixpkgs#something` without using channels
31 | # also use the exact version of nixpkgs from the flake the system is built from
32 | # to avoid cache misses
33 | nixPath = lib.mkForce [ "nixpkgs=${inputs.nixpkgs}" ];
34 | registry.nixpkgs.flake = inputs.nixpkgs;
35 | };
36 | }
37 |
--------------------------------------------------------------------------------
/nixos/sshd.nix:
--------------------------------------------------------------------------------
1 | {
2 | services = {
3 | fail2ban.enable = true;
4 | openssh = {
5 | enable = true;
6 | settings = {
7 | # only allow SSH key auth
8 | PasswordAuthentication = false;
9 | PermitRootLogin = "no";
10 | AllowUsers = [ "mat" ];
11 | };
12 | };
13 | };
14 |
15 | users.users.mat.openssh.authorizedKeys.keys = [
16 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDsT6GLG7sY8YKX7JM+jqS3EAti3YMzwHKWViveqkZvu"
17 | ];
18 | }
19 |
--------------------------------------------------------------------------------
/nixos/theme.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs,
3 | pkgs,
4 | lib,
5 | config,
6 | isLinux,
7 | isServer,
8 | ...
9 | }:
10 | {
11 | imports = [ inputs.tokyonight.homeManagerModules.default ];
12 | home.sessionVariables.COLORSCHEME = "tokyonight";
13 | # enable globally for all supported programs
14 | tokyonight = {
15 | enable = true;
16 | style = "night";
17 | };
18 | programs.btop.settings = {
19 | color_theme = "tokyonight-night";
20 | theme_background = false;
21 | };
22 | # handle GTK themeing
23 | gtk = {
24 | enable = isLinux && !isServer;
25 | theme = {
26 | package = pkgs.tokyonight-gtk-theme.override {
27 | # macos style window buttons
28 | tweakVariants = [ "macos" ];
29 | };
30 | name = "Tokyonight-Dark";
31 | };
32 | };
33 |
34 | dconf.settings."org/gnome/shell/extensions/user-theme".name = "Tokyonight-Dark";
35 |
36 | xdg.configFile =
37 | { }
38 | // lib.optionalAttrs isLinux {
39 | "gtk-4.0/assets".source =
40 | "${config.gtk.theme.package}/share/themes/${config.gtk.theme.name}/gtk-4.0/assets";
41 | "gtk-4.0/gtk.css".source =
42 | "${config.gtk.theme.package}/share/themes/${config.gtk.theme.name}/gtk-4.0/gtk.css";
43 | "gtk-4.0/gtk-dark.css".source =
44 | "${config.gtk.theme.package}/share/themes/${config.gtk.theme.name}/gtk-4.0/gtk-dark.css";
45 | };
46 |
47 | # My Neovim Lua is not generated by Nix
48 | programs.neovim.tokyonight.enable = false;
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/nvim/.luacheckrc:
--------------------------------------------------------------------------------
1 | globals = {
2 | 'vim',
3 | 'dbg',
4 | }
5 |
--------------------------------------------------------------------------------
/nvim/.luarc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
3 | "Lua.workspace.checkThirdParty": false
4 | }
--------------------------------------------------------------------------------
/nvim/ftplugin/markdown.lua:
--------------------------------------------------------------------------------
1 | vim.api.nvim_set_option_value('wrap', true, { win = 0 })
2 | vim.api.nvim_set_option_value('linebreak', true, { win = 0 })
3 |
4 | -- set up autocmd to revert the above options when another filetype is focused
5 | vim.api.nvim_create_autocmd('BufWinEnter', {
6 | callback = function()
7 | if vim.bo.ft == 'markdown' then
8 | vim.api.nvim_set_option_value('wrap', true, { win = 0 })
9 | vim.api.nvim_set_option_value('linebreak', true, { win = 0 })
10 | else
11 | vim.api.nvim_set_option_value('wrap', false, { win = 0 })
12 | vim.api.nvim_set_option_value('linebreak', false, { win = 0 })
13 | end
14 | end,
15 | })
16 |
17 | local ok, otter = pcall(require, 'otter')
18 | if ok then
19 | otter.activate()
20 | end
21 |
--------------------------------------------------------------------------------
/nvim/ftplugin/nix.lua:
--------------------------------------------------------------------------------
1 | local ok, otter = pcall(require, 'otter')
2 | if ok then
3 | pcall(otter.activate)
4 | end
5 |
--------------------------------------------------------------------------------
/nvim/init.lua:
--------------------------------------------------------------------------------
1 | -- use Neovim's experimental Lua module loader that does byte-caching of Lua modules
2 | vim.loader.enable()
3 | ---Debug Lua stuff and print a nice debug message via `vim.inspect`.
4 | ---@param ... any
5 | _G.dbg = function(...)
6 | local info = debug.getinfo(2, 'S')
7 | local source = info.source:sub(2)
8 | source = vim.loop.fs_realpath(source) or source
9 | source = vim.fn.fnamemodify(source, ':~:.') .. ':' .. info.linedefined
10 | local what = { ... }
11 | if vim.islist(what) and vim.tbl_count(what) <= 1 then
12 | what = what[1]
13 | end
14 | local msg = vim.inspect(vim.deepcopy(what))
15 | vim.notify(msg, vim.log.levels.INFO, {
16 | title = 'Debug: ' .. source,
17 | on_open = function(win)
18 | vim.wo[win].conceallevel = 3
19 | vim.wo[win].concealcursor = ''
20 | vim.wo[win].spell = false
21 | local buf = vim.api.nvim_win_get_buf(win)
22 | vim.treesitter.start(buf, 'lua')
23 | end,
24 | })
25 | end
26 |
27 | require('my.settings')
28 | require('my.plugins')
29 |
30 | vim.api.nvim_create_user_command('H', function()
31 | vim.cmd.help(vim.fn.expand(''))
32 | end, { desc = 'Help for cursorword' })
33 |
34 | vim.api.nvim_create_autocmd('UiEnter', {
35 | callback = function()
36 | local bufs = vim.api.nvim_list_bufs()
37 | for _, buf in ipairs(bufs) do
38 | local bufname = vim.api.nvim_buf_get_name(buf)
39 | if #bufs == 1 and vim.fn.isdirectory(bufname) ~= 0 then
40 | -- if opened to a directory, cd to the directory
41 | vim.cmd.cd(bufname)
42 | break
43 | end
44 |
45 | if #bufname ~= 0 then
46 | return
47 | end
48 | end
49 | end,
50 | once = true,
51 | })
52 |
53 | -- if I'm editing my nvim config, make sure I'm `cd`d into `nvim`
54 | vim.api.nvim_create_autocmd('BufRead', {
55 | once = true,
56 | pattern = '*',
57 | callback = function()
58 | local bufname = vim.api.nvim_buf_get_name(0)
59 | if
60 | string.find(bufname, '/git/dotfiles/nvim') and not vim.endswith(vim.loop.cwd()--[[@as string]], '/nvim')
61 | then
62 | vim.cmd.cd('./nvim')
63 | end
64 | end,
65 | })
66 |
67 | -- open files to last location
68 | vim.api.nvim_create_autocmd('BufReadPost', {
69 | command = [[if line("'\"") >= 1 && line("'\"") <= line("$") && &ft !~# 'commit' | exe "normal! g`\"" | endif]],
70 | })
71 |
72 | -- custom URL handling to open GitHub shorthands
73 | local open = vim.ui.open
74 | vim.ui.open = function(uri) ---@diagnostic disable-line: duplicate-set-field
75 | -- GitHub shorthand pattern, e.g. mrjones2014/dotfiles
76 | if not string.match(uri, '[a-z]*://[^ >,;]*') and string.match(uri, '[%w%p\\-]*/[%w%p\\-]*') then
77 | uri = string.format('https://github.com/%s', uri)
78 | elseif
79 | vim.api.nvim_get_option_value('filetype', { buf = 0 }) == 'rust'
80 | and not string.match(uri, '^https?://[%w-_%.%?%.:/%+=&]+')
81 | then
82 | -- if the cursorword is not a URL, see if we can open the docs page with rustaceanvim
83 | vim.cmd.RustLsp('openDocs')
84 | return
85 | end
86 |
87 | open(uri)
88 | end
89 |
90 | -- if in SSH session, copy to local system clipboard using OSC52
91 | -- See: https://github.com/neovim/neovim/discussions/28010#discussioncomment-9529001
92 | if vim.env.SSH_TTY then
93 | vim.api.nvim_create_autocmd('TextYankPost', {
94 | callback = function()
95 | local copy_to_unnamedplus = require('vim.ui.clipboard.osc52').copy('+')
96 | copy_to_unnamedplus(vim.v.event.regcontents)
97 | local copy_to_unnamed = require('vim.ui.clipboard.osc52').copy('*')
98 | copy_to_unnamed(vim.v.event.regcontents)
99 | end,
100 | })
101 | end
102 |
103 | -- set up UI tweaks on load
104 | require('my.utils.lsp').apply_ui_tweaks()
105 |
--------------------------------------------------------------------------------
/nvim/lua/my/cmd-palette.lua:
--------------------------------------------------------------------------------
1 | ---@class PaletteEntry
2 | ---@field text string|fun(buf:number, win:number):string
3 | ---@field on_selected fun(buf:number, win:number)
4 |
5 | ---@type PaletteEntry[]
6 | return {
7 | {
8 | text = ' Copy relative filepath',
9 | on_selected = function(buf)
10 | local filepath = vim.api.nvim_buf_get_name(buf)
11 | if not filepath or #filepath == 0 then
12 | vim.notify('Could not expand filepath')
13 | return
14 | end
15 |
16 | local relpath = vim.fn.simplify(require('my.utils.path').relative(vim.fn.expand('%') --[[@as string]]))
17 | require('my.utils.clipboard').copy(relpath)
18 | vim.notify('Relative filepath copied to clipboard')
19 | end,
20 | },
21 | {
22 | text = ' Copy git branch name',
23 | on_selected = function(buf)
24 | local branch = vim.g.gitsigns_head or vim.b[buf].gitsigns_head
25 | if not branch or #branch == 0 then
26 | vim.notify('Could not determine git branch')
27 | return
28 | end
29 | require('my.utils.clipboard').copy(branch)
30 | end,
31 | },
32 | }
33 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/autopairs.lua:
--------------------------------------------------------------------------------
1 | return {
2 | {
3 | 'windwp/nvim-autopairs',
4 | event = { 'InsertEnter' },
5 | config = function()
6 | local Rule = require('nvim-autopairs.rule')
7 | local npairs = require('nvim-autopairs')
8 | local cond = require('nvim-autopairs.conds')
9 | npairs.setup({
10 | disable_filetype = { 'snacks_picker_input' },
11 | })
12 | -- <> pair for generics and stuff,
13 | -- complete <> if the preceding text is alphanumeric or :: for Rust
14 | npairs.add_rule(Rule('<', '>', {
15 | -- *exclude* these filetypes so that nvim-ts-autotag works instead
16 | '-html',
17 | '-javascriptreact',
18 | '-typescriptreact',
19 | }):with_pair(cond.before_regex('%a+:?:?$', 3)):with_move(function(opts)
20 | return opts.char == '>'
21 | end))
22 | end,
23 | },
24 | {
25 | 'windwp/nvim-ts-autotag',
26 | ft = { 'html', 'javascriptreact', 'typescriptreact' },
27 | opts = {},
28 | },
29 | }
30 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/colorizer.lua:
--------------------------------------------------------------------------------
1 | ---@diagnostic disable-next-line -- optional parameters omitted
2 | local filetypes = vim.list_extend(vim.deepcopy(require('my.lsp.filetypes').filetypes), { 'conf', 'tmux', 'Onedarkpro' })
3 |
4 | return {
5 | 'NvChad/nvim-colorizer.lua',
6 | ft = filetypes,
7 | cmd = 'ColorizerAttachToBuffer',
8 | opts = {
9 | filetypes = filetypes,
10 | user_default_options = {
11 | names = false,
12 | css = true,
13 | sass = { enable = true, parsers = { 'css' } },
14 | always_update = true,
15 | },
16 | },
17 | }
18 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/colorscheme.lua:
--------------------------------------------------------------------------------
1 | local function get_tokyonight_hl_fg(hl, group, guard)
2 | -- don't allow more than 5 levels of recursion
3 | guard = guard or 0
4 | if guard > 5 then
5 | return nil
6 | end
7 |
8 | if not hl[group] then
9 | return nil
10 | end
11 |
12 | if hl[group].link then
13 | return get_tokyonight_hl_fg(hl, hl[group].link, guard + 1)
14 | end
15 |
16 | return hl[group].fg
17 | end
18 |
19 | local colorscheme = vim.env.COLORSCHEME or 'tokyonight'
20 |
21 | return {
22 | 'folke/tokyonight.nvim',
23 | dependencies = {
24 | {
25 | 'folke/snacks.nvim',
26 | opts = {
27 | indent = {
28 | animate = { enabled = false },
29 | },
30 | },
31 | },
32 | },
33 | enabled = colorscheme == 'tokyonight',
34 | lazy = false,
35 | priority = 1000,
36 | opts = {
37 | style = 'night',
38 | dim_inactive = true,
39 | plugins = { auto = true },
40 | on_highlights = function(hl, c)
41 | hl.WinBar = { bg = c.fg_gutter }
42 | hl.WinBarNC = hl.WinBar
43 | -- navic in winbar
44 | hl.NavicText.bg = c.fg_gutter
45 | hl.NavicSeparator.bg = c.fg_gutter
46 | for key, _ in pairs(hl) do
47 | if vim.startswith(key, 'NavicIcons') then
48 | hl[key] = { bg = hl.WinBar.bg, fg = get_tokyonight_hl_fg(hl, key) }
49 | end
50 | end
51 | -- borderless pickers
52 | local prompt = '#2d3149'
53 | hl.SnacksPickerInput = {
54 | bg = prompt,
55 | fg = c.fg_dark,
56 | }
57 | hl.SnacksPickerInputBorder = {
58 | bg = prompt,
59 | fg = prompt,
60 | }
61 | hl.SnacksPickerPrompt = {
62 | bg = prompt,
63 | }
64 | hl.NormalFloat = {
65 | bg = c.bg,
66 | }
67 | hl.SnacksPickerBoxTitle = {
68 | bg = prompt,
69 | fg = prompt,
70 | }
71 | hl.SnacksPickerBoxBorder = hl.SnacksPickerBoxTitle
72 | hl.SnacksPickerBorder = {
73 | bg = c.bg,
74 | fg = c.bg,
75 | }
76 | hl.SnacksPickerPreviewTitle = hl.SnacksPickerBorder
77 | hl.SnacksPickerResultsTitle = hl.SnacksPickerBorder
78 | hl.SnacksPickerListTitle = hl.SnacksPickerBorder
79 | end,
80 | },
81 | config = function(_, opts)
82 | require('tokyonight').setup(opts)
83 | vim.cmd.colorscheme('tokyonight-night')
84 | end,
85 | }
86 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/comments.lua:
--------------------------------------------------------------------------------
1 | return {
2 | {
3 | 'folke/todo-comments.nvim',
4 | event = 'BufRead',
5 | opts = {
6 | -- See result with below comments
7 | -- TODO a todo message
8 | -- FIX Fix me
9 | -- BUG this is a bug
10 | -- PERF performance note
11 | -- NOTE just a note
12 | -- HACK this is a hack
13 | -- WARN this is a warning
14 | -- WARNING this is also a warning
15 | --
16 | -- TODO with a very long
17 | -- multiline comment
18 | --
19 | -- SAFETY: a safety comment for a Rust `unsafe { }` block
20 | --
21 | -- NB: nota bene
22 | highlight = {
23 | -- change pattern to not require a colon after the keyword
24 | pattern = [[.*<(KEYWORDS)\s*]],
25 | keyword = 'bg',
26 | comments_only = true,
27 | },
28 | search = { pattern = [[.*<(KEYWORDS)\s*]] },
29 | keywords = {
30 | SAFETY = { icon = ' ', color = 'warning' },
31 | NB = { icon = ' ', color = 'info' },
32 | },
33 | },
34 | },
35 | {
36 | 'numToStr/Comment.nvim',
37 | event = 'BufRead',
38 | dependencies = { 'JoosepAlviste/nvim-ts-context-commentstring' },
39 | config = function()
40 | -- default mappings:
41 | -- {
42 | -- toggler = {
43 | -- ---Line-comment toggle keymap
44 | -- line = 'gcc',
45 | -- ---Block-comment toggle keymap
46 | -- block = 'gbc',
47 | -- },
48 | -- ---LHS of operator-pending mappings in NORMAL and VISUAL mode
49 | -- opleader = {
50 | -- ---Line-comment keymap
51 | -- line = 'gc',
52 | -- ---Block-comment keymap
53 | -- block = 'gb',
54 | -- },
55 | -- ---LHS of extra mappings
56 | -- extra = {
57 | -- ---Add comment on the line above
58 | -- above = 'gcO',
59 | -- ---Add comment on the line below
60 | -- below = 'gco',
61 | -- ---Add comment at the end of line
62 | -- eol = 'gcA',
63 | -- }
64 | -- }
65 |
66 | require('Comment').setup({ ---@diagnostic disable-line: missing-fields
67 | pre_hook = require('ts_context_commentstring.integrations.comment_nvim').create_pre_hook(),
68 | })
69 | require('Comment.ft').mysql = { '# %s', '/* %s */' }
70 | end,
71 | },
72 | }
73 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/completion.lua:
--------------------------------------------------------------------------------
1 | return {
2 | 'saghen/blink.cmp',
3 | version = '*',
4 | dependencies = {
5 | {
6 | 'L3MON4D3/LuaSnip',
7 | version = 'v2.*',
8 | keys = {
9 | {
10 | '',
11 | function()
12 | require('luasnip').jump(-1)
13 | end,
14 | mode = { 'i', 's' },
15 | desc = 'Jump to previous snippet node',
16 | },
17 | {
18 | '',
19 | function()
20 | local ls = require('luasnip')
21 | if ls.expand_or_jumpable() then
22 | ls.expand_or_jump()
23 | end
24 | end,
25 | mode = { 'i', 's' },
26 | desc = 'Expand or jump to next snippet node',
27 | },
28 | {
29 | '',
30 | function()
31 | local ls = require('luasnip')
32 | if ls.choice_active() then
33 | ls.change_choice(-1)
34 | end
35 | end,
36 | mode = { 'i', 's' },
37 | desc = 'Select previous choice in snippet choice nodes',
38 | },
39 | {
40 | '',
41 | function()
42 | local ls = require('luasnip')
43 | if ls.choice_active() then
44 | ls.change_choice(1)
45 | end
46 | end,
47 | mode = { 'i', 's' },
48 | desc = 'Select next choice in snippet choice nodes',
49 | },
50 | {
51 | '',
52 | function()
53 | require('luasnip').unlink_current()
54 | end,
55 | mode = { 'i', 'n' },
56 | desc = 'Clear snippet jumps',
57 | },
58 | },
59 | },
60 | { 'folke/lazydev.nvim' },
61 | },
62 | opts = {
63 | enabled = function()
64 | return not vim.tbl_contains({ 'minifiles' }, vim.bo.filetype)
65 | end,
66 | snippets = { preset = 'luasnip' },
67 | signature = { enabled = true },
68 | keymap = {
69 | preset = 'enter',
70 | [''] = { 'select_next', 'fallback' },
71 | [''] = { 'select_prev', 'fallback' },
72 | [''] = {},
73 | [''] = {},
74 | },
75 | completion = {
76 | menu = {
77 | draw = {
78 | columns = {
79 | { 'label', 'label_description', gap = 1 },
80 | { 'kind_icon', 'kind' },
81 | },
82 | },
83 | },
84 | documentation = { auto_show = true, auto_show_delay_ms = 0 },
85 | ghost_text = { enabled = true },
86 | },
87 | cmdline = {
88 | keymap = {
89 | [''] = {},
90 | [''] = { 'accept', 'fallback' },
91 | },
92 | completion = {
93 | menu = { auto_show = true },
94 | },
95 | },
96 | sources = {
97 | default = { 'lazydev', 'lsp', 'path', 'snippets', 'buffer' },
98 | providers = {
99 | lazydev = {
100 | name = 'LazyDev',
101 | module = 'lazydev.integrations.blink',
102 | -- boost lazydev suggestions to top
103 | score_offset = 100,
104 | },
105 | },
106 | },
107 | },
108 | }
109 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/flash.lua:
--------------------------------------------------------------------------------
1 | return {
2 | 'folke/flash.nvim',
3 | keys = {
4 | {
5 | 's',
6 | function()
7 | require('flash').jump()
8 | end,
9 | mode = { 'n', 'x', 'o' },
10 | desc = 'Jump forwards',
11 | },
12 | {
13 | 'S',
14 | function()
15 | require('flash').jump({ search = { forward = false } })
16 | end,
17 | mode = { 'n', 'x', 'o' },
18 | desc = 'Jump backwards',
19 | },
20 | },
21 | opts = {
22 | jump = { nohlsearch = true },
23 | },
24 | }
25 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/git.lua:
--------------------------------------------------------------------------------
1 | local clipboard = require('my.utils.clipboard')
2 | return {
3 | {
4 | 'folke/snacks.nvim',
5 | keys = {
6 | {
7 | 'gy',
8 | function()
9 | clipboard.copy(require('snacks.gitbrowse').get_url())
10 | end,
11 | desc = 'Copy git permalink',
12 | silent = true,
13 | },
14 | },
15 | opts = {
16 | gitbrowse = {
17 | what = 'permalink',
18 | url_patterns = {
19 | ['gitlab%.1password%.io'] = {
20 | branch = '/-/tree/{branch}',
21 | file = '/-/blob/{branch}/{file}#L{line_start}-L{line_end}',
22 | permalink = '/-/blob/{commit}/{file}#L{line_start}-L{line_end}',
23 | commit = '/-/commit/{commit}',
24 | },
25 | },
26 | },
27 | },
28 | },
29 | {
30 | 'lewis6991/gitsigns.nvim',
31 | lazy = false,
32 | keys = {
33 | {
34 | 'bl',
35 | function()
36 | vim.cmd.Gitsigns('toggle_current_line_blame')
37 | end,
38 | desc = 'Toggle inline git blame',
39 | },
40 | },
41 | opts = {
42 | current_line_blame = false,
43 | current_line_blame_opts = {
44 | virt_text = true,
45 | virt_text_pos = 'eol',
46 | delay = 100,
47 | },
48 | signcolumn = true,
49 | current_line_blame_formatter = ' | , - ',
50 | on_attach = function()
51 | vim.cmd.redrawstatus()
52 | end,
53 | },
54 | },
55 | {
56 | 'sindrets/diffview.nvim',
57 | cmd = {
58 | 'DiffviewLog',
59 | 'DiffviewOpen',
60 | 'DiffviewClose',
61 | 'DiffviewRefresh',
62 | 'DiffviewFocusFiles',
63 | 'DiffviewFileHistory',
64 | 'DiffviewToggleFiles',
65 | },
66 | opts = {
67 | enhanced_diff_hl = true,
68 | view = {
69 | file_panel = {
70 | win_config = {
71 | position = 'right',
72 | },
73 | },
74 | },
75 | },
76 | },
77 | {
78 | 'pwntester/octo.nvim',
79 | cmd = 'Octo',
80 | opts = {
81 | gh_env = function()
82 | local github_token = require('op').get_secret('GitHub', 'token')
83 | if not github_token or not vim.startswith(github_token, 'ghp_') then
84 | error('Failed to get GitHub token.')
85 | end
86 |
87 | return { GITHUB_TOKEN = github_token }
88 | end,
89 | },
90 | },
91 | }
92 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/grug_far.lua:
--------------------------------------------------------------------------------
1 | return {
2 | 'MagicDuck/grug-far.nvim',
3 | cmd = { 'GrugFar', 'GrugFarWithin' },
4 | keys = {
5 | {
6 | 's',
7 | function()
8 | require('grug-far').open({})
9 | end,
10 | mode = 'n',
11 | desc = 'Search and replace',
12 | },
13 | {
14 | 'f',
15 | function()
16 | require('grug-far').open({ prefills = { paths = vim.fn.expand('%') } })
17 | end,
18 | desc = 'Search and replace in current file',
19 | },
20 | {
21 | 's',
22 | function()
23 | require('grug-far').open({ visualSelectionUsage = 'operate-within-range' })
24 | end,
25 | mode = 'v',
26 | desc = 'Search and replace visual selection',
27 | },
28 | },
29 | }
30 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/heirline/conditions.lua:
--------------------------------------------------------------------------------
1 | local M = {}
2 |
3 | function M.should_show_filename(bufname)
4 | local bt = vim.api.nvim_get_option_value('buftype', { buf = 0 })
5 | local ft = vim.api.nvim_get_option_value('filetype', { buf = 0 })
6 | return (not bt or bt == '')
7 | and ft ~= 'nofile'
8 | and ft ~= 'Trouble'
9 | and ft ~= 'snacks_picker_input'
10 | and ft ~= 'help'
11 | and bufname
12 | and bufname ~= ''
13 | end
14 |
15 | function M.is_floating_window(win_id)
16 | win_id = win_id or 0
17 | local win_cfg = vim.api.nvim_win_get_config(win_id)
18 | return win_cfg and (win_cfg.relative ~= '' or not win_cfg.relative)
19 | end
20 |
21 | return M
22 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/heirline/init.lua:
--------------------------------------------------------------------------------
1 | return {
2 | {
3 | 'SmiteshP/nvim-navic',
4 | init = function()
5 | require('my.utils.lsp').on_attach(function(client, bufnr)
6 | if
7 | client.server_capabilities.documentSymbolProvider
8 | and client.name ~= 'nil_ls' -- don't attach to both nil_ls and nixd
9 | and not vim.startswith(client.name, 'otter-ls') -- don't attach to otter-ls
10 | then
11 | require('nvim-navic').attach(client, bufnr)
12 | end
13 | end)
14 | end,
15 | config = function()
16 | require('nvim-navic').setup({
17 | highlight = true,
18 | separator = ' ',
19 | })
20 | end,
21 | },
22 | {
23 | 'rebelot/heirline.nvim',
24 | lazy = false,
25 | config = function()
26 | local shared = require('my.configure.heirline.shared')
27 | local sl = require('my.configure.heirline.statusline')
28 | local wb = require('my.configure.heirline.winbar')
29 |
30 | local colors = require('tokyonight.colors').setup()
31 | require('heirline').setup({
32 | opts = {
33 | colors = {
34 | black = colors.bg_dark,
35 | gray = colors.dark5,
36 | green = colors.green,
37 | blue = colors.blue,
38 | yellow = colors.terminal.yellow_bright,
39 | base = colors.bg,
40 | surface0 = colors.fg_gutter,
41 | surface1 = colors.dark3,
42 | surface2 = colors.blue7,
43 | },
44 | disable_winbar_cb = function()
45 | local conditions = require('my.configure.heirline.conditions')
46 | return conditions.is_floating_window() or not conditions.should_show_filename(vim.api.nvim_buf_get_name(0))
47 | end,
48 | },
49 | statusline = { ---@diagnostic disable-line:missing-fields
50 | sl.Mode,
51 | sl.Branch,
52 | shared.FileIcon('surface0'),
53 | sl.FilePath,
54 | sl.Align,
55 | sl.UnsavedChanges,
56 | sl.Align,
57 | sl.RecordingMacro,
58 | sl.SpellCheckToggle,
59 | sl.LspFormatToggle,
60 | sl.LazyStats,
61 | shared.Diagnostics(false),
62 | },
63 | winbar = { ---@diagnostic disable-line:missing-fields
64 | shared.FileIcon('base'),
65 | wb.UniqueFilename,
66 | wb.Diagnostics,
67 | shared.Trunc,
68 | wb.Navic,
69 | },
70 | })
71 | end,
72 | },
73 | }
74 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/heirline/separators.lua:
--------------------------------------------------------------------------------
1 | return {
2 | rounded_left = '',
3 | rounded_right = '',
4 | }
5 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/heirline/shared.lua:
--------------------------------------------------------------------------------
1 | local utils = require('heirline.utils')
2 |
3 | local M = {}
4 |
5 | function M.FileIcon(bg_color)
6 | return {
7 | init = function(self)
8 | self.bufname = vim.api.nvim_buf_get_name(0)
9 | local icon, hl = require('nvim-web-devicons').get_icon(self.bufname, vim.fn.fnamemodify(self.bufname, ':e'))
10 | self.icon = icon
11 | self.icon_hl = hl
12 | end,
13 | provider = ' ',
14 | hl = { bg = bg_color },
15 | {
16 | condition = function(self)
17 | return self.icon ~= nil
18 | and self.icon_hl ~= nil
19 | and require('my.configure.heirline.conditions').should_show_filename(self.bufname)
20 | end,
21 | provider = function(self)
22 | return self.icon
23 | end,
24 | hl = function(self)
25 | return { fg = utils.get_highlight(self.icon_hl).fg, bg = bg_color }
26 | end,
27 | },
28 | }
29 | end
30 |
31 | local diagnostics_order = {
32 | vim.diagnostic.severity.HINT,
33 | vim.diagnostic.severity.INFO,
34 | vim.diagnostic.severity.WARN,
35 | vim.diagnostic.severity.ERROR,
36 | }
37 | local severity_name = {
38 | [vim.diagnostic.severity.HINT] = 'Hint',
39 | [vim.diagnostic.severity.INFO] = 'Info',
40 | [vim.diagnostic.severity.WARN] = 'Warn',
41 | [vim.diagnostic.severity.ERROR] = 'Error',
42 | }
43 | local diagnostics_base = {
44 | update = { 'DiagnosticChanged', 'BufEnter' },
45 | init = function(self)
46 | self.counts = vim.diagnostic.count(0)
47 | end,
48 | }
49 |
50 | function M.Diagnostics(is_winbar, bg)
51 | bg = bg or 'surface0'
52 | return utils.insert(
53 | diagnostics_base,
54 | unpack(vim
55 | .iter(diagnostics_order)
56 | :map(function(severity)
57 | local component = {
58 | provider = function(self)
59 | local sign = vim.diagnostic.config().signs.text[severity]
60 | return string.format('%s%s ', sign, self.counts[severity] or 0)
61 | end,
62 | hl = function()
63 | return { fg = utils.get_highlight(string.format('DiagnosticSign%s', severity_name[severity])).fg, bg = bg }
64 | end,
65 | }
66 | if is_winbar then
67 | component.condition = function(self)
68 | return (self.counts[severity] or 0) > 0
69 | end
70 | end
71 | return component
72 | end)
73 | :totable())
74 | )
75 | end
76 |
77 | M.Trunc = {
78 | provider = '%<',
79 | }
80 |
81 | return M
82 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/heirline/winbar.lua:
--------------------------------------------------------------------------------
1 | local conditions = require('heirline.conditions')
2 | local sep = require('my.configure.heirline.separators')
3 |
4 | local special_filenames = { 'mod.rs', 'lib.rs' }
5 |
6 | local function get_current_filenames()
7 | local listed_buffers = vim
8 | .iter(vim.api.nvim_list_bufs())
9 | :filter(function(bufnr)
10 | return vim.bo[bufnr].buflisted and vim.api.nvim_buf_is_loaded(bufnr)
11 | end)
12 | :totable()
13 |
14 | return vim.iter(listed_buffers):map(vim.api.nvim_buf_get_name):totable()
15 | end
16 |
17 | -- Get unique name for the current buffer
18 | local function get_unique_filename(filename)
19 | local filenames = vim
20 | .iter(get_current_filenames())
21 | :filter(function(filename_other)
22 | return filename_other ~= filename
23 | end)
24 | :map(string.reverse)
25 | :totable() -- Reverse filenames in order to compare their names
26 | filename = string.reverse(filename)
27 |
28 | local index
29 |
30 | -- For every other filename, compare it with the name of the current file char-by-char to
31 | -- find the minimum index `i` where the i-th character is different for the two filenames
32 | -- After doing it for every filename, get the maximum value of `i`
33 | if next(filenames) then
34 | index = math.max(unpack(vim
35 | .iter(filenames)
36 | :map(function(filename_other)
37 | for i = 1, #filename do
38 | -- Compare i-th character of both names until they aren't equal
39 | if filename:sub(i, i) ~= filename_other:sub(i, i) then
40 | return i
41 | end
42 | end
43 | return 1
44 | end)
45 | :totable()))
46 | else
47 | index = 1
48 | end
49 |
50 | -- Iterate backwards (since filename is reversed) until a "/" is found
51 | -- in order to show a valid file path
52 | while index <= #filename do
53 | if filename:sub(index, index) == '/' then
54 | index = index - 1
55 | break
56 | end
57 |
58 | index = index + 1
59 | end
60 |
61 | local result = string.reverse(string.sub(filename, 1, index))
62 | -- for special filenames like `lib.rs`, `mod.rs`, `index.ts` etc.
63 | -- always show at least one parent
64 | if vim.iter(ipairs(special_filenames)):any(function(_, special)
65 | return special == result
66 | end) then
67 | local parts = vim.split(string.reverse(filename), '/')
68 | -- if parent is just `src` then show another parent
69 | if parts[#parts - 1] == 'src' then
70 | return table.concat({ parts[#parts - 2], parts[#parts - 1], parts[#parts] }, '/')
71 | end
72 | return table.concat({ parts[#parts - 1], parts[#parts] }, '/')
73 | end
74 | return result
75 | end
76 |
77 | local M = {}
78 |
79 | M.UniqueFilename = {
80 | init = function(self)
81 | self.bufname = get_unique_filename(vim.api.nvim_buf_get_name(0))
82 | end,
83 | {
84 | condition = function(self)
85 | return require('my.configure.heirline.conditions').should_show_filename(self.bufname)
86 | end,
87 | provider = function(self)
88 | return string.format(' %s ', self.bufname)
89 | end,
90 | hl = { bg = 'base' },
91 | },
92 | {
93 | -- file save status indicator
94 | condition = function()
95 | return vim.bo.modified == true
96 | end,
97 | provider = ' ',
98 | hl = { bg = 'base' },
99 | },
100 | {
101 | provider = sep.rounded_right,
102 | hl = function()
103 | return { bg = conditions.has_diagnostics() and 'surface1' or 'surface2', fg = 'base' }
104 | end,
105 | },
106 | }
107 |
108 | M.Diagnostics = {
109 | provider = ' ',
110 | hl = { bg = 'surface1' },
111 | condition = conditions.has_diagnostics,
112 | require('my.configure.heirline.shared').Diagnostics(true, 'surface1'),
113 | {
114 | provider = sep.rounded_right,
115 | hl = { fg = 'surface1', bg = 'surface2' },
116 | },
117 | }
118 |
119 | M.Navic = {
120 | condition = function()
121 | return conditions.lsp_attached() and require('nvim-navic').is_available()
122 | end,
123 | provider = function()
124 | return string.format(' %s', require('nvim-navic').get_location())
125 | end,
126 | hl = { bg = 'surface0' },
127 | }
128 |
129 | return M
130 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/init.lua:
--------------------------------------------------------------------------------
1 | -- plugins with little or no config can go here
2 |
3 | return {
4 | { 'nvim-lua/plenary.nvim' },
5 | {
6 | 'DaikyXendo/nvim-material-icon',
7 | main = 'nvim-web-devicons',
8 | opts = {},
9 | },
10 | { 'tpope/vim-eunuch', cmd = { 'Delete', 'Move', 'Chmod', 'SudoWrite', 'Rename' } },
11 | { 'tpope/vim-sleuth', event = 'BufReadPre' },
12 | {
13 | 'nat-418/boole.nvim',
14 | keys = { '', '' },
15 | opts = { mappings = { increment = '', decrement = '' } },
16 | },
17 | {
18 | 'mrjones2014/iconpicker.nvim',
19 | cmds = { 'Icons' },
20 | init = function()
21 | vim.api.nvim_create_user_command('Icons', function()
22 | require('iconpicker').pick(function(icon)
23 | if not icon or #icon == 0 then
24 | return
25 | end
26 |
27 | require('my.utils.clipboard').copy(icon)
28 | vim.notify('Copied icon to clipboard.', vim.log.levels.INFO)
29 | end)
30 | end, { desc = 'Pick NerdFont icons and copy to clipboard' })
31 | end,
32 | },
33 | { 'mrjones2014/lua-gf.nvim', dev = true, ft = 'lua' },
34 | {
35 | 'echasnovski/mini.splitjoin',
36 | keys = {
37 | {
38 | 'gS',
39 | function()
40 | require('mini.splitjoin').toggle()
41 | end,
42 | desc = 'Split/join arrays, argument lists, etc. from one vs. multiline and vice versa',
43 | },
44 | },
45 | },
46 | { 'echasnovski/mini.trailspace', event = 'BufRead', opts = {} },
47 | {
48 | 'max397574/better-escape.nvim',
49 | event = 'InsertEnter',
50 | opts = {
51 | mappings = {
52 | -- do not map jj because I use jujutsu and the command is jj
53 | i = {
54 | j = { k = '', j = false },
55 | k = { k = '', j = '' },
56 | },
57 | c = {
58 | j = { k = '', j = false },
59 | k = { k = '', j = '' },
60 | },
61 | },
62 | },
63 | },
64 | {
65 | 'saecki/crates.nvim',
66 | event = { 'BufRead Cargo.toml' },
67 | opts = {},
68 | },
69 | {
70 | 'folke/lazy.nvim',
71 | lazy = false,
72 | keys = {
73 | {
74 | 'L',
75 | function()
76 | vim.cmd.Lazy()
77 | end,
78 | },
79 | },
80 | },
81 | }
82 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/markdown.lua:
--------------------------------------------------------------------------------
1 | return {
2 | {
3 | 'MeanderingProgrammer/markdown.nvim',
4 | main = 'render-markdown',
5 | dependencies = { 'nvim-treesitter/nvim-treesitter' },
6 | opts = {},
7 | ft = 'markdown',
8 | },
9 | {
10 | 'OXY2DEV/helpview.nvim',
11 | ft = 'help',
12 | dependencies = { 'nvim-treesitter/nvim-treesitter' },
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/mini_bracketed.lua:
--------------------------------------------------------------------------------
1 | return {
2 | 'echasnovski/mini.bracketed',
3 | keys = {
4 | { '[c', desc = 'Jump to previous comment block' },
5 | { ']c', desc = 'Jump to next comment block' },
6 | { '[x', desc = 'Jump to previous conflict marker' },
7 | { ']x', desc = 'Jump to next conflict marker' },
8 | { '[d', desc = 'Jump to previous diagnostic' },
9 | { ']d', desc = 'Jump to next diagnostic' },
10 | { '[q', desc = 'Jump to previous Quickfix list entry' },
11 | { ']q', desc = 'Jump to next Quickfix list entry' },
12 | { '[t', desc = 'Jump to previous Treesitter node' },
13 | { ']t', desc = 'Jump to next Treesitter node' },
14 | },
15 | opts = {},
16 | }
17 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/mini_files.lua:
--------------------------------------------------------------------------------
1 | return {
2 | 'echasnovski/mini.files',
3 | dependencies = {
4 | {
5 | 'folke/snacks.nvim',
6 | lazy = false,
7 | opts = { rename = { enabled = false } },
8 | },
9 | },
10 | init = function()
11 | vim.api.nvim_create_autocmd('User', {
12 | pattern = 'MiniFilesActionRename',
13 | callback = function(event)
14 | -- LSP rename files when renamed via mini.files
15 | require('snacks.rename').on_rename_file(event.data.from, event.data.to)
16 | end,
17 | })
18 | vim.api.nvim_create_autocmd('User', {
19 | pattern = 'MiniFilesBufferCreate',
20 | callback = function(args)
21 | local minifiles = require('mini.files')
22 | local buf = args.data.buf_id
23 |
24 | -- close with as well as q
25 | vim.keymap.set('n', '', function()
26 | minifiles.close()
27 | end, { buffer = buf })
28 |
29 | -- set up ability to confirm changes with :w
30 | vim.api.nvim_set_option_value('buftype', 'acwrite', { buf = buf })
31 | vim.api.nvim_buf_set_name(buf, string.format('mini.files-%s', vim.loop.hrtime()))
32 | vim.api.nvim_create_autocmd('BufWriteCmd', {
33 | buffer = buf,
34 | callback = function()
35 | minifiles.synchronize()
36 | end,
37 | })
38 |
39 | -- ctrl+v to open selected buffer in a split
40 | vim.keymap.set('n', '', function()
41 | vim.api.nvim_win_call(minifiles.get_explorer_state().target_window, function()
42 | vim.cmd.vsp()
43 | minifiles.set_target_window(vim.api.nvim_get_current_win())
44 | end)
45 | minifiles.go_in({ close_on_file = true })
46 | end, { desc = 'Open file in split window', buffer = buf })
47 | end,
48 | })
49 | end,
50 | keys = {
51 | {
52 | '',
53 | function()
54 | local minifiles = require('mini.files')
55 | if vim.bo.ft == 'minifiles' then
56 | minifiles.close()
57 | else
58 | local file = vim.api.nvim_buf_get_name(0)
59 | local file_exists = vim.fn.filereadable(file) ~= 0
60 | minifiles.open(file_exists and file or nil)
61 | minifiles.reveal_cwd()
62 | end
63 | end,
64 | desc = 'Open mini.files',
65 | },
66 | },
67 | opts = {
68 | content = {
69 | filter = function(entry)
70 | return entry.name ~= '.DS_Store' and entry.name ~= '.git' and entry.name ~= '.direnv'
71 | end,
72 | sort = function(entries)
73 | -- technically can filter entries here too, and checking gitignore for _every entry individually_
74 | -- like I would have to in `content.filter` above is too slow. Here we can give it _all_ the entries
75 | -- at once, which is much more performant.
76 | local all_paths = table.concat(
77 | vim
78 | .iter(entries)
79 | :map(function(entry)
80 | return entry.path
81 | end)
82 | :totable(),
83 | '\n'
84 | )
85 | local output_lines = {}
86 | local job_id = vim.fn.jobstart({ 'git', 'check-ignore', '--stdin' }, {
87 | stdout_buffered = true,
88 | on_stdout = function(_, data)
89 | output_lines = data
90 | end,
91 | })
92 |
93 | -- command failed to run
94 | if job_id < 1 then
95 | return entries
96 | end
97 |
98 | -- send paths via STDIN
99 | vim.fn.chansend(job_id, all_paths)
100 | vim.fn.chanclose(job_id, 'stdin')
101 | vim.fn.jobwait({ job_id })
102 | return require('mini.files').default_sort(vim
103 | .iter(entries)
104 | :filter(function(entry)
105 | return not vim.tbl_contains(output_lines, entry.path)
106 | end)
107 | :totable())
108 | end,
109 | },
110 | mappings = {
111 | go_in_plus = '',
112 | },
113 | windows = {
114 | preview = true,
115 | width_preview = 120,
116 | },
117 | },
118 | }
119 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/mini_move.lua:
--------------------------------------------------------------------------------
1 | return {
2 | 'echasnovski/mini.move',
3 | keys = {
4 | { '', desc = 'Move text left', mode = { 'n', 'v', 'x' } },
5 | { '', desc = 'Move text right', mode = { 'n', 'v', 'x' } },
6 | { '', desc = 'Move text down', mode = { 'n', 'v', 'x' } },
7 | { '', desc = 'Move text up', mode = { 'n', 'v', 'x' } },
8 | },
9 | opts = {
10 | mappings = {
11 | left = '',
12 | right = '',
13 | up = '',
14 | down = '',
15 | line_left = '',
16 | line_right = '',
17 | line_up = '',
18 | line_down = '',
19 | },
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/neotest.lua:
--------------------------------------------------------------------------------
1 | local cmds = {
2 | {
3 | 'Test',
4 | function()
5 | require('neotest').run.run()
6 | end,
7 | {
8 | desc = 'Run nearest test',
9 | },
10 | },
11 | {
12 | 'TestFile',
13 | function()
14 | require('neotest').run.run(vim.fn.expand('%'))
15 | end,
16 | { desc = 'Run tests in current file' },
17 | },
18 | {
19 | 'TestStop',
20 | function()
21 | require('neotest').run.stop()
22 | end,
23 | { desc = 'Kill running tests' },
24 | },
25 | {
26 | 'TestOpen',
27 | function()
28 | require('neotest').output.open()
29 | end,
30 | { desc = 'Open test output' },
31 | },
32 | {
33 | 'TestSummary',
34 | function()
35 | require('neotest').summary.open()
36 | end,
37 | { desc = 'Open test summary' },
38 | },
39 | }
40 |
41 | return {
42 | 'nvim-neotest/neotest',
43 | dependencies = {
44 | 'nvim-neotest/nvim-nio',
45 | 'nvim-neotest/neotest-plenary',
46 | 'nvim-neotest/neotest-go',
47 | 'nvim-neotest/neotest-jest',
48 | 'mrcjkb/rustaceanvim',
49 | },
50 | cmds = vim
51 | .iter(cmds)
52 | :map(function(cmd)
53 | return cmd[1]
54 | end)
55 | :totable(),
56 | init = function()
57 | for _, cmd in ipairs(cmds) do
58 | vim.api.nvim_create_user_command(cmd[1], cmd[2], cmd[3])
59 | end
60 | end,
61 | config = function()
62 | ---@diagnostic disable: missing-fields
63 | require('neotest').setup({
64 | discovery = { enabled = false },
65 | diagnostic = { enabled = true },
66 | status = { enabled = true },
67 | icons = {
68 | expanded = '',
69 | child_prefix = '',
70 | child_indent = '',
71 | final_child_prefix = '',
72 | non_collapsible = '',
73 | collapsed = '',
74 |
75 | passed = '',
76 | running = '',
77 | failed = '',
78 | unknown = '',
79 | },
80 | summary = {
81 | mappings = {
82 | jumpto = '',
83 | expand = { '', '<2-LeftMouse>' },
84 | },
85 | },
86 | adapters = {
87 | require('neotest-plenary'),
88 | require('neotest-go'),
89 | require('rustaceanvim.neotest'),
90 | require('neotest-jest')({
91 | jestCommand = 'pnpm jest',
92 | env = { CI = true },
93 | cwd = function(path)
94 | return require('lspconfig.util').root_pattern('package.json', 'jest.config.js')(path)
95 | end,
96 | }),
97 | },
98 | })
99 | end,
100 | }
101 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/noice.lua:
--------------------------------------------------------------------------------
1 | return {
2 | 'folke/noice.nvim',
3 | dependencies = {
4 | 'rcarriga/nvim-notify',
5 | 'MunifTanjim/nui.nvim',
6 | },
7 | event = 'VeryLazy',
8 | opts = {
9 | lsp = {
10 | signature = { enabled = true },
11 | hover = { enabled = true },
12 | documentation = {
13 | opts = {
14 | win_options = {
15 | concealcursor = 'n',
16 | conceallevel = 3,
17 | winhighlight = { Normal = 'LspFloat' },
18 | },
19 | },
20 | },
21 | override = {
22 | ['vim.lsp.util.convert_input_to_markdown_lines'] = true,
23 | ['vim.lsp.util.stylize_markdown'] = true,
24 | -- ['cmp.entry.get_documentation'] = true,
25 | },
26 | },
27 | cmdline = {
28 | format = {
29 | cmdline = { icon = ' ' },
30 | search_down = { icon = ' ' },
31 | search_up = { icon = ' ' },
32 | filter = { icon = ' ', lang = 'fish' },
33 | lua = { icon = ' ' },
34 | help = { icon = ' ' },
35 | },
36 | opts = {
37 | position = {
38 | row = '98%',
39 | col = 0,
40 | },
41 | size = { width = '100%' },
42 | border = {
43 | padding = { 0, 3 },
44 | },
45 | win_options = {
46 | winhighlight = {
47 | Normal = 'SnacksPickerInput',
48 | FloatBorder = 'SnacksPickerInputBorder',
49 | },
50 | },
51 | },
52 | },
53 | history = {
54 | filter = {},
55 | },
56 | routes = {
57 | {
58 | filter = {
59 | any = {
60 | { find = 'No active Snippet' },
61 | { find = 'No signature help available' },
62 | { find = '^<$' },
63 | { kind = 'wmsg' },
64 | },
65 | },
66 | opts = { skip = true },
67 | },
68 | },
69 | views = {
70 | mini = {
71 | position = {
72 | row = '98%',
73 | },
74 | },
75 | popup = {
76 | win_options = {
77 | winhighlight = {
78 | Normal = 'NormalFloat',
79 | FloatBorder = 'FloatBorder',
80 | },
81 | },
82 | },
83 | },
84 | },
85 | }
86 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/op.lua:
--------------------------------------------------------------------------------
1 | return {
2 | 'mrjones2014/op.nvim',
3 | dev = true,
4 | build = 'make install',
5 | cmd = {
6 | 'OpSignin',
7 | 'OpSignout',
8 | 'OpWhoami',
9 | 'OpCreate',
10 | 'OpView',
11 | 'OpEdit',
12 | 'OpOpen',
13 | 'OpInsert',
14 | 'OpNote',
15 | 'OpSidebar',
16 | 'OpAnalyzeBuffer',
17 | },
18 | opts = {
19 | sidebar = {
20 | side = 'left',
21 | },
22 | },
23 | }
24 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/rustaceanvim.lua:
--------------------------------------------------------------------------------
1 | return {
2 | 'mrcjkb/rustaceanvim',
3 | ft = 'rust',
4 | version = '^5',
5 | depenencies = { 'folke/neoconf.nvim' },
6 | keys = {
7 | {
8 | 'rd',
9 | function()
10 | vim.cmd.RustLsp('relatedDiagnostics')
11 | end,
12 | desc = 'Open related diagnostics',
13 | },
14 | {
15 | 'oc',
16 | function()
17 | vim.cmd.vsp()
18 | vim.cmd.RustLsp('openCargo')
19 | end,
20 | desc = 'Open Cargo.toml in vert split',
21 | },
22 | },
23 | init = function()
24 | local neoconf = require('neoconf')
25 | require('my.lsp.snippets').rust()
26 | vim.g.rustaceanvim = {
27 | server = {
28 | cmd = { vim.env.NVIM_RUST_ANALYZER },
29 | default_settings = {
30 | ['rust-analyzer'] = {
31 | cargo = { allFeatures = true, targetDir = true },
32 | check = {
33 | allTargets = true,
34 | command = 'clippy',
35 | },
36 | diagnostics = {
37 | disabled = { 'inactive-code', 'unresolved-proc-macro' },
38 | },
39 | procMacro = { enable = true },
40 | flags = { exit_timeout = 100 },
41 | files = {
42 | -- Make rustaceanvim effectively work with neoconf for the fields we care about
43 | excludeDirs = vim.tbl_get(neoconf.get('vscode') or {}, 'rust-analyzer', 'files', 'excludeDirs') or {
44 | 'target',
45 | 'node_modules',
46 | '.direnv',
47 | '.git',
48 | },
49 | },
50 | },
51 | },
52 | },
53 | }
54 | end,
55 | }
56 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/smart-splits.lua:
--------------------------------------------------------------------------------
1 | local function lazymap(module, fn)
2 | return function()
3 | require(module)[fn]()
4 | end
5 | end
6 |
7 | return {
8 | 'mrjones2014/smart-splits.nvim',
9 | dev = true,
10 | lazy = false,
11 | opts = { ignored_buftypes = { 'nofile' }, cursor_follows_swapped_bufs = true },
12 | keys = {
13 | { '', lazymap('smart-splits', 'move_cursor_left'), desc = 'Move to left window' },
14 | { '', lazymap('smart-splits', 'move_cursor_down'), desc = 'Move to downward window' },
15 | { '', lazymap('smart-splits', 'move_cursor_up'), desc = 'Move to upward window' },
16 | { '', lazymap('smart-splits', 'move_cursor_right'), desc = 'Move to right window' },
17 | { '', lazymap('smart-splits', 'resize_left'), desc = 'Resize window left' },
18 | { '', lazymap('smart-splits', 'resize_down'), desc = 'Resize window down' },
19 | { '', lazymap('smart-splits', 'resize_up'), desc = 'Resize window up' },
20 | { '', lazymap('smart-splits', 'resize_right'), desc = 'Resize window right' },
21 | { 'h', lazymap('smart-splits', 'swap_buf_left'), desc = 'Swap buffer left' },
22 | { 'j', lazymap('smart-splits', 'swap_buf_up'), desc = 'Swap buffer left' },
23 | { 'k', lazymap('smart-splits', 'swap_buf_down'), desc = 'Swap buffer left' },
24 | { 'l', lazymap('smart-splits', 'swap_buf_right'), desc = 'Swap buffer left' },
25 | },
26 | }
27 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/snacks_picker.lua:
--------------------------------------------------------------------------------
1 | local path = require('my.utils.path')
2 |
3 | local ripgrep_ignore_file_path = (
4 | path.join(vim.env.XDG_CONFIG_HOME or path.join(vim.env.HOME, '.config'), 'ripgrep_ignore')
5 | )
6 |
7 | local function pick(which, opts)
8 | return function()
9 | return require('snacks.picker')[which](opts)
10 | end
11 | end
12 |
13 | return {
14 | 'folke/snacks.nvim',
15 | keys = {
16 | { 'ff', pick('files', { hidden = true }), desc = 'Find files' },
17 | { 'fh', pick('recent'), desc = 'Find oldfiles' },
18 | { 'fb', pick('buffers'), desc = 'Find buffers' },
19 | { 'ft', pick('grep', { hidden = true }), desc = 'Find text' },
20 | { 'fs', pick('lsp_workspace_symbols'), desc = 'Find LSP symbols in the workspace' },
21 | { 'fr', pick('resume'), desc = 'Resume last finder' },
22 | },
23 | opts = {
24 | styles = {
25 | input = {
26 | relative = 'cursor',
27 | },
28 | },
29 | input = { enabled = true },
30 | image = { enabled = true },
31 | picker = {
32 | layout = {
33 | preset = 'telescope',
34 | },
35 | sources = {
36 | files = { args = { '--ignore-file', ripgrep_ignore_file_path } },
37 | grep = { args = { '--ignore-file', ripgrep_ignore_file_path } },
38 | recent = {
39 | filter = {
40 | paths = {
41 | [vim.uv.cwd()] = true,
42 | [string.format('%s/.git/COMMIT_EDITMSG', vim.uv.cwd())] = false,
43 | },
44 | },
45 | },
46 | },
47 | actions = {
48 | trouble_open = {
49 | action = function(picker)
50 | require('trouble.sources.snacks').open(picker, {})
51 | end,
52 | description = 'smart-open-with-trouble',
53 | },
54 | },
55 | win = {
56 | input = {
57 | keys = {
58 | -- fully close picker on `ESC`, I use `jk` to go to normal mode
59 | [''] = { 'close', mode = { 'n', 'i' } },
60 | -- keep `` to clear prompt
61 | [''] = false,
62 | [''] = {
63 | 'trouble_open',
64 | mode = { 'n', 'i' },
65 | },
66 | },
67 | },
68 | },
69 | formatters = {
70 | file = {
71 | filename_first = true,
72 | truncate = 85,
73 | },
74 | },
75 | },
76 | },
77 | dependencies = {
78 | {
79 | 'folke/trouble.nvim',
80 | cmd = 'Trouble',
81 | keys = {
82 | { 'd', 'Trouble diagnostics toggle filter.buf=0', desc = 'Toggle diagnostics list' },
83 | { 'q', 'Trouble qflist toggle', desc = 'Toggle quickfix list' },
84 | { 'l', 'Trouble symbols toggle win.position=right', desc = 'Show LSP symbol tree' },
85 | },
86 | opts = { action_keys = { hover = {} } },
87 | },
88 | {
89 | 'kevinhwang91/nvim-bqf',
90 | ft = 'qf', -- load on quickfix list opened,
91 | opts = {
92 | func_map = {
93 | pscrolldown = '',
94 | pscrollup = '',
95 | },
96 | },
97 | },
98 | },
99 | }
100 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/sql.lua:
--------------------------------------------------------------------------------
1 | return {
2 | 'kristijanhusak/vim-dadbod-ui',
3 | dependencies = {
4 | { 'tpope/vim-dadbod', lazy = true },
5 | { 'kristijanhusak/vim-dadbod-completion', ft = { 'sql', 'mysql', 'plsql' }, lazy = true },
6 | },
7 | cmd = {
8 | 'DBUI',
9 | 'DBUIToggle',
10 | 'DBUIAddConnection',
11 | 'DBUIFindBuffer',
12 | },
13 | init = function()
14 | vim.g.db_ui_use_nerd_fonts = 1
15 | end,
16 | }
17 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/treesitter.lua:
--------------------------------------------------------------------------------
1 | return {
2 | 'nvim-treesitter/nvim-treesitter',
3 | dependencies = {
4 | {
5 | 'calops/hmts.nvim',
6 | version = '*',
7 | ft = 'nix',
8 | },
9 | {
10 | 'hiphish/rainbow-delimiters.nvim',
11 | config = function()
12 | local rainbow = require('rainbow-delimiters')
13 | vim.g.rainbow_delimiters = {
14 | strategy = {
15 | [''] = rainbow.strategy['global'],
16 | vim = rainbow.strategy['local'],
17 | },
18 | query = {
19 | [''] = 'rainbow-delimiters',
20 | lua = 'rainbow-blocks',
21 | html = 'rainbow-tags',
22 | },
23 | highlight = {
24 | 'RainbowDelimiterRed',
25 | 'RainbowDelimiterYellow',
26 | 'RainbowDelimiterBlue',
27 | 'RainbowDelimiterOrange',
28 | 'RainbowDelimiterGreen',
29 | 'RainbowDelimiterViolet',
30 | 'RainbowDelimiterCyan',
31 | },
32 | }
33 | end,
34 | },
35 | {
36 | 'JoosepAlviste/nvim-ts-context-commentstring',
37 | init = function()
38 | vim.g.skip_ts_context_commentstring_module = true
39 | end,
40 | opts = {},
41 | },
42 | 'andymass/vim-matchup',
43 | {
44 | 'ziontee113/query-secretary',
45 | keys = {
46 | {
47 | 'qs',
48 | function()
49 | require('query-secretary').query_window_initiate()
50 | end,
51 | desc = 'Open Query Secretary',
52 | },
53 | },
54 | opts = {},
55 | },
56 | },
57 | event = 'BufRead',
58 | cmd = { 'TSInstall', 'TSUpdate', 'TSUpdateSync' },
59 | build = function()
60 | if #vim.api.nvim_list_uis() == 0 then
61 | -- update sync if running headless
62 | vim.cmd.TSUpdateSync()
63 | else
64 | -- otherwise update async
65 | vim.cmd.TSUpdate()
66 | end
67 | end,
68 | init = function()
69 | vim.g.matchup_matchparen_offscreen = {
70 | method = 'popup',
71 | }
72 | end,
73 | config = function()
74 | vim.filetype.add({
75 | extension = {
76 | mdx = 'mdx',
77 | },
78 | })
79 | -- highlight mdx with markdown -- it's close enough. We also do JSX injection via ./after/queries/markdown/injections.scm
80 | vim.treesitter.language.register('markdown', 'mdx')
81 | require('nvim-treesitter.configs').setup({ ---@diagnostic disable-line:missing-fields
82 | ensure_installed = require('my.lsp.filetypes').treesitter_parsers,
83 | highlight = {
84 | enable = true,
85 | },
86 | indent = {
87 | enable = true,
88 | },
89 | incremental_selection = {
90 | enable = true,
91 | -- not sure why but these don't work correctly
92 | -- if they're defined in keymaps.lua instead of here
93 | keymaps = {
94 | init_selection = '',
95 | node_incremental = '',
96 | scope_incremental = '',
97 | node_decremental = '',
98 | },
99 | },
100 | -- treesitter parser for Swift requires treesitter-cli and only really works on mac
101 | additional_vim_regex_highlighting = { 'swift' },
102 | matchup = {
103 | enable = true,
104 | include_match_words = true,
105 | },
106 | })
107 | end,
108 | }
109 |
--------------------------------------------------------------------------------
/nvim/lua/my/configure/which_key.lua:
--------------------------------------------------------------------------------
1 | local function toggle_char_at_end_of_line(char)
2 | return function()
3 | local api = vim.api
4 | local line = api.nvim_get_current_line()
5 | local commentstring = vim.api.nvim_get_option_value('commentstring', { buf = 0 }):gsub('%%s', '')
6 | local escaped_commentstring = commentstring:gsub('([%(%)%.%%%+%-%*%?%[%^%$])', '%%%1')
7 | local code, comment = line:match('^(.*)' .. escaped_commentstring .. '(.*)$')
8 |
9 | if code then
10 | code = code:gsub('%s*$', '') -- remove trailing spaces
11 | local last_char = code:sub(-1)
12 |
13 | if last_char == char then
14 | code = code:sub(1, #code - 1)
15 | else
16 | code = code .. char
17 | end
18 |
19 | line = code .. ' ' .. commentstring .. (comment or '')
20 | else
21 | line = line:gsub('%s*$', '') -- remove trailing spaces
22 | local last_char = line:sub(-1)
23 |
24 | if last_char == char then
25 | line = line:sub(1, #line - 1)
26 | else
27 | line = line .. char
28 | end
29 | end
30 |
31 | return api.nvim_set_current_line(line)
32 | end
33 | end
34 |
35 | return {
36 | 'folke/which-key.nvim',
37 | event = 'VeryLazy',
38 | dependencies = {
39 | {
40 | 'folke/snacks.nvim',
41 | lazy = false,
42 | opts = { bufdelete = { enabled = true } },
43 | keys = {
44 | {
45 | 'W',
46 | function()
47 | require('snacks.bufdelete').delete(0)
48 | end,
49 | desc = 'Close current buffer',
50 | },
51 | },
52 | },
53 | },
54 | opts = {
55 | preset = 'helix',
56 | win = { col = 0 },
57 | expand = 2,
58 | triggers = {
59 | { '', mode = 'nixsotc' },
60 | { 'f', mode = 'n' },
61 | },
62 | spec = {
63 | {
64 | '',
65 | function()
66 | local buf = vim.api.nvim_get_current_buf()
67 | local win = vim.api.nvim_get_current_win()
68 | vim.ui.select(require('my.cmd-palette'), {
69 | prompt = 'Command Palette',
70 | format_item = function(
71 | item --[[@as PaletteEntry]]
72 | )
73 | if type(item.text) == 'function' then
74 | return item.text(buf, win)
75 | end
76 | return item.text
77 | end,
78 | }, function(
79 | choice --[[@as PaletteEntry]]
80 | )
81 | local callback = vim.tbl_get(choice or {}, 'on_selected')
82 | if type(callback) == 'function' then
83 | callback(buf, win)
84 | end
85 | end)
86 | end,
87 | },
88 | { '', ':noh', desc = 'Clear highlighting on in normal mode', hidden = true },
89 | {
90 | 'jk',
91 | function()
92 | require('notify').dismiss({ silent = true, pending = true })
93 | end,
94 | desc = 'Dismiss notifications',
95 | },
96 | -- allow moving the cursor through wrapped lines using j and k,
97 | -- note that I have line wrapping turned off but turned on only for Markdown
98 | { 'j', 'v:count || mode(1)[0:1] == "no" ? "j" : "gj"', expr = true, mode = { 'n', 'v' }, hidden = true },
99 | { 'k', 'v:count || mode(1)[0:1] == "no" ? "k" : "gk"', expr = true, mode = { 'n', 'v' }, hidden = true },
100 | { '', ':bn', desc = 'Move to next buffer' },
101 | { '', ':bp', desc = 'Move to previous buffer' },
102 | {
103 | 's',
104 | require('my.utils.editor').toggle_spellcheck,
105 | desc = 'Toggle spellcheck',
106 | },
107 | {
108 | '',
109 | toggle_char_at_end_of_line(';'),
110 | mode = { 'n', 'i' },
111 | desc = 'Toggle semicolon',
112 | },
113 | {
114 | '',
115 | toggle_char_at_end_of_line(','),
116 | mode = { 'n', 'i' },
117 | desc = 'Toggle trailing comma',
118 | },
119 | },
120 | },
121 | keys = {
122 | {
123 | '?',
124 | function()
125 | require('which-key').show({ global = false })
126 | end,
127 | desc = 'Buffer Local Keymaps (which-key)',
128 | },
129 | },
130 | }
131 |
--------------------------------------------------------------------------------
/nvim/lua/my/lsp/filetypes.lua:
--------------------------------------------------------------------------------
1 | local M = {}
2 |
3 | M.config = {
4 | ['css'] = {
5 | formatter = 'prettierd',
6 | lspconfig = { 'cssls' },
7 | },
8 | ['html'] = {
9 | lspconfig = { 'html' },
10 | },
11 | ['json'] = {
12 | lspconfig = 'jsonls',
13 | treesitter = { 'json', 'jsonc' },
14 | },
15 | ['typescript'] = {
16 | lspconfig = { 'ts_ls', 'eslint' },
17 | formatter = 'prettierd',
18 | treesitter = { 'typescript', 'javascript', 'tsx' },
19 | },
20 | ['lua'] = {
21 | lspconfig = 'lua_ls',
22 | formatter = 'stylua',
23 | linter = 'luacheck',
24 | treesitter = { 'lua', 'luadoc' },
25 | },
26 | ['rust'] = {
27 | -- let rustaceanvim set this up for us
28 | lspconfig = false,
29 | -- we just want auto formatting
30 | formatter = 'rustfmt',
31 | },
32 | ['go'] = {
33 | lspconfig = 'gopls',
34 | formatter = 'gofmt',
35 | },
36 | ['markdown'] = {
37 | lspconfig = 'marksman',
38 | formatter = {
39 | 'prettierd',
40 | 'injected', -- see :h conform-formatters, formats injected code blocks
41 | },
42 | treesitter = { 'markdown', 'markdown_inline' },
43 | },
44 | ['sh'] = {
45 | treesitter = { 'bash' },
46 | -- shellcheck and shellfmt are run through the LSP
47 | lspconfig = 'bashls',
48 | },
49 | ['swift'] = {
50 | lspconfig = 'sourcekit',
51 | formatter = 'swiftfmt',
52 | treesitter = false, -- requires treesitter-cli and only really works on mac
53 | },
54 | ['nix'] = {
55 | lspconfig = { 'nixd', 'nil_ls' },
56 | linter = 'statix',
57 | formatter = { 'nixfmt', 'injected' },
58 | },
59 | ['toml'] = {
60 | lspconfig = 'taplo',
61 | },
62 | ['fish'] = {
63 | formatter = 'fish_indent',
64 | linter = 'fish',
65 | },
66 | ['yaml'] = {
67 | lspconfig = 'yamlls',
68 | treesitter = { 'yaml' },
69 | },
70 | }
71 |
72 | M.filetypes = vim.tbl_keys(M.config)
73 |
74 | if vim.fn.filereadable('./tailwind.config.js') ~= 0 then
75 | table.insert(M.config['css'].lspconfig, 'tailwindcss')
76 | table.insert(M.config['typescript'].lspconfig, 'tailwindcss')
77 | table.insert(M.config['html'].lspconfig, 'tailwindcss')
78 | end
79 |
80 | -- these all use the same config
81 | M.config['javascript'] = M.config['typescript']
82 | M.config['typescriptreact'] = M.config['typescript']
83 | M.config['javascriptreact'] = M.config['typescript']
84 | M.config['scss'] = M.config['css']
85 |
86 | local function cfg_by_ft(cfg)
87 | local result = {}
88 | for ft, ft_cfg in pairs(M.config) do
89 | if ft_cfg[cfg] then
90 | result[ft] = type(ft_cfg[cfg]) == 'table' and ft_cfg[cfg] or { ft_cfg[cfg] }
91 | end
92 | end
93 | return result
94 | end
95 |
96 | M.formatters_by_ft = cfg_by_ft('formatter')
97 | M.linters_by_ft = cfg_by_ft('linter')
98 |
99 | M.treesitter_parsers = (function()
100 | local result = {}
101 | for ft, config in pairs(M.config) do
102 | if config.treesitter ~= false then
103 | local treesitter = config.treesitter or ft
104 | treesitter = type(treesitter) == 'table' and treesitter or { treesitter }
105 | result = vim.list_extend(result, treesitter)
106 | end
107 | end
108 |
109 | -- insert extra parsers here
110 | table.insert(result, 'regex')
111 | table.insert(result, 'just')
112 | table.insert(result, 'kdl')
113 |
114 | return result
115 | end)()
116 |
117 | return M
118 |
--------------------------------------------------------------------------------
/nvim/lua/my/lsp/go.lua:
--------------------------------------------------------------------------------
1 | return {
2 | settings = {
3 | gopls = {
4 | hints = {
5 | compositeLiteralFields = true,
6 | compositeLiteralTypes = true,
7 | constantValues = true,
8 | functionTypeParameters = true,
9 | parameterNames = true,
10 | rangeVariableTypes = true,
11 | },
12 | },
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/nvim/lua/my/lsp/lua.lua:
--------------------------------------------------------------------------------
1 | local runtime_path = vim.split(package.path, ';', {})
2 | table.insert(runtime_path, 'lua/?.lua')
3 | table.insert(runtime_path, 'lua/?/init.lua')
4 |
5 | local globals = { 'vim' }
6 | if string.find(assert(vim.loop.cwd()), 'hammerspoon') then
7 | globals = { 'hs' }
8 | end
9 |
10 | return {
11 | root_dir = require('lspconfig.util').root_pattern('.luacheckrc', 'stylua.toml'),
12 | settings = {
13 | Lua = {
14 | completion = {
15 | autoRequire = false,
16 | },
17 | runtime = {
18 | -- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
19 | version = 'LuaJIT',
20 | -- Setup your lua path
21 | path = runtime_path,
22 | },
23 | diagnostics = {
24 | globals = globals,
25 | },
26 | workspace = {
27 | -- Make the server aware of Neovim runtime files
28 | library = vim.api.nvim_get_runtime_file('', true),
29 | -- disable annoying "do you need to configure your workspace as luassert" prompts
30 | checkThirdParty = false,
31 | },
32 | -- Do not send telemetry data containing a randomized but unique identifier
33 | telemetry = {
34 | enable = false,
35 | },
36 | },
37 | },
38 | }
39 |
--------------------------------------------------------------------------------
/nvim/lua/my/lsp/nix.lua:
--------------------------------------------------------------------------------
1 | return {
2 | settings = {
3 | nixd = {
4 | options = {
5 | nixos = {
6 | expr = string.format(
7 | '(builtins.getFlake ("git+file://" + toString %s/git/dotfiles/.)).nixosConfigurations.pc.options',
8 | vim.env.HOME
9 | ),
10 | },
11 | home_manager = {
12 | expr = string.format(
13 | -- line is too long
14 | -- luacheck: ignore
15 | '(builtins.getFlake ("git+file://" + toString %s/git/dotfiles/.)).nixosConfigurations."%s".modules.home-manager',
16 | vim.env.HOME,
17 | vim.uv.os_uname().sysname == 'Darwin' and 'Mats-MacBook-Pro' or 'pc'
18 | ),
19 | },
20 | nix_darwin = {
21 | expr = string.format(
22 | -- line is too long
23 | -- luacheck: ignore
24 | '(builtins.getFlake ("git+file://" + toString %s/git/dotfiles/.)).nixosConfigurations."Mats-MacBook-Pro".options',
25 | vim.env.HOME
26 | ),
27 | },
28 | },
29 | },
30 | },
31 | }
32 |
--------------------------------------------------------------------------------
/nvim/lua/my/lsp/snippets.lua:
--------------------------------------------------------------------------------
1 | local M = {}
2 |
3 | local LUA_MODULE_RETURN_TS_QUERY = [[
4 | (return_statement
5 | (expression_list
6 | (identifier) @return-value-name))
7 | ]]
8 |
9 | function M.lua()
10 | local ls = require('luasnip')
11 | local s = ls.s
12 | local i = ls.insert_node
13 | local f = ls.function_node
14 | local fmt = require('luasnip.extras.fmt').fmt
15 | local p = ls.parser.parse_snippet
16 | local c = ls.choice_node
17 |
18 | local function get_returned_mod_name()
19 | local query = vim.treesitter.query.parse('lua', LUA_MODULE_RETURN_TS_QUERY)
20 | local parser = vim.treesitter.get_parser(0, 'lua')
21 | if parser == nil then
22 | error('Install Lua treesitter parser')
23 | end
24 | local tree = parser:parse()[1]
25 | local num_lines = #vim.api.nvim_buf_get_lines(0, 0, -1, false)
26 | for _, node, _ in query:iter_captures(tree:root(), 0, num_lines - 3, num_lines) do
27 | return vim.treesitter.get_node_text(node, 0, {})
28 | end
29 | end
30 |
31 | local function get_req_module(req_path)
32 | local parts = vim.split(req_path[1][1], '%.', { trimempty = true })
33 | return parts[#parts] or ''
34 | end
35 |
36 | local function get_req_module_upper(req_path)
37 | local path = get_req_module(req_path)
38 | return path:sub(1, 1):upper() .. path:sub(2)
39 | end
40 |
41 | ls.add_snippets('lua', {
42 | -- type req, type the module path, and the `local {}` will be automatically
43 | -- filled in as the last section of the module path, e.g. `require('my.custom.mod)` => `local mod`
44 | -- the `local` part is also a choice node allowing you to toggle between `local mod` and `local Mod`
45 | s(
46 | 'req',
47 | fmt("local {} = require('{}')", {
48 | c(2, {
49 | f(get_req_module, { 1 }),
50 | f(get_req_module_upper, { 1 }),
51 | }),
52 | i(1),
53 | })
54 | ),
55 | -- create a new function on the module, dynamically using the `local` that is
56 | -- returned from the overall module, e.g.
57 | -- local MyMod = {}
58 | -- ...
59 | -- return MyMod
60 | -- in this case it will use `function MyMod.fn_name()`
61 | s(
62 | 'mfn',
63 | c(1, {
64 | fmt('function {}.{}({})\n {}\nend', {
65 | f(get_returned_mod_name, {}),
66 | i(1),
67 | i(2),
68 | i(3),
69 | }),
70 | fmt('function {}:{}({})\n {}\nend', {
71 | f(get_returned_mod_name, {}),
72 | i(1),
73 | i(2),
74 | i(3),
75 | }),
76 | })
77 | ),
78 | -- create a module structure, allowing you to customize the module name
79 | p('mod', 'local $1 = {}\n\n$0\n\nreturn $1'),
80 | -- require without assigning to a local
81 | p('rq', "require('$0')"),
82 | -- inline global function
83 | p('fn', 'function($1)\n $0\nend'),
84 | -- inline local function
85 | p('lfn', 'local function $1($2)\n $0\nend'),
86 | })
87 | end
88 |
89 | -- since these snippets are shared between 4 filetypes,
90 | -- ensure we're only registering them once
91 | local ts_snippets_added = false
92 | function M.typescript()
93 | if ts_snippets_added then
94 | return
95 | end
96 |
97 | local ls = require('luasnip')
98 | local p = ls.parser.parse_snippet
99 |
100 | local snippets = {
101 | p('fn', 'function $1($2)$3 {\n $0\n}'),
102 | p('afn', 'const $1 = ($2)$3 => {\n $0\n}'),
103 | p('ifn', '($1)$2 => {\n $0\n}'),
104 | }
105 |
106 | ls.add_snippets('typescript', snippets)
107 | ls.add_snippets('typescriptreact', snippets)
108 | ls.add_snippets('javascript', snippets)
109 | ls.add_snippets('javascriptreact', snippets)
110 |
111 | ts_snippets_added = true
112 | end
113 | M.typescriptreact = M.typescript
114 | M.javascript = M.typescript
115 | M.javascriptreact = M.typescript
116 |
117 | function M.rust()
118 | local ls = require('luasnip')
119 | local p = ls.parser.parse_snippet
120 | ls.add_snippets('rust', {
121 | p('deny', '#![deny(clippy::all, clippy::pedantic, rust_2018_idioms, clippy::unwrap_used)]\n\n', {}),
122 | p('fn', 'fn $1($2)$3 {\n $0\n}'),
123 | p('res', 'Result<$1, $2>$0'),
124 | p('opt', 'Option<$1>$0'),
125 | p('const', 'const $1: $2 = $0;'),
126 | p('enum', '#[derive(Debug)]\nenum $1 {\n $0\n}'),
127 | p('derive', '#[derive($0)]'),
128 | p('tst', '#[test]'),
129 | p('impl', 'impl $1 {\n $0\n}'),
130 | p('for', 'for $1 in $2 {\n $0\n}'),
131 | p('ifl', 'if let Some($1) = $1 {\n $0\n}'),
132 | p('lets', 'let Some($1) = $1 else {\n $0\n};'),
133 | p('leto', 'let Ok($1) = $1 else {\n $0\n};'),
134 | })
135 | end
136 |
137 | function M.nix()
138 | local ls = require('luasnip')
139 | local p = ls.parser.parse_snippet
140 | ls.add_snippets('nix', {
141 | p(
142 | 'flake',
143 | [[{
144 | description = "$1";
145 | inputs = {
146 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";$2
147 | };
148 | outputs = inputs@{ nixpkgs, ... }: {
149 | $0
150 | };
151 | }]]
152 | ),
153 | })
154 | end
155 |
156 | return M
157 |
--------------------------------------------------------------------------------
/nvim/lua/my/lsp/typescript.lua:
--------------------------------------------------------------------------------
1 | local root_dir = require('lspconfig.util').root_pattern('pnpm-workspace.yaml', 'package.json')
2 | -- prioritize workspace roots
3 | if
4 | vim.fn.filereadable(vim.loop.cwd() .. '/pnpm-workspace.yaml') > 0
5 | or vim.fn.filereadable(vim.loop.cwd() .. '/pnpm-workspace.yml') > 0
6 | then
7 | root_dir = function()
8 | return vim.loop.cwd()
9 | end
10 | end
11 |
12 | return {
13 | root_dir = root_dir,
14 | settings = {
15 | typescript = {
16 | inlayHints = {
17 | includeInlayParameterNameHints = 'all',
18 | includeInlayParameterNameHintsWhenArgumentMatchesName = true,
19 | includeInlayFunctionParameterTypeHints = true,
20 | includeInlayVariableTypeHints = false,
21 | includeInlayPropertyDeclarationTypeHints = true,
22 | includeInlayFunctionLikeReturnTypeHints = true,
23 | includeInlayEnumMemberValueHints = true,
24 | },
25 | },
26 | javascript = {
27 | inlayHints = {
28 | includeInlayParameterNameHints = 'all',
29 | includeInlayParameterNameHintsWhenArgumentMatchesName = true,
30 | includeInlayFunctionParameterTypeHints = true,
31 | includeInlayVariableTypeHints = false,
32 | includeInlayPropertyDeclarationTypeHints = true,
33 | includeInlayFunctionLikeReturnTypeHints = true,
34 | includeInlayEnumMemberValueHints = true,
35 | },
36 | },
37 | },
38 | }
39 |
--------------------------------------------------------------------------------
/nvim/lua/my/plugins.lua:
--------------------------------------------------------------------------------
1 | local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'
2 | if not vim.loop.fs_stat(lazypath) then
3 | vim.fn.system({
4 | 'git',
5 | 'clone',
6 | '--filter=blob:none',
7 | '--single-branch',
8 | 'https://github.com/folke/lazy.nvim.git',
9 | lazypath,
10 | })
11 | end
12 | vim.opt.runtimepath:prepend(lazypath)
13 | require('lazy').setup('my.configure', {
14 | defaults = {
15 | lazy = true,
16 | },
17 | dev = { path = '~/git', fallback = true },
18 | install = {
19 | colorscheme = { vim.env.COLORSCHEME or 'tokyonight', 'habamax' },
20 | },
21 | performance = {
22 | rtp = {
23 | disabled_plugins = {
24 | 'netrw',
25 | 'netrwPlugin',
26 | 'netrwSettings',
27 | 'netrwFileHandlers',
28 | 'gzip',
29 | 'zip',
30 | 'zipPlugin',
31 | 'tar',
32 | 'tarPlugin',
33 | 'getscript',
34 | 'getscriptPlugin',
35 | 'vimball',
36 | 'vimballPlugin',
37 | '2html_plugin',
38 | 'logipat',
39 | 'rrhelper',
40 | 'spellfile_plugin',
41 | 'matchit',
42 | },
43 | },
44 | },
45 | })
46 |
--------------------------------------------------------------------------------
/nvim/lua/my/settings.lua:
--------------------------------------------------------------------------------
1 | vim.g.mapleader = ' '
2 | vim.g.maplocalleader = ';'
3 | vim.opt.compatible = false
4 | vim.opt.cursorline = false
5 | vim.opt.autoread = true
6 | vim.opt.mouse = 'a'
7 | vim.opt.autoindent = true
8 | vim.opt.backspace = 'indent,eol,start'
9 | vim.opt.scrolloff = 4
10 | vim.opt.signcolumn = 'yes:3'
11 | vim.opt.hidden = true
12 | vim.opt.number = true
13 | vim.opt.undodir = string.format('%s/undo', vim.fn.stdpath('data'))
14 | vim.opt.undofile = true
15 | vim.opt.wildmenu = true
16 | vim.opt.wrap = false
17 | vim.opt.autochdir = false
18 | vim.opt.diffopt = 'vertical'
19 | vim.opt.smartcase = true
20 | vim.opt.ignorecase = true
21 | vim.opt.termguicolors = true
22 | vim.opt.syntax = 'on'
23 | vim.opt.modeline = true
24 | vim.opt.updatetime = 100
25 | vim.opt.confirm = true
26 | vim.opt.showtabline = 0
27 | vim.opt.cmdheight = 0
28 | vim.opt.sessionoptions = 'buffers,curdir,folds,winpos,winsize,localoptions'
29 | vim.opt.laststatus = 3
30 | vim.opt.virtualedit = 'onemore'
31 | vim.opt.splitkeep = 'screen'
32 | vim.opt.clipboard = 'unnamedplus'
33 |
34 | -- setting to 0 makes it default to value of tabstop
35 | vim.opt.shiftwidth = 0
36 | vim.opt.tabstop = 2
37 | vim.opt.expandtab = true
38 | vim.opt.splitbelow = true
39 | vim.opt.splitright = true
40 |
41 | vim.opt.shortmess:append('I')
42 | vim.opt.wildignore:append({ '*.DS_Store' })
43 | -- enable line-wrapping with left and right cursor movement
44 | vim.opt.whichwrap:append({ ['<'] = true, ['>'] = true, ['h'] = true, ['l'] = true, ['['] = true, [']'] = true })
45 | -- add @, -, and $ as keywords for full SCSS support
46 | vim.opt.iskeyword:append({ '@-@', '-', '$' })
47 | -- slightly thicker window separators
48 | vim.opt.fillchars = {
49 | horiz = '━',
50 | horizup = '┻',
51 | horizdown = '┳',
52 | vert = '┃',
53 | vertleft = '┫',
54 | vertright = '┣',
55 | verthoriz = '╋',
56 | eob = ' ',
57 | }
58 |
59 | vim.filetype.add({
60 | pattern = {
61 | ['*.jsonc'] = 'jsonc',
62 | ['tsconfig.json'] = 'jsonc',
63 | ['tsconfig*.json'] = 'jsonc',
64 | },
65 | })
66 |
67 | vim.opt.exrc = true
68 |
--------------------------------------------------------------------------------
/nvim/lua/my/utils/clipboard.lua:
--------------------------------------------------------------------------------
1 | local M = {}
2 |
3 | -- use OSC52 yank so it works over SSH
4 | local copy_fn = require('vim.ui.clipboard.osc52').copy('+')
5 |
6 | ---Copy string to system clipboard
7 | ---@param str string|string[]
8 | function M.copy(str)
9 | if type(str) == 'string' then
10 | -- the built-in clipboard function
11 | -- takes a table of strings
12 | str = { str }
13 | end
14 | copy_fn(str)
15 | end
16 |
17 | return M
18 |
--------------------------------------------------------------------------------
/nvim/lua/my/utils/editor.lua:
--------------------------------------------------------------------------------
1 | local M = {}
2 |
3 | function M.toggle_spellcheck()
4 | if vim.o.spell then
5 | vim.opt.spell = false
6 | else
7 | vim.opt.spelloptions = { 'camel', 'noplainbuffer' }
8 | vim.opt.spell = true
9 | end
10 | end
11 |
12 | function M.spellcheck_enabled()
13 | return vim.o.spell
14 | end
15 |
16 | return M
17 |
--------------------------------------------------------------------------------
/nvim/lua/my/utils/lsp.lua:
--------------------------------------------------------------------------------
1 | local Methods = vim.lsp.protocol.Methods
2 |
3 | local M = {}
4 |
5 | local formatting_enabled = true
6 |
7 | ---Set up a callback to run on LSP attach
8 | ---@param callback fun(client:table,bufnr:number)
9 | function M.on_attach(callback)
10 | vim.api.nvim_create_autocmd('LspAttach', {
11 | callback = function(args)
12 | local bufnr = args.buf
13 | local client = vim.lsp.get_client_by_id(args.data.client_id)
14 | if client then
15 | callback(client, bufnr)
16 | end
17 | end,
18 | })
19 | end
20 |
21 | function M.on_attach_default(client, bufnr)
22 | -- if current nvim version supports inlay hints, enable them
23 | if vim.lsp['inlay_hint'] ~= nil and client.supports_method(Methods.textDocument_inlayHint) then
24 | vim.lsp.inlay_hint.enable(true)
25 | end
26 |
27 | -- Run eslint fixes before writing
28 | if client.name == 'eslint' then
29 | vim.api.nvim_create_autocmd('BufWritePre', {
30 | buffer = bufnr,
31 | command = 'EslintFixAll',
32 | })
33 | end
34 |
35 | vim.api.nvim_create_autocmd('CursorHold', {
36 | callback = function()
37 | vim.diagnostic.open_float(nil, { focus = false, scope = 'cursor', border = 'none' })
38 | end,
39 | buffer = bufnr,
40 | })
41 | end
42 |
43 | function M.apply_ui_tweaks()
44 | -- customize LSP icons
45 | local icons = {
46 | Error = ' ',
47 | Warn = ' ',
48 | Hint = ' ',
49 | Info = ' ',
50 | }
51 |
52 | local icon_map = {
53 | [vim.diagnostic.severity.ERROR] = icons.Error,
54 | [vim.diagnostic.severity.WARN] = icons.Warn,
55 | [vim.diagnostic.severity.INFO] = icons.Info,
56 | [vim.diagnostic.severity.HINT] = icons.Hint,
57 | }
58 |
59 | local function diagnostic_format(diagnostic)
60 | return string.format('%s %s (%s)', icon_map[diagnostic.severity], diagnostic.message, diagnostic.code)
61 | end
62 |
63 | vim.diagnostic.config({
64 | virtual_text = {
65 | format = diagnostic_format,
66 | prefix = '',
67 | },
68 | float = {
69 | format = diagnostic_format,
70 | },
71 | signs = { priority = 100, text = icon_map },
72 | underline = true,
73 | update_in_insert = false,
74 | severity_sort = true,
75 | })
76 |
77 | -- enable virtual text diagnostics for Neotest only
78 | vim.diagnostic.config({ virtual_text = true }, vim.api.nvim_create_namespace('neotest'))
79 | end
80 |
81 | function M.toggle_formatting_enabled(enable)
82 | if enable == nil then
83 | enable = not formatting_enabled
84 | end
85 | if enable then
86 | formatting_enabled = true
87 | vim.notify('Enabling LSP formatting...', vim.log.levels.INFO)
88 | else
89 | formatting_enabled = false
90 | vim.notify('Disabling LSP formatting...', vim.log.levels.INFO)
91 | end
92 | end
93 |
94 | function M.is_formatting_enabled()
95 | return formatting_enabled
96 | end
97 |
98 | ---@param buf number|nil defaults to 0 (current buffer)
99 | ---@return string|nil
100 | function M.get_formatter_name(buf)
101 | buf = buf or tonumber(vim.g.actual_curbuf or vim.api.nvim_get_current_buf())
102 |
103 | -- if it uses conform.nvim, grab the formatter name
104 | local formatter = require('my.lsp.filetypes').formatters_by_ft[vim.bo[buf].ft]
105 | if formatter then
106 | return type(formatter) == 'table' and table.concat(formatter, ', ') or formatter
107 | end
108 |
109 | -- otherwise just return the LSP server name
110 | local clients = vim.lsp.get_clients({ bufnr = buf, method = Methods.textDocument_formatting })
111 | if #clients > 0 then
112 | return clients[1].name
113 | end
114 |
115 | return nil
116 | end
117 |
118 | return M
119 |
--------------------------------------------------------------------------------
/nvim/lua/my/utils/path.lua:
--------------------------------------------------------------------------------
1 | local M = {}
2 |
3 | ---Join two or more paths together
4 | ---@param ... string
5 | ---@return string
6 | function M.join(...)
7 | return vim.fn.simplify(table.concat({ ... }, '/'))
8 | end
9 |
10 | ---Get path relative to current working directory,
11 | ---replacing `$HOME` with `~` if applicable.
12 | ---@param path string
13 | ---@return string
14 | function M.relative(path)
15 | return vim.fn.fnamemodify(path, ':~:.') or path
16 | end
17 |
18 | return M
19 |
--------------------------------------------------------------------------------
/nvim/stylua.toml:
--------------------------------------------------------------------------------
1 | indent_type = "Spaces"
2 | indent_width = 2
3 | quote_style = "AutoPreferSingle"
4 |
--------------------------------------------------------------------------------
/packages/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs,
3 | pkgs,
4 | system,
5 | ...
6 | }:
7 | {
8 | vim-zellij-navigator = pkgs.callPackage ./vim-zellij-navigator.nix { };
9 | zjstatus = inputs.zjstatus.packages.${system}.default;
10 | darwin-rebuild = inputs.nix-darwin.packages."aarch64-darwin".default;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/vim-zellij-navigator.nix:
--------------------------------------------------------------------------------
1 | {
2 | lib,
3 | stdenv,
4 | fetchurl,
5 | }:
6 |
7 | stdenv.mkDerivation rec {
8 | pname = "vim-zellij-navigator";
9 | version = "0.2.1";
10 |
11 | src = fetchurl {
12 | url = "https://github.com/hiasr/vim-zellij-navigator/releases/download/${version}/vim-zellij-navigator.wasm";
13 | sha256 = "sha256-wpIxPkmVpoAgOsdQKYuipSlDAbsD3/n6tTuOEriJHn0=";
14 | };
15 |
16 | dontUnpack = true;
17 |
18 | installPhase = ''
19 | mkdir -p $out/bin
20 | cp $src $out/bin/vim-zellij-navigator.wasm
21 | chmod +x $out/bin/vim-zellij-navigator.wasm
22 | '';
23 |
24 | meta = with lib; {
25 | description = "Vim Zellij Navigator WASM plugin";
26 | homepage = "https://github.com/hiasr/vim-zellij-navigator";
27 | license = licenses.mit;
28 | platforms = platforms.all;
29 | maintainers = [ ];
30 | };
31 | }
32 |
--------------------------------------------------------------------------------
/secrets.nix:
--------------------------------------------------------------------------------
1 | # This module is NOT imported into the NixOS config,
2 | # it is only used by the agenix CLI to determine which
3 | # keys to use to encrypt secrets.
4 | let
5 | # my public key
6 | users = [
7 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDhIkQh2Viqa519kFJjIPUrz3jrwkSljezVlLU5GP0uh mat@nixos"
8 | ];
9 | # server host key
10 | systems = [
11 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDXHRx83f5MWdhcEHXduTINyUu6yqd2eOgZHE0XNYFlO root@homelab"
12 | ];
13 | secrets = [
14 | "secrets/mullvad_wireguard.age"
15 | "secrets/wireguard_server.age"
16 | "secrets/cleanuperr_env.age"
17 | "secrets/homarr_env.age"
18 | "secrets/cloudflare_certbot_token.age"
19 | "secrets/duckdns_token.age"
20 | "secrets/gatus_discord_webhook_env.age"
21 | "secrets/paperless_admin_pw.age"
22 | "secrets/docmost_env.age"
23 | ];
24 | in
25 | builtins.listToAttrs (
26 | map (secret: {
27 | name = secret;
28 | value = {
29 | publicKeys = users ++ systems;
30 | };
31 | }) secrets
32 | )
33 |
--------------------------------------------------------------------------------
/secrets/cleanuperr_env.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrjones2014/dotfiles/412867a9f6b0e7e40ad5ea5f8d22c3e2102de6b8/secrets/cleanuperr_env.age
--------------------------------------------------------------------------------
/secrets/cloudflare_certbot_token.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrjones2014/dotfiles/412867a9f6b0e7e40ad5ea5f8d22c3e2102de6b8/secrets/cloudflare_certbot_token.age
--------------------------------------------------------------------------------
/secrets/docmost_env.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrjones2014/dotfiles/412867a9f6b0e7e40ad5ea5f8d22c3e2102de6b8/secrets/docmost_env.age
--------------------------------------------------------------------------------
/secrets/duckdns_token.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrjones2014/dotfiles/412867a9f6b0e7e40ad5ea5f8d22c3e2102de6b8/secrets/duckdns_token.age
--------------------------------------------------------------------------------
/secrets/gatus_discord_webhook_env.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrjones2014/dotfiles/412867a9f6b0e7e40ad5ea5f8d22c3e2102de6b8/secrets/gatus_discord_webhook_env.age
--------------------------------------------------------------------------------
/secrets/homarr_env.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrjones2014/dotfiles/412867a9f6b0e7e40ad5ea5f8d22c3e2102de6b8/secrets/homarr_env.age
--------------------------------------------------------------------------------
/secrets/mullvad_wireguard.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrjones2014/dotfiles/412867a9f6b0e7e40ad5ea5f8d22c3e2102de6b8/secrets/mullvad_wireguard.age
--------------------------------------------------------------------------------
/secrets/paperless_admin_pw.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrjones2014/dotfiles/412867a9f6b0e7e40ad5ea5f8d22c3e2102de6b8/secrets/paperless_admin_pw.age
--------------------------------------------------------------------------------
/secrets/wireguard_server.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrjones2014/dotfiles/412867a9f6b0e7e40ad5ea5f8d22c3e2102de6b8/secrets/wireguard_server.age
--------------------------------------------------------------------------------
/ublock-mdbook/.envrc:
--------------------------------------------------------------------------------
1 | use flake
2 |
--------------------------------------------------------------------------------
/ublock-mdbook/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 |
3 | # `nix build` results
4 | result
5 |
--------------------------------------------------------------------------------
/ublock-mdbook/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "ublock-mdbook"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | mdbook = "0.4.30"
10 | serde = { version = "1", features = ["derive"] }
11 | serde_json = "1"
12 | serde_yaml = "0.9.21"
13 | clap = "4"
14 | regex = "1"
15 |
--------------------------------------------------------------------------------
/ublock-mdbook/default.nix:
--------------------------------------------------------------------------------
1 | { pkgs, ... }:
2 | let
3 | manifest = (pkgs.lib.importTOML ./Cargo.toml).package;
4 | in
5 | pkgs.rustPlatform.buildRustPackage {
6 | inherit (manifest) version;
7 | pname = manifest.name;
8 | cargoLock.lockFile = ./Cargo.lock;
9 | src = pkgs.lib.cleanSource ./.;
10 | }
11 |
--------------------------------------------------------------------------------
/ublock-mdbook/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-utils": {
4 | "inputs": {
5 | "systems": "systems"
6 | },
7 | "locked": {
8 | "lastModified": 1694529238,
9 | "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
10 | "owner": "numtide",
11 | "repo": "flake-utils",
12 | "rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
13 | "type": "github"
14 | },
15 | "original": {
16 | "owner": "numtide",
17 | "repo": "flake-utils",
18 | "type": "github"
19 | }
20 | },
21 | "nixpkgs": {
22 | "locked": {
23 | "lastModified": 1696193975,
24 | "narHash": "sha256-mnQjUcYgp9Guu3RNVAB2Srr1TqKcPpRXmJf4LJk6KRY=",
25 | "owner": "nixos",
26 | "repo": "nixpkgs",
27 | "rev": "fdd898f8f79e8d2f99ed2ab6b3751811ef683242",
28 | "type": "github"
29 | },
30 | "original": {
31 | "owner": "nixos",
32 | "ref": "nixos-unstable",
33 | "repo": "nixpkgs",
34 | "type": "github"
35 | }
36 | },
37 | "root": {
38 | "inputs": {
39 | "flake-utils": "flake-utils",
40 | "nixpkgs": "nixpkgs"
41 | }
42 | },
43 | "systems": {
44 | "locked": {
45 | "lastModified": 1681028828,
46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
47 | "owner": "nix-systems",
48 | "repo": "default",
49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
50 | "type": "github"
51 | },
52 | "original": {
53 | "owner": "nix-systems",
54 | "repo": "default",
55 | "type": "github"
56 | }
57 | }
58 | },
59 | "root": "root",
60 | "version": 7
61 | }
62 |
--------------------------------------------------------------------------------
/ublock-mdbook/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "A very basic flake";
3 |
4 | inputs = {
5 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
6 | flake-utils.url = "github:numtide/flake-utils";
7 | };
8 |
9 | outputs =
10 | {
11 | self,
12 | nixpkgs,
13 | flake-utils,
14 | ...
15 | }:
16 | flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-darwin" ] (
17 | system:
18 | let
19 | pkgs = nixpkgs.legacyPackages.${system};
20 | in
21 | {
22 | packages.default = pkgs.callPackage ./. { inherit pkgs; };
23 | devShell = pkgs.mkShell {
24 | CARGO_INSTALL_ROOT = "${toString ./.}/.cargo";
25 | buildInputs = with pkgs; [
26 | cargo
27 | rustc
28 | git
29 | clippy
30 | rust-analyzer
31 | libiconv
32 | ];
33 | };
34 | }
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/ublock-mdbook/src/main.rs:
--------------------------------------------------------------------------------
1 | use clap::{Arg, ArgMatches, Command};
2 | use mdbook::{
3 | book::Book,
4 | preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext},
5 | BookItem,
6 | };
7 | use regex::Regex;
8 | use std::io::prelude::*;
9 | use std::{collections::HashMap, error::Error, io, process};
10 |
11 | fn parse_ublock_filters() -> (String, String, String) {
12 | let filters =
13 | serde_yaml::from_str::>>(include_str!("./ublock-filters.yml"))
14 | .unwrap();
15 |
16 | let md_linkify_pattern = Regex::new(r"[^A-Za-z]").unwrap();
17 |
18 | let mut filters_toc = String::from("");
19 | let mut filters_all = String::from("");
20 | let mut individual_sections = String::from("");
21 | for item in filters.iter() {
22 | for (site_name, filters_text) in item.iter() {
23 | filters_toc.push_str(
24 | format!(
25 | "- [{}](#{})\n",
26 | site_name,
27 | md_linkify_pattern
28 | .replace_all(site_name.to_lowercase().as_str(), "-")
29 | .replace("---", "--")
30 | )
31 | .as_str(),
32 | );
33 | individual_sections.push_str(
34 | format!(
35 | "\n## {}\n\n```adblock\n{}\n```\n",
36 | site_name,
37 | filters_text.trim()
38 | )
39 | .as_str(),
40 | );
41 | filters_all.push_str(
42 | format!(
43 | r#"
44 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
45 | !! {}{}!!
46 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
47 |
48 | {}
49 |
50 | "#,
51 | site_name,
52 | " ".repeat(36 - site_name.len()),
53 | filters_text.trim(),
54 | )
55 | .as_str(),
56 | );
57 | }
58 | }
59 |
60 | filters_all = filters_all.trim().to_string();
61 |
62 | filters_toc.push('\n');
63 |
64 | (filters_all, filters_toc, individual_sections)
65 | }
66 |
67 | struct UblockTagProcessor;
68 |
69 | impl Preprocessor for UblockTagProcessor {
70 | fn name(&self) -> &str {
71 | "ublockfilters"
72 | }
73 |
74 | fn run(&self, _: &PreprocessorContext, mut book: Book) -> mdbook::errors::Result {
75 | let (filters_all, filters_toc, filters_sections) = parse_ublock_filters();
76 | book.for_each_mut(|bookitem| {
77 | if let BookItem::Chapter(chapter) = bookitem {
78 | chapter.content = chapter
79 | .content
80 | .replace(
81 | "{{#ublockfilters-all}}",
82 | format!("```adblock\n{}\n```", filters_all.as_str()).as_str(),
83 | )
84 | .replace("{{#ublockfilters-toc}}", filters_toc.as_str())
85 | .replace("{{#ublockfilters}}", filters_sections.as_str());
86 | }
87 | });
88 | Ok(book)
89 | }
90 | }
91 |
92 | fn handle_processing(pre: &dyn Preprocessor) -> Result<(), Box> {
93 | let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?;
94 | let processed_book = pre.run(&ctx, book).unwrap();
95 | serde_json::to_writer(io::stdout(), &processed_book)?;
96 |
97 | Ok(())
98 | }
99 |
100 | fn is_supported(pre: &dyn Preprocessor, sub_args: &ArgMatches) -> bool {
101 | let renderer = sub_args
102 | .get_one::("renderer")
103 | .expect("Required argument");
104 | pre.supports_renderer(renderer)
105 | }
106 |
107 | fn make_app() -> Command {
108 | Command::new("ublockfilters-preprocessor")
109 | .about("A mdbook preprocessor which loads my uBlock Origin filters into the page, and generates a filter list which can be subscribed to.")
110 | .subcommand(
111 | Command::new("supports")
112 | .arg(Arg::new("renderer").required(true))
113 | .about("Check whether a renderer is supported by this preprocessor"),
114 | ).subcommand(Command::new("gen-filter-list").arg(
115 | Arg::new("path").required(true)
116 | ).about("Generate ublock-filters.txt list that can be subscribed to."))
117 | }
118 |
119 | fn main() -> Result<(), Box> {
120 | let matches = make_app().get_matches();
121 |
122 | let preprocessor = UblockTagProcessor {};
123 |
124 | if let Some(sub_args) = matches.subcommand_matches("supports") {
125 | process::exit(if is_supported(&preprocessor, sub_args) {
126 | 0
127 | } else {
128 | 1
129 | });
130 | } else if let Some(sub_args) = matches.subcommand_matches("gen-filter-list") {
131 | let path = sub_args
132 | .get_one::("path")
133 | .expect("required argument");
134 | let (filters_all, _, _) = parse_ublock_filters();
135 | let mut writer = std::fs::OpenOptions::new()
136 | .create(true)
137 | .write(true)
138 | .truncate(true)
139 | .open(path)?;
140 | writer.write_all(filters_all.as_bytes())?;
141 | } else if let Err(e) = handle_processing(&preprocessor) {
142 | eprintln!("{}", e);
143 | process::exit(1);
144 | }
145 | Ok(())
146 | }
147 |
--------------------------------------------------------------------------------
/ublock-mdbook/src/ublock-filters.yml:
--------------------------------------------------------------------------------
1 | - YouTube: |
2 | ! block YouTube Shorts
3 | www.youtube.com##.ytdRichGridGroupHost
4 |
5 | ! block "Join" button
6 | www.youtube.com##div#sponsor-button
7 |
8 | ! Google sometimes adds an artifical 5 second delay on youtube
9 | ! for Firefox users...
10 | www.youtube.com##+js(nano-stb, resolve(1), *, 0.001)
11 |
12 | ! Block "Members Only" videos
13 | www.youtube.com##ytd-rich-item-renderer.ytd-rich-grid-renderer:has(.badge-style-type-members-only)
14 |
15 | ! Block "ambient mode" garbage
16 | youtube.com###cinematics.ytd-watch-flexy:remove()
17 |
18 | ! Block YouTube's cringe gigantic banner ads on the home screen
19 | www.youtube.com##ytd-statement-banner-renderer
20 | www.youtube.com##.ytd-brand-video-shelf-renderer
21 |
22 | ! Block YouTube's definitely totally unbiased "misinformation" banners, I can think for myself
23 | www.youtube.com##.ytd-clarification-renderer
24 | www.youtube.com###clarify-box
25 |
26 | ! Copied from https://raw.githubusercontent.com/mchangrh/yt-neuter/main/yt-neuter.txt
27 |
28 | ! voice search button
29 | youtube.com###voice-search-button
30 |
31 | ! create button
32 | youtube.com##ytd-topbar-menu-button-renderer:has(button[aria-label=Create])
33 |
34 | ! block image/animations from being fetched (falls back to normal logo)
35 | ||www.gstatic.com/youtube/img/promos/*$image
36 |
37 | ! block child elements of yoodle
38 | youtube.com##.ytd-yoodle-renderer
39 |
40 | !! surveys (#24)
41 | ! survey answer card/ thanks
42 | youtube.com##yt-survey-answer-card-renderer
43 |
44 | ! checkbox survey?
45 | youtube.com##.ytd-checkbox-survey-renderer
46 |
47 | ! feedback survey?
48 | youtube.com##ytd-feedback-survey-renderer
49 | youtube.com##.ytd-feedback-survey-renderer
50 |
51 | ! follow up survey?
52 | youtube.com##.ytd-survey-follow-up-renderer
53 | youtube.com##ytd-survey-follow-up-renderer
54 |
55 | ! generic surveys
56 | youtube.com##ytd-inline-survey-renderer
57 | youtube.com##.ytd-inline-survey-renderer
58 |
59 | ! multistage survey?
60 | youtube.com##.ytd-multi-stage-survey-renderer
61 | youtube.com##ytd-multi-stage-survey-renderer
62 |
63 | ! ratings survey?
64 | youtube.com##ytd-rating-survey-renderer
65 | youtube.com##.ytd-rating-survey-renderer
66 |
67 | ! RED exit survey
68 | youtube.com##.ytd-red-cancel-survey-renderer
69 | youtube.com##ytd-red-cancel-survey-renderer
70 |
71 | ! "how are your recommendations" survey
72 | youtube.com##ytd-single-option-survey-renderer
73 | youtube.com##.ytd-single-option-survey-renderer
74 |
75 | ! shelves (#15)
76 | ! game shelf
77 | youtube.com##ytd-rich-shelf-renderer[has-rounded-box-art-thumbnail-style]
78 |
79 | ! free movies (targets movies channelID
80 | youtube.com##ytd-rich-shelf-renderer:has(a[href="/channel/UClgRkhTL3_hImCAmdLfDE4g"])
81 |
82 | ! community posts
83 | youtube.com##ytd-rich-shelf-renderer:has(ytd-rich-item-renderer[is-post])
84 |
85 | ! breaking news shelf
86 | youtube.com##ytd-rich-shelf-renderer:has(#title:has-text(Breaking news))
87 | m.youtube.com##ytm-rich-section-renderer:has(.rich-shelf-title:has-text(Breaking news))
88 |
89 | ! brand featured banner/ shelf #40
90 | youtube.com##ytd-rich-shelf-renderer:has(ytd-badge-supported-renderer#featured-badge)
91 | youtube.com##ytd-statement-banner-renderer
92 | youtube.com##ytd-brand-video-singleton-renderer
93 | youtube.com##ytd-brand-video-shelf-renderer
94 | youtube.com##.ytd-brand-video-shelf-renderer
95 |
96 | ! mixes
97 | youtube.com##ytd-rich-item-renderer:has(ytd-thumbnail-overlay-bottom-panel-renderer)
98 |
99 | ! movies free with ads
100 | youtube.com##ytd-rich-item-renderer:has(.badge-style-type-ypc> span:has-text(Free with Ads))
101 |
102 | ! "new to you" (#2)
103 | youtube.com##ytd-rich-item-renderer.style-scope:has(>.ytd-feed-nudge-renderer)
104 | youtube.com##.ytd-rich-item-renderer:has(>.ytd-feed-nudge-renderer)
105 |
106 | ! explore categories
107 | youtube.com##ytd-feed-filter-chip-bar-renderer:has(yt-chip-cloud-chip-renderer[chip-style=STYLE_HOME_FILTER])
108 |
109 | ! youtube shorts in sidebar on video player page
110 | youtube.com##ytd-reel-shelf-renderer
111 |
112 | - Stack Overflow / Stack Exchange: |
113 | ! Block the cookie banner that covers the entire bottom left corner even with JS disabled
114 | stackexchange.com##.js-consent-banner
115 | stackoverflow.com##.js-consent-banner
116 | askubuntu.com##.js-consent-banner
117 |
118 | - GitHub: |
119 | ! Block Copilot Workspace button
120 | github.com##[class*="CopilotWorkspaceButton"]
121 |
122 | ! Block all GitHub Copilot shit in the header
123 | github.com##.AppHeader-CopilotChat
124 |
125 | ! Copilot shit in the header on the mobile layout
126 | github.com##copilot-dashboard-entrypoint
127 |
128 | ! Block "Code 55% faster with GitHub Copilot" (lmao, stop lying)
129 | github.com##button:has-text(with GitHub Copilot)
130 |
131 | ! Block GitHub's cringe algorithmic social media style feed
132 | github.com###feed-next
133 | github.com##[aria-labelledby="feed-next"]
134 |
135 | ! Block the corporate changelog above the "Explore Repositories" section
136 | github.com##.dashboard-changelog
137 |
138 | ! Block GitHub's ads above the corporate changelog
139 | github.com##.js-notice
140 |
141 | ! Block "achievements"
142 | github.com##.d-block:has(a[href*="?tab=achievements"])
143 | github.com##.d-md-block:has(a[href*="?tab=achievements"])
144 |
145 | - Glassdoor: |
146 | ! Block the "hard sell" overlay that tries to force you to pay money just to read a review
147 | www.glassdoor.com##.hardsellOverlay
148 |
149 | - PayPal: |
150 | ! Block the cookie banner, same as rejecting cookies
151 | www.paypal.com##.ccpaCookieBanner_container-custom.ccpaCookieBanner_container
152 |
153 | - Discord: |
154 | ! Block "Add super reaction" button
155 | discord.com##div[aria-label="Add Super Reaction"]
156 |
157 | ! Block Nitro stuff in user settings
158 | discord.com##div[class*="premiumTab"]
159 | discord.com##div[class*="premiumFeatureBorder"]
160 | discord.com##div[class*="birthdayFeatureBorder"]
161 |
162 | ! Block "Super" reactions, paid emojis lmao
163 | discord.com##div[class*="reactionInner"][aria-label*="super"]
164 |
165 | ! Block "Download Apps" button
166 | discord.com##div[aria-label*="Download Apps"]:upward(div[class*="listItem"])
167 |
168 | ! Block "Buy Boosts" bar
169 | discord.com##div[aria-label*="Buy Boosts to help unlock"]
170 |
171 | ! Block "Discord's Birthday" crap
172 | discord.com##li[role="listitem"]:has-text("Discord's Birthday")
173 |
174 | ! Block "Start an Activity" button
175 | discord.com##button[aria-label="Start an Activity"]
176 |
--------------------------------------------------------------------------------