From beb5071a7ad8c0d65f4a3c5bcafcfa3f4928ccaf Mon Sep 17 00:00:00 2001 From: L-Nafaryus Date: Tue, 6 Jun 2023 23:18:09 +0500 Subject: [PATCH] initial commit --- .gitignore | 1 + LICENSE | 22 ++ bin/autoclicker | 8 + bin/gitinfo | 9 + bin/mov2gif | 72 ++++ bin/myip | 21 ++ bin/optimimg | 86 +++++ bin/optimpdf | 36 ++ bin/zzz | 22 ++ config/emacs/aliases.zsh | 6 + config/emacs/doom-config/config.el | 140 ++++++++ config/emacs/doom-config/init.el | 194 +++++++++++ config/emacs/doom-config/packages.el | 52 +++ config/git/aliases.zsh | 68 ++++ config/git/attributes | 3 + config/git/config | 58 ++++ config/git/ignore | 36 ++ config/ncmpcpp/aliases.zsh | 3 + config/ncmpcpp/bindings | 424 +++++++++++++++++++++++ config/ncmpcpp/config | 433 ++++++++++++++++++++++++ config/tmux/aliases.zsh | 27 ++ config/tmux/swap-pane.sh | 23 ++ config/tmux/tmux.conf | 123 +++++++ config/zsh/.zshenv | 34 ++ config/zsh/.zshrc | 52 +++ config/zsh/aliases.zsh | 84 +++++ config/zsh/completion.zsh | 97 ++++++ config/zsh/completions/_hey | 36 ++ config/zsh/config.zsh | 84 +++++ config/zsh/keybinds.zsh | 65 ++++ default.nix | 80 +++++ flake.nix | 70 ++++ hosts/common.nix | 32 ++ hosts/elnafo/default.nix | 78 +++++ hosts/elnafo/hardware-configuration.nix | 90 +++++ hosts/niximg.nix | 21 ++ lib/attrs.nix | 22 ++ lib/default.nix | 15 + lib/modules.nix | 52 +++ lib/nixos.nix | 23 ++ lib/options.nix | 15 + modules/desktop/apps/godot.nix | 16 + modules/desktop/audio/default.nix | 19 ++ modules/desktop/browsers/default.nix | 14 + modules/desktop/browsers/firefox.nix | 226 +++++++++++++ modules/desktop/default.nix | 62 ++++ modules/desktop/documents/default.nix | 17 + modules/desktop/gaming/lutris.nix | 19 ++ modules/desktop/gaming/steam.nix | 17 + modules/desktop/gnome.nix | 25 ++ modules/desktop/graphics/default.nix | 50 +++ modules/desktop/media/recording.nix | 27 ++ modules/desktop/term/default.nix | 16 + modules/desktop/vm/qemu.nix | 21 ++ modules/dev/cc.nix | 29 ++ modules/dev/common-lisp.nix | 25 ++ modules/dev/default.nix | 14 + modules/dev/lua.nix | 25 ++ modules/dev/python.nix | 41 +++ modules/dev/rust.nix | 31 ++ modules/dev/shell.nix | 24 ++ modules/editors/default.nix | 14 + modules/editors/emacs.nix | 64 ++++ modules/editors/vim.nix | 23 ++ modules/hardware/audio.nix | 41 +++ modules/hardware/bluetooth.nix | 14 + modules/hardware/fs.nix | 45 +++ modules/hardware/nvidia.nix | 29 ++ modules/hardware/sensors.nix | 16 + modules/hardware/wacom.nix | 24 ++ modules/options.nix | 79 +++++ modules/security.nix | 78 +++++ modules/services/calibre.nix | 16 + modules/services/fail2ban.nix | 38 +++ modules/services/gitea.nix | 49 +++ modules/services/jellyfin.nix | 21 ++ modules/services/nginx.nix | 74 ++++ modules/services/ssh.nix | 23 ++ modules/services/syncthing.nix | 20 ++ modules/services/transmission.nix | 32 ++ modules/services/wireguard.nix | 22 ++ modules/shell/direnv.nix | 15 + modules/shell/git.nix | 30 ++ modules/shell/gnupg.nix | 31 ++ modules/shell/pass.nix | 24 ++ modules/shell/tmux.nix | 46 +++ modules/shell/zsh.nix | 88 +++++ modules/xdg.nix | 48 +++ overlays/.git-keep | 0 packages/.git-keep | 0 shell.nix | 17 + templates/default.nix | 7 + templates/module/default.nix | 15 + templates/project/.git-keep | 0 94 files changed, 4578 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100755 bin/autoclicker create mode 100755 bin/gitinfo create mode 100755 bin/mov2gif create mode 100755 bin/myip create mode 100755 bin/optimimg create mode 100755 bin/optimpdf create mode 100755 bin/zzz create mode 100644 config/emacs/aliases.zsh create mode 100644 config/emacs/doom-config/config.el create mode 100644 config/emacs/doom-config/init.el create mode 100644 config/emacs/doom-config/packages.el create mode 100644 config/git/aliases.zsh create mode 100644 config/git/attributes create mode 100644 config/git/config create mode 100644 config/git/ignore create mode 100644 config/ncmpcpp/aliases.zsh create mode 100644 config/ncmpcpp/bindings create mode 100644 config/ncmpcpp/config create mode 100644 config/tmux/aliases.zsh create mode 100755 config/tmux/swap-pane.sh create mode 100644 config/tmux/tmux.conf create mode 100644 config/zsh/.zshenv create mode 100644 config/zsh/.zshrc create mode 100644 config/zsh/aliases.zsh create mode 100644 config/zsh/completion.zsh create mode 100755 config/zsh/completions/_hey create mode 100644 config/zsh/config.zsh create mode 100644 config/zsh/keybinds.zsh create mode 100644 default.nix create mode 100644 flake.nix create mode 100644 hosts/common.nix create mode 100644 hosts/elnafo/default.nix create mode 100644 hosts/elnafo/hardware-configuration.nix create mode 100644 hosts/niximg.nix create mode 100644 lib/attrs.nix create mode 100644 lib/default.nix create mode 100644 lib/modules.nix create mode 100644 lib/nixos.nix create mode 100644 lib/options.nix create mode 100644 modules/desktop/apps/godot.nix create mode 100644 modules/desktop/audio/default.nix create mode 100644 modules/desktop/browsers/default.nix create mode 100644 modules/desktop/browsers/firefox.nix create mode 100644 modules/desktop/default.nix create mode 100644 modules/desktop/documents/default.nix create mode 100644 modules/desktop/gaming/lutris.nix create mode 100644 modules/desktop/gaming/steam.nix create mode 100644 modules/desktop/gnome.nix create mode 100644 modules/desktop/graphics/default.nix create mode 100644 modules/desktop/media/recording.nix create mode 100644 modules/desktop/term/default.nix create mode 100644 modules/desktop/vm/qemu.nix create mode 100644 modules/dev/cc.nix create mode 100644 modules/dev/common-lisp.nix create mode 100644 modules/dev/default.nix create mode 100644 modules/dev/lua.nix create mode 100644 modules/dev/python.nix create mode 100644 modules/dev/rust.nix create mode 100644 modules/dev/shell.nix create mode 100644 modules/editors/default.nix create mode 100644 modules/editors/emacs.nix create mode 100644 modules/editors/vim.nix create mode 100644 modules/hardware/audio.nix create mode 100644 modules/hardware/bluetooth.nix create mode 100644 modules/hardware/fs.nix create mode 100644 modules/hardware/nvidia.nix create mode 100644 modules/hardware/sensors.nix create mode 100644 modules/hardware/wacom.nix create mode 100644 modules/options.nix create mode 100644 modules/security.nix create mode 100644 modules/services/calibre.nix create mode 100644 modules/services/fail2ban.nix create mode 100644 modules/services/gitea.nix create mode 100644 modules/services/jellyfin.nix create mode 100644 modules/services/nginx.nix create mode 100644 modules/services/ssh.nix create mode 100644 modules/services/syncthing.nix create mode 100644 modules/services/transmission.nix create mode 100644 modules/services/wireguard.nix create mode 100644 modules/shell/direnv.nix create mode 100644 modules/shell/git.nix create mode 100644 modules/shell/gnupg.nix create mode 100644 modules/shell/pass.nix create mode 100644 modules/shell/tmux.nix create mode 100644 modules/shell/zsh.nix create mode 100644 modules/xdg.nix create mode 100644 overlays/.git-keep create mode 100644 packages/.git-keep create mode 100644 shell.nix create mode 100644 templates/default.nix create mode 100644 templates/module/default.nix create mode 100644 templates/project/.git-keep diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ffecc31 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2023 George Kusayko [L-Nafaryus] + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/bin/autoclicker b/bin/autoclicker new file mode 100755 index 0000000..7a39f10 --- /dev/null +++ b/bin/autoclicker @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +while true; do + xdotool mousedown 1 + sleep 0.01 + xdotool mouseup 1 + sleep 0.01 +done diff --git a/bin/gitinfo b/bin/gitinfo new file mode 100755 index 0000000..fceb6c5 --- /dev/null +++ b/bin/gitinfo @@ -0,0 +1,9 @@ +#!/usr/bin/env cached-nix-shell +#! nix-shell -p nix-prefetch-git -i bash + +for repo in $@; do + if [[ "$repo" != https://* ]]; then + repo="https://github.com/$repo" + fi + nix-prefetch-git --quiet "$repo" +done diff --git a/bin/mov2gif b/bin/mov2gif new file mode 100755 index 0000000..5b0bad4 --- /dev/null +++ b/bin/mov2gif @@ -0,0 +1,72 @@ +#!/usr/bin/env cached-nix-shell +#! nix-shell -i bash -p ffmpeg gifsicle + +# Uses ffmpeg to convert a video file to a gif file. Optimizes the final result +# with gifsicle. +# +# Requires: ffmpeg gifsicle +# +# Example: +# mov2gif some-vid.mp4 output.gif +# mov2gif -f 10 -s 00:00:10 -t 00:00:05 some-vid.mp4 output.gif + +set -e +PALETTE="/tmp/palette.png" + +usage() { + cat <&2 echo "$OPTARG requires an argument" + usage + exit 1 + ;; + *) >&2 echo "Not a valid arg: $opt" + usage + exit 1 + ;; + esac +done +shift $((OPTIND-1)) + +# +if (($# == 0)); then + >&2 echo "No movie file specified" + exit +elif [[ ! -f $1 ]]; then + >&2 echo "$1 does not exist" + exit +fi + +src="$1" +dest="${2:-${src%.*}.gif}" +flags="fps=${fps}$scale" + +# stats_mode=full favors motion, causing the resulting palette to better +# accomodate fades and transitions. +ffmpeg -v warning $start $duration -i "file:$src" -vf "$flags,palettegen=stats_mode=full" -y "$PALETTE" +ffmpeg -v warning $start $duration -i "file:$src" -i "$PALETTE" -lavfi "$flags [x]; [x][1:v] paletteuse" -y "$dest" + +gifsicle -O3 "$dest" -o "$dest" diff --git a/bin/myip b/bin/myip new file mode 100755 index 0000000..0ac81c6 --- /dev/null +++ b/bin/myip @@ -0,0 +1,21 @@ +#!/usr/bin/env zsh +# Echos your local or WAN IP + +if [[ $1 == -w ]]; then + wan=1 + shift +fi + +if [[ -n $wan ]]; then + dig +short myip.opendns.com @resolver1.opendns.com +else + IF=$(netstat -rn | awk '/^0.0.0.0/ {thif=substr($0,74,10); print thif;} /^default.*UG/ {thif=substr($0,65,10); print thif;}') + if command -v ifconfig >/dev/null; then + ifconfig ${NET_IF} | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' + elif command -v ip >/dev/null; then + ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/' + else + >&2 echo "No easy way to grab your IP" + exit 1 + fi +fi diff --git a/bin/optimimg b/bin/optimimg new file mode 100755 index 0000000..03bf63a --- /dev/null +++ b/bin/optimimg @@ -0,0 +1,86 @@ +#!/usr/bin/env zsh + +# Optimize image files (losslessly by default) +# +# Examples: +# imgoptim image.jpg image.png image.gif +# imgoptim directory_of_images/ +# imgoptim directory_of_images/*.png +# +# Requires: +# PNGs: optipng, pngquant (lossy) +# JPGs: jpegtran, jpegoptim (lossy) +# GIFs: gifsicle +# +# Packages (same name on homebrew & arch linux) +# optipng [pngquant] libjpeg-turbo [jpegoptim] gifsicle + +set -e +unset CDPATH + +_usage() { + cat <&2 echo "$OPTARG requires an argument"; _usage; exit 1;; + *) >&2 echo "Not a valid arg: $opt"; _usage; exit 1;; + esac +done +shift $((OPTIND-1)) + +cmds=( optipng jpegtran gifsicle ) +[[ -n $lossy ]] && cmds=( $cmds pngquant jpegoptim ) +for cmd in ${cmds[@]}; do + if ! command -v $cmd >/dev/null; then + >&2 echo "$cmd isn't installed" + error=1 + fi +done +if [[ -n $error ]]; then + >&2 echo "There were errors, aborting" + exit 1 +fi + +for file in $@; do + if [[ -d $file ]]; then + imgoptim $file/* + elif [[ -f $file ]]; then + pre_size=$(_filesize "$file") + case ${file##*.} in + png) + [[ -n $lossy ]] && pngquant $file + optipng -nc -nb -o7 $file + ;; + gif) + gifsicle --batch --optimize=3 "$file" + ;; + jpg|jpeg) + [[ -n $lossy ]] && jpegoptim --max=90 "$file" + jpegtran -copy none -optimize -progressive -outfile "$file" "$file" + ;; + *) + printf "Unrecognized file '$file': ignored" + ;; + esac + post_size=$(_filesize "$file") + perc=$(echo "((${pre_size} - ${post_size}) / ${pre_size}) * 100" | bc -l) + printf "* %s: %d => %d (%.2f%% reduction)\n" "$file" "${pre_size}" "${post_size}" "$perc" + fi +done diff --git a/bin/optimpdf b/bin/optimpdf new file mode 100755 index 0000000..167e90b --- /dev/null +++ b/bin/optimpdf @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# Compress pdf files using ghostscript. +# +# Defaults to 72 dpi. +# +# Usage: +# pdfoptim SRC [TARGET] [DPI] +# +# Requires: ghostscript (gs) + +optimize() { + # Adapted from Alfred Klomp's shrinkpdf script + # + gs -q -dNOPAUSE -dBATCH -dSAFER \ + -sDEVICE=pdfwrite \ + -dPDFSETTINGS=/screen \ + -dCompatibilityLevel=1.3 \ + -dEmbedAllFonts=true \ + -dSubsetFonts=true \ + -dAutoRotatePages=/None \ + -dMonoImageResolution=$3 \ + -dMonoImageDownsampleType=/Bicubic \ + -dGrayImageResolution=$3 \ + -dGrayImageDownsampleType=/Bicubic \ + -dColorImageResolution=$3 \ + -dColorImageDownsampleType=/Bicubic \ + -sOutputFile="$2" \ + "$1" +} + +src="$1" +dest="${2:--}" +dpi="${3:-72}" + +optimize "$src" "$dest" "$dpi" || exit $? diff --git a/bin/zzz b/bin/zzz new file mode 100755 index 0000000..99d90d7 --- /dev/null +++ b/bin/zzz @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# If -f, then put computer to sleep. +# Otherwise, lock screen and turn off monitor. + +if [[ $1 == -h ]]; then + bin=${0##*/} + echo "Usage: $bin [-f]" + echo + echo " $bin # put display to sleep" + echo " $bin -f # put computer to sleep" +elif [[ $1 == -f ]]; then + echo "Going to sleep..." + systemctl suspend +else + echo "Shutting my eyes..." + if command -v slock >/dev/null; then + pgrep slock >/dev/null || slock & + fi + sleep 1 + xset dpms force off +fi diff --git a/config/emacs/aliases.zsh b/config/emacs/aliases.zsh new file mode 100644 index 0000000..1393184 --- /dev/null +++ b/config/emacs/aliases.zsh @@ -0,0 +1,6 @@ +#!/usr/bin/env zsh + +e() { pgrep emacs && emacsclient -n "$@" || emacs -nw "$@" } +ediff() { emacs -nw --eval "(ediff-files \"$1\" \"$2\")"; } +eman() { emacs -nw --eval "(switch-to-buffer (man \"$1\"))"; } +ekill() { emacsclient --eval '(kill-emacs)'; } diff --git a/config/emacs/doom-config/config.el b/config/emacs/doom-config/config.el new file mode 100644 index 0000000..3b11fe7 --- /dev/null +++ b/config/emacs/doom-config/config.el @@ -0,0 +1,140 @@ +;;; $DOOMDIR/config.el -*- lexical-binding: t; -*- + +;; Place your private configuration here! Remember, you do not need to run 'doom +;; sync' after modifying this file! + + +;; Some functionality uses this to identify you, e.g. GPG configuration, email +;; clients, file templates and snippets. It is optional. +(setq user-full-name "L-Nafaryus" + user-mail-address "l.nafaryus@gmail.com") + +;; Doom exposes five (optional) variables for controlling fonts in Doom: +;; +;; - `doom-font' -- the primary font to use +;; - `doom-variable-pitch-font' -- a non-monospace font (where applicable) +;; - `doom-big-font' -- used for `doom-big-font-mode'; use this for +;; presentations or streaming. +;; - `doom-unicode-font' -- for unicode glyphs +;; - `doom-serif-font' -- for the `fixed-pitch-serif' face +;; +;; See 'C-h v doom-font' for documentation and more examples of what they +;; accept. For example: +;; +;;(setq doom-font (font-spec :family "Fira Code" :size 12 :weight 'semi-light) +;; doom-variable-pitch-font (font-spec :family "Fira Sans" :size 13)) +;; +;; If you or Emacs can't find your font, use 'M-x describe-font' to look them +;; up, `M-x eval-region' to execute elisp code, and 'M-x doom/reload-font' to +;; refresh your font settings. If Emacs still can't find your font, it likely +;; wasn't installed correctly. Font issues are rarely Doom issues! + +;; There are two ways to load a theme. Both assume the theme is installed and +;; available. You can either set `doom-theme' or manually load a theme with the +;; `load-theme' function. This is the default: +(setq doom-theme 'doom-one) + +;; This determines the style of line numbers in effect. If set to `nil', line +;; numbers are disabled. For relative line numbers, set this to `relative'. +(setq display-line-numbers-type t) + +;; If you use `org' and don't want your org files in the default location below, +;; change `org-directory'. It must be set before org loads! +(setq org-directory "~/org/") + + +;; Whenever you reconfigure a package, make sure to wrap your config in an +;; `after!' block, otherwise Doom's defaults may override your settings. E.g. +;; +;; (after! PACKAGE +;; (setq x y)) +;; +;; The exceptions to this rule: +;; +;; - Setting file/directory variables (like `org-directory') +;; - Setting variables which explicitly tell you to set them before their +;; package is loaded (see 'C-h v VARIABLE' to look up their documentation). +;; - Setting doom variables (which start with 'doom-' or '+'). +;; +;; Here are some additional functions/macros that will help you configure Doom. +;; +;; - `load!' for loading external *.el files relative to this one +;; - `use-package!' for configuring packages +;; - `after!' for running code after a package has loaded +;; - `add-load-path!' for adding directories to the `load-path', relative to +;; this file. Emacs searches the `load-path' when you load packages with +;; `require' or `use-package'. +;; - `map!' for binding new keys +;; +;; To get information about any of these functions/macros, move the cursor over +;; the highlighted symbol at press 'K' (non-evil users must press 'C-c c k'). +;; This will open documentation for it, including demos of how they are used. +;; Alternatively, use `C-h o' to look up a symbol (functions, variables, faces, +;; etc). +;; +;; You can also try 'gd' (or 'C-c c d') to jump to their definition and see how +;; they are implemented. + +(setq confirm-kill-emacs nil) + +(after! mu4e + (setq +mu4e-gmail-accounts '(("l.nafaryus@gmail.com" . "/gmail-main"))) + + (auth-source-pass-enable) + (setq auth-source-debug t) + (setq auth-source-do-cache nil) + (setq auth-sources '(password-store)) + + ;; don't need to run cleanup after indexing for gmail + (setq mu4e-index-cleanup nil) + ;; because gmail uses labels as folders we can use lazy check since + ;; messages don't really "move" + (setq mu4e-index-lazy-check t) + + (set-email-account! "l.nafaryus@gmail.com" + '((mu4e-sent-folder . "/gmail-main/[Gmail]/Sent Mail") + (mu4e-drafts-folder . "/gmail-main/[Gmail]/Drafts") + (mu4e-trash-folder . "/gmail-main/[Gmail]/Trash") + (mu4e-refile-folder . "/gmail-main/[Gmail]/All Mail") + (smtpmail-smtp-user . "l.nafaryus@gmail.com") + (smtpmail-local-domain . "gmail.com") + (smtpmail-default-smtp-server . "smtp.gmail.com") + (smtpmail-smtp-server . "smtp.gmail.com") + (smtpmail-smtp-service . 587) + (mu4e-compose-signature . "---\nL-Nafaryus")) + t) + + (setq mu4e-context-policy 'ask-if-none) + (setq mu4e-compose-context-policy 'always-ask) + ;; viewing options + (setq mu4e-view-show-addresses t) + ;; Do not leave message open after it has been sent + (setq message-kill-buffer-on-exit t) + ;; Don't ask for a 'context' upon opening mu4e + (setq mu4e-context-policy 'pick-first) + ;; Don't ask to quit + (setq mu4e-confirm-quit nil) + (setq mu4e-attachment-dir "~/.mail/.attachments") + +(require 'mu4e-alert) + +(setq mu4e-alert-interesting-mail-query "flag:unread AND maildir:/gmail-main/Inbox") + +(mu4e-alert-enable-mode-line-display) + +(defun refresh-mu4e-alert-mode-line () +(interactive) +(mu4e~proc-kill) +(async-shell-command "mbsync -a") +(mu4e-alert-enable-mode-line-display) +(mu4e-alert-enable-notifications) +) + +(run-with-timer 0 60 'refresh-mu4e-alert-mode-line) +) + +(setq projectile-require-project-root nil) +(setq projectile-project-search-path '("~/projects")) + +(after! meson-mode + (add-hook 'meson-mode-hook 'company-mode)) diff --git a/config/emacs/doom-config/init.el b/config/emacs/doom-config/init.el new file mode 100644 index 0000000..8cab3b0 --- /dev/null +++ b/config/emacs/doom-config/init.el @@ -0,0 +1,194 @@ +;;; init.el -*- lexical-binding: t; -*- + +;; This file controls what Doom modules are enabled and what order they load +;; in. Remember to run 'doom sync' after modifying it! + +;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's +;; documentation. There you'll find a link to Doom's Module Index where all +;; of our modules are listed, including what flags they support. + +;; NOTE Move your cursor over a module's name (or its flags) and press 'K' (or +;; 'C-c c k' for non-vim users) to view its documentation. This works on +;; flags as well (those symbols that start with a plus). +;; +;; Alternatively, press 'gd' (or 'C-c c d') on a module to browse its +;; directory (for easy access to its source code). + +(doom! :input + ;;bidi ; (tfel ot) thgir etirw uoy gnipleh + ;;chinese + ;;japanese + ;;layout ; auie,ctsrnm is the superior home row + + :completion + company ; the ultimate code completion backend + ;;helm ; the *other* search engine for love and life + ;;ido ; the other *other* search engine... + ;;ivy ; a search engine for love and life + vertico ; the search engine of the future + + :ui + ;;deft ; notational velocity for Emacs + doom ; what makes DOOM look the way it does + doom-dashboard ; a nifty splash screen for Emacs + ;;doom-quit ; DOOM quit-message prompts when you quit Emacs + (emoji +unicode) ; 🙂 + hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW + ;;hydra + ;;indent-guides ; highlighted indent columns + ligatures ; ligatures and symbols to make your code pretty again + ;;minimap ; show a map of the code on the side + modeline ; snazzy, Atom-inspired modeline, plus API + ;;nav-flash ; blink cursor line after big motions + ;;neotree ; a project drawer, like NERDTree for vim + ophints ; highlight the region an operation acts on + (popup +defaults) ; tame sudden yet inevitable temporary windows + ;;tabs ; a tab bar for Emacs + treemacs ; a project drawer, like neotree but cooler + unicode ; extended unicode support for various languages + (vc-gutter +pretty) ; vcs diff in the fringe + vi-tilde-fringe ; fringe tildes to mark beyond EOB + window-select ; visually switch windows + workspaces ; tab emulation, persistence & separate workspaces + ;;zen ; distraction-free coding or writing + + :editor + (evil +everywhere); come to the dark side, we have cookies + file-templates ; auto-snippets for empty files + fold ; (nigh) universal code folding + ;;(format +onsave) ; automated prettiness + ;;god ; run Emacs commands without modifier keys + ;;lispy ; vim for lisp, for people who don't like vim + ;;multiple-cursors ; editing in many places at once + ;;objed ; text object editing for the innocent + ;;parinfer ; turn lisp into python, sort of + ;;rotate-text ; cycle region at point between text candidates + snippets ; my elves. They type so I don't have to + ;;word-wrap ; soft wrapping with language-aware indent + + :emacs + dired ; making dired pretty [functional] + electric ; smarter, keyword-based electric-indent + ;;ibuffer ; interactive buffer management + undo ; persistent, smarter undo for your inevitable mistakes + vc ; version-control and Emacs, sitting in a tree + + :term + ;;eshell ; the elisp shell that works everywhere + ;;shell ; simple shell REPL for Emacs + ;;term ; basic terminal emulator for Emacs + vterm ; the best terminal emulation in Emacs + + :checkers + syntax ; tasing you for every semicolon you forget + (spell +flyspell) ; tasing you for misspelling mispelling + grammar ; tasing grammar mistake every you make + + :tools + ;;ansible + biblio ; Writes a PhD for you (citation needed) + ;;debugger ; FIXME stepping through code, to help you add bugs + ;;direnv + ;;docker + ;;editorconfig ; let someone else argue about tabs vs spaces + ;;ein ; tame Jupyter notebooks with emacs + (eval +overlay) ; run code, run (also, repls) + ;;gist ; interacting with github gists + lookup ; navigate your code and its documentation + lsp ; M-x vscode + magit ; a git porcelain for Emacs + make ; run make tasks from Emacs + pass ; password manager for nerds + pdf ; pdf enhancements + ;;prodigy ; FIXME managing external services & code builders + ;;rgb ; creating color strings + ;;taskrunner ; taskrunner for all your projects + ;;terraform ; infrastructure as code + ;;tmux ; an API for interacting with tmux + ;;tree-sitter ; syntax and parsing, sitting in a tree... + ;;upload ; map local to remote projects via ssh/ftp + + :os + (:if IS-MAC macos) ; improve compatibility with macOS + ;;tty ; improve the terminal Emacs experience + + :lang + ;;agda ; types of types of types of types... + ;;beancount ; mind the GAAP + (cc +lsp) ; C > C++ == 1 + ;;clojure ; java with a lisp + common-lisp ; if you've seen one lisp, you've seen them all + ;;coq ; proofs-as-programs + ;;crystal ; ruby at the speed of c + ;;csharp ; unity, .NET, and mono shenanigans + ;;data ; config/data formats + ;;(dart +flutter) ; paint ui and not much else + ;;dhall + ;;elixir ; erlang done right + ;;elm ; care for a cup of TEA? + emacs-lisp ; drown in parentheses + ;;erlang ; an elegant language for a more civilized age + ;;ess ; emacs speaks statistics + ;;factor + ;;faust ; dsp, but you get to keep your soul + ;;fortran ; in FORTRAN, GOD is REAL (unless declared INTEGER) + ;;fsharp ; ML stands for Microsoft's Language + ;;fstar ; (dependent) types and (monadic) effects and Z3 + ;;gdscript ; the language you waited for + ;;(go +lsp) ; the hipster dialect + ;;(graphql +lsp) ; Give queries a REST + ;;(haskell +lsp) ; a language that's lazier than I am + ;;hy ; readability of scheme w/ speed of python + ;;idris ; a language you can depend on + ;;json ; At least it ain't XML + ;;(java +lsp) ; the poster child for carpal tunnel syndrome + ;;javascript ; all(hope(abandon(ye(who(enter(here)))))) + ;;julia ; a better, faster MATLAB + ;;kotlin ; a better, slicker Java(Script) + latex ; writing papers in Emacs has never been so fun + ;;lean ; for folks with too much to prove + ;;ledger ; be audit you can be + ;;lua ; one-based indices? one-based indices + markdown ; writing docs for people to ignore + ;;nim ; python + lisp at the speed of c + ;;nix ; I hereby declare "nix geht mehr!" + ;;ocaml ; an objective camel + org ; organize your plain life in plain text + ;;php ; perl's insecure younger brother + ;;plantuml ; diagrams for confusing people more + ;;purescript ; javascript, but functional + python ; beautiful is better than ugly + ;;qt ; the 'cutest' gui framework ever + ;;racket ; a DSL for DSLs + ;;raku ; the artist formerly known as perl6 + ;;rest ; Emacs as a REST client + rst ; ReST in peace + ;;(ruby +rails) ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"} + (rust +lsp) ; Fe2O3.unwrap().unwrap().unwrap().unwrap() + ;;scala ; java, but good + ;;(scheme +guile) ; a fully conniving family of lisps + sh ; she sells {ba,z,fi}sh shells on the C xor + ;;sml + ;;solidity ; do you need a blockchain? No. + ;;swift ; who asked for emoji variables? + ;;terra ; Earth and Moon in alignment for performance. + ;;web ; the tubes + yaml ; JSON, but readable + ;;zig ; C, but simpler + + :email + (mu4e +org +gmail) + ;;notmuch + ;;(wanderlust +gmail) + + :app + calendar + ;;emms + ;;everywhere ; *leave* Emacs!? You must be joking + ;;irc ; how neckbeards socialize + ;;(rss +org) ; emacs as an RSS reader + ;;twitter ; twitter client https://twitter.com/vnought + + :config + ;;literate + (default +bindings +smartparens)) diff --git a/config/emacs/doom-config/packages.el b/config/emacs/doom-config/packages.el new file mode 100644 index 0000000..a13057c --- /dev/null +++ b/config/emacs/doom-config/packages.el @@ -0,0 +1,52 @@ +;; -*- no-byte-compile: t; -*- +;;; $DOOMDIR/packages.el + +;; To install a package with Doom you must declare them here and run 'doom sync' +;; on the command line, then restart Emacs for the changes to take effect -- or +;; use 'M-x doom/reload'. + + +;; To install SOME-PACKAGE from MELPA, ELPA or emacsmirror: +;(package! some-package) + +;; To install a package directly from a remote git repo, you must specify a +;; `:recipe'. You'll find documentation on what `:recipe' accepts here: +;; https://github.com/radian-software/straight.el#the-recipe-format +;(package! another-package +; :recipe (:host github :repo "username/repo")) + +;; If the package you are trying to install does not contain a PACKAGENAME.el +;; file, or is located in a subdirectory of the repo, you'll need to specify +;; `:files' in the `:recipe': +;(package! this-package +; :recipe (:host github :repo "username/repo" +; :files ("some-file.el" "src/lisp/*.el"))) + +;; If you'd like to disable a package included with Doom, you can do so here +;; with the `:disable' property: +;(package! builtin-package :disable t) + +;; You can override the recipe of a built in package without having to specify +;; all the properties for `:recipe'. These will inherit the rest of its recipe +;; from Doom or MELPA/ELPA/Emacsmirror: +;(package! builtin-package :recipe (:nonrecursive t)) +;(package! builtin-package-2 :recipe (:repo "myfork/package")) + +;; Specify a `:branch' to install a package from a particular branch or tag. +;; This is required for some packages whose default branch isn't 'master' (which +;; our package manager can't deal with; see radian-software/straight.el#279) +;(package! builtin-package :recipe (:branch "develop")) + +;; Use `:pin' to specify a particular commit to install. +;(package! builtin-package :pin "1a2b3c4d5e") + + +;; Doom's packages are pinned to a specific commit and updated from release to +;; release. The `unpin!' macro allows you to unpin single packages... +;(unpin! pinned-package) +;; ...or multiple packages +;(unpin! pinned-package another-pinned-package) +;; ...Or *all* packages (NOT RECOMMENDED; will likely break things) +;(unpin! t) + +(package! meson-mode) diff --git a/config/git/aliases.zsh b/config/git/aliases.zsh new file mode 100644 index 0000000..b3ca990 --- /dev/null +++ b/config/git/aliases.zsh @@ -0,0 +1,68 @@ +#!/usr/bin/env zsh + +g() { [[ $# = 0 ]] && git status --short . || git $*; } + +alias cdg='cd `git rev-parse --show-toplevel`' +alias git='noglob git' +alias ga='git add' +alias gap='git add --patch' +alias gb='git branch -av' +alias gop='git open' +alias gbl='git blame' +alias gc='git commit' +alias gcm='git commit -m' +alias gca='git commit --amend' +alias gcf='git commit --fixup' +alias gcl='git clone' +alias gco='git checkout' +alias gcoo='git checkout --' +alias gf='git fetch' +alias gi='git init' +alias gl='git log --graph --pretty="format:%C(yellow)%h%Creset %C(red)%G?%Creset%C(green)%d%Creset %s %Cblue(%cr) %C(bold blue)<%aN>%Creset"' +alias gll='git log --pretty="format:%C(yellow)%h%Creset %C(red)%G?%Creset%C(green)%d%Creset %s %Cblue(%cr) %C(bold blue)<%aN>%Creset"' +alias gL='gl --stat' +alias gp='git push' +alias gpl='git pull --rebase --autostash' +alias gs='git status --short .' +alias gss='git status' +alias gst='git stash' +alias gr='git reset HEAD' +alias gv='git rev-parse' + +# fzf +if (( $+commands[fzf] )); then + __git_log () { + # format str implies: + # --abbrev-commit + # --decorate + git log \ + --color=always \ + --graph \ + --all \ + --date=short \ + --format="%C(bold blue)%h%C(reset) %C(green)%ad%C(reset) | %C(white)%s %C(red)[%an] %C(bold yellow)%d" + } + + _fzf_complete_git() { + ARGS="$@" + + # these are commands I commonly call on commit hashes. + # cp->cherry-pick, co->checkout + + if [[ $ARGS == 'git cp'* || \ + $ARGS == 'git cherry-pick'* || \ + $ARGS == 'git co'* || \ + $ARGS == 'git checkout'* || \ + $ARGS == 'git reset'* || \ + $ARGS == 'git show'* || \ + $ARGS == 'git log'* ]]; then + _fzf_complete "--reverse --multi" "$@" < <(__git_log) + else + eval "zle ${fzf_default_completion:-expand-or-complete}" + fi + } + + _fzf_complete_git_post() { + sed -e 's/^[^a-z0-9]*//' | awk '{print $1}' + } +fi diff --git a/config/git/attributes b/config/git/attributes new file mode 100644 index 0000000..a1bd92e --- /dev/null +++ b/config/git/attributes @@ -0,0 +1,3 @@ +*.lisp diff=lisp +*.el diff=lisp +*.org diff=org diff --git a/config/git/config b/config/git/config new file mode 100644 index 0000000..9109625 --- /dev/null +++ b/config/git/config @@ -0,0 +1,58 @@ +[user] + name = Henrik Lissner + email = git@henrik.io + signingKey = FA1FADD9440B688CAA75A057B60957CA074D39A3 +[commit] + gpgSign = true +[tag] + gpgSign = true +[core] + whitespace = trailing-space +[init] + defaultBranch = main +[github] + user = hlissner +[gitlab] + user = hlissner +[push] + autoSquash = true +[push] + default = current + gpgSign = if-asked +[pull] + rebase = true +[alias] + unadd = reset HEAD + # data analysis + ranked-authors = !git authors | sort | uniq -c | sort -n + emails = !git log --format="%aE" | sort -u + email-domains = !git log --format="%aE" | awk -F'@' '{print $2}' | sort -u +[filter "lfs"] + required = true + smudge = git-lfs smudge -- %f + process = git-lfs filter-process + clean = git-lfs clean -- %f +[url "https://github.com/"] + insteadOf = gh: +[url "git@github.com:"] + insteadOf = ssh+gh: +[url "git@github.com:hlissner/"] + insteadOf = gh:/ +[url "https://gitlab.com/"] + insteadOf = gl: +[url "https://gist.github.com/"] + insteadOf = gist: +[url "https://bitbucket.org/"] + insteadOf = bb: +[url "https://git.henrik.io"] + insteadOf = my: +[diff "lisp"] + xfuncname = "^(((;;;+ )|\\(|([ \t]+\\(((cl-|el-patch-)?def(un|var|macro|method|custom)|gb/))).*)$" +[diff "org"] + xfuncname = "^(\\*+ +.*)$" +[credential "https://github.com"] + helper = + helper = !gh auth git-credential +[credential "https://gist.github.com"] + helper = + helper = !gh auth git-credential diff --git a/config/git/ignore b/config/git/ignore new file mode 100644 index 0000000..663d047 --- /dev/null +++ b/config/git/ignore @@ -0,0 +1,36 @@ +# For emacs: +*~ +*.*~ +\#* +.\#* + +# For vim: +*.swp +.*.sw[a-z] +*.un~ +.netrwhist + +# OS generated files # +###################### +.DS_Store? +.DS_Store +.CFUserTextEncoding +.Trash +.Xauthority +thumbs.db +Thumbs.db +Icon? + +# Code stuffs # +############### +.ccls-cache/ +.sass-cache/ +__pycache__/ + +# Compiled thangs # +################### +*.class +*.exe +*.o +*.pyc +*.elc diff --git a/config/ncmpcpp/aliases.zsh b/config/ncmpcpp/aliases.zsh new file mode 100644 index 0000000..e231429 --- /dev/null +++ b/config/ncmpcpp/aliases.zsh @@ -0,0 +1,3 @@ +alias rate=mpd-rate +alias mpcs='mpc search any' +alias mpcsp='mpc searchplay any' diff --git a/config/ncmpcpp/bindings b/config/ncmpcpp/bindings new file mode 100644 index 0000000..8170baf --- /dev/null +++ b/config/ncmpcpp/bindings @@ -0,0 +1,424 @@ +def_key "k" + scroll_up + +def_key "j" + scroll_down + +def_key "K" + select_item + scroll_up + +def_key "J" + select_item + scroll_down + +def_key "h" + previous_column + +def_key "l" + next_column + +def_key "L" + show_lyrics + +def_key "up" + volume_up + +def_key "down" + volume_down + +#def_key "right" +# next_column +# +#def_key "left" +# previous_column +# +#def_key "mouse" +# mouse_event +# +#def_key "up" +# scroll_up +# +#def_key "shift-up" +# select_item +# scroll_up +# +#def_key "down" +# scroll_down +# +#def_key "shift-down" +# select_item +# scroll_down +# +#def_key "[" +# scroll_up_album +# +#def_key "]" +# scroll_down_album +# +#def_key "{" +# scroll_up_artist +# +#def_key "}" +# scroll_down_artist +# +#def_key "page_up" +# page_up +# +#def_key "page_down" +# page_down +# +#def_key "home" +# move_home +# +#def_key "end" +# move_end +# +#def_key "insert" +# select_item +# +#def_key "enter" +# enter_directory +# +#def_key "enter" +# toggle_output +# +#def_key "enter" +# run_action +# +#def_key "enter" +# play_item +# +#def_key "space" +# add_item_to_playlist +# +#def_key "space" +# toggle_lyrics_update_on_song_change +# +#def_key "space" +# toggle_visualization_type +# +#def_key "delete" +# delete_playlist_items +# +#def_key "delete" +# delete_browser_items +# +#def_key "delete" +# delete_stored_playlist +# +#def_key "right" +# next_column +# +#def_key "right" +# slave_screen +# +#def_key "right" +# volume_up +# +#def_key "+" +# volume_up +# +#def_key "left" +# previous_column +# +#def_key "left" +# master_screen +# +#def_key "left" +# volume_down +# +#def_key "-" +# volume_down +# +#def_key ":" +# execute_command +# +#def_key "tab" +# next_screen +# +#def_key "shift-tab" +# previous_screen +# +#def_key "f1" +# show_help +# +#def_key "1" +# show_playlist +# +#def_key "2" +# show_browser +# +#def_key "2" +# change_browse_mode +# +#def_key "3" +# show_search_engine +# +#def_key "3" +# reset_search_engine +# +#def_key "4" +# show_media_library +# +#def_key "4" +# toggle_media_library_columns_mode +# +#def_key "5" +# show_playlist_editor +# +#def_key "6" +# show_tag_editor +# +#def_key "7" +# show_outputs +# +#def_key "8" +# show_visualizer +# +#def_key "=" +# show_clock +# +#def_key "@" +# show_server_info +# +#def_key "s" +# stop +# +#def_key "p" +# pause +# +#def_key ">" +# next +# +#def_key "<" +# previous +# +#def_key "ctrl-h" +# jump_to_parent_directory +# +#def_key "ctrl-h" +# replay_song +# +#def_key "backspace" +# jump_to_parent_directory +# +#def_key "backspace" +# replay_song +# +#def_key "f" +# seek_forward +# +#def_key "b" +# seek_backward +# +#def_key "r" +# toggle_repeat +# +#def_key "z" +# toggle_random +# +#def_key "y" +# save_tag_changes +# +#def_key "y" +# start_searching +# +#def_key "y" +# toggle_single +# +#def_key "R" +# toggle_consume +# +#def_key "Y" +# toggle_replay_gain_mode +# +#def_key "T" +# toggle_add_mode +# +#def_key "|" +# toggle_mouse +# +#def_key "#" +# toggle_bitrate_visibility +# +#def_key "Z" +# shuffle +# +#def_key "x" +# toggle_crossfade +# +#def_key "X" +# set_crossfade +# +#def_key "u" +# update_database +# +#def_key "ctrl-s" +# sort_playlist +# +#def_key "ctrl-s" +# toggle_browser_sort_mode +# +#def_key "ctrl-s" +# toggle_media_library_sort_mode +# +#def_key "ctrl-r" +# reverse_playlist +# +#def_key "ctrl-_" +# select_found_items +# +#def_key "/" +# find +# +#def_key "/" +# find_item_forward +# +#def_key "?" +# find +# +#def_key "?" +# find_item_backward +# +#def_key "." +# next_found_item +# +#def_key "," +# previous_found_item +# +#def_key "w" +# toggle_find_mode +# +#def_key "e" +# edit_song +# +#def_key "e" +# edit_library_tag +# +#def_key "e" +# edit_library_album +# +#def_key "e" +# edit_directory_name +# +#def_key "e" +# edit_playlist_name +# +#def_key "e" +# edit_lyrics +# +#def_key "i" +# show_song_info +# +#def_key "I" +# show_artist_info +# +#def_key "g" +# jump_to_position_in_song +# +#def_key "l" +# show_lyrics +# +#def_key "ctrl-v" +# select_range +# +#def_key "v" +# reverse_selection +# +#def_key "V" +# remove_selection +# +#def_key "B" +# select_album +# +#def_key "a" +# add_selected_items +# +#def_key "c" +# clear_playlist +# +#def_key "c" +# clear_main_playlist +# +#def_key "C" +# crop_playlist +# +#def_key "C" +# crop_main_playlist +# +#def_key "m" +# move_sort_order_up +# +#def_key "m" +# move_selected_items_up +# +#def_key "m" +# set_visualizer_sample_multiplier +# +#def_key "n" +# move_sort_order_down +# +#def_key "n" +# move_selected_items_down +# +#def_key "M" +# move_selected_items_to +# +#def_key "A" +# add +# +#def_key "S" +# save_playlist +# +#def_key "o" +# jump_to_playing_song +# +#def_key "G" +# jump_to_browser +# +#def_key "G" +# jump_to_playlist_editor +# +#def_key "~" +# jump_to_media_library +# +#def_key "E" +# jump_to_tag_editor +# +#def_key "U" +# toggle_playing_song_centering +# +#def_key "P" +# toggle_display_mode +# +#def_key "\\" +# toggle_interface +# +#def_key "!" +# toggle_separators_between_albums +# +#def_key "L" +# toggle_lyrics_fetcher +# +#def_key "F" +# toggle_fetching_lyrics_in_background +# +#def_key "ctrl-l" +# toggle_screen_lock +# +#def_key "`" +# toggle_library_tag_type +# +#def_key "`" +# refetch_lyrics +# +#def_key "`" +# add_random_items +# +#def_key "ctrl-p" +# set_selected_items_priority +# +#def_key "q" +# quit +# diff --git a/config/ncmpcpp/config b/config/ncmpcpp/config new file mode 100644 index 0000000..d9ed1d4 --- /dev/null +++ b/config/ncmpcpp/config @@ -0,0 +1,433 @@ +##### directories ###### +ncmpcpp_directory = ~/.config/ncmpcpp + +## Directory for storing downloaded lyrics. +lyrics_directory = ~/music/.db/lyrics + +##### connection settings ##### +mpd_host = "~/.config/mpd/mpd.sock" +#mpd_host = "127.0.0.1" +#mpd_port = 6600 +#mpd_connection_timeout = 5 + +## Needed for tag editor and file operations to work. +mpd_music_dir = ~/music +mpd_crossfade_time = 5 + +##### music visualizer ##### +## +## Note: In order to make music visualizer work you'll +## need to use mpd fifo output, whose format parameter +## has to be set to 44100:16:1 for mono visualization +## or 44100:16:2 for stereo visualization. Example +## configuration (it has to be put into mpd.conf): +## +## audio_output { +## type "fifo" +## name "Visualizer feed" +## path "/tmp/mpd.fifo" +## format "44100:16:2" +## } +## +#visualizer_fifo_path = /tmp/mpd.fifo + +## +## Note: Below parameter is needed for ncmpcpp +## to determine which output provides data for +## visualizer and thus allow syncing between +## visualization and sound as currently there +## are some problems with it. +## +#visualizer_output_name = Visualizer feed + +## +## If you set format to 44100:16:2, make it 'yes'. +## +visualizer_in_stereo = no + +## +## Multiply received samples by given value. Very +## useful for proper visualization of quiet music. +## +#visualizer_sample_multiplier = 1.0 + +## +## Note: Below parameter defines how often ncmpcpp +## has to "synchronize" visualizer and audio outputs. +## 30 seconds is optimal value, but if you experience +## synchronization problems, set it to lower value. +## Keep in mind that sane values start with >=10. +## +visualizer_sync_interval = 30 + +## +## Note: To enable spectrum frequency visualization +## you need to compile ncmpcpp with fftw3 support. +## +# +## Available values: spectrum, wave, wave_filled, ellipse. +visualizer_type = spectrum +## Alternative subset of 256 colors for terminals that support it. +#visualizer_color = 41, 83, 119, 155, 185, 215, 209, 203, 197, 161 +visualizer_color = blue, cyan, green, yellow, magenta, red +#visualizer_look = ●▮ +# +##### system encoding ##### +## +## ncmpcpp should detect your charset encoding +## but if it failed to do so, you can specify +## charset encoding you are using here. +## +## Note: You can see whether your ncmpcpp build +## supports charset detection by checking output +## of `ncmpcpp --version`. +## +## Note: Since MPD uses UTF-8 by default, setting +## this option makes sense only if your encoding +## is different. +## +#system_encoding = "" +# +##### delays ##### +# +## Time of inactivity (in seconds) after playlist +## highlighting will be disabled (0 = always on). +## +#playlist_disable_highlight_delay = 5 +# +## Defines how long messages are supposed to be visible. +## +message_delay_time = 2 +# +##### song format ##### +## +## For a song format you can use: +## +## %l - length +## %f - filename +## %D - directory +## %a - artist +## %A - album artist +## %t - title +## %b - album +## %y - date +## %n - track number (01/12 -> 01) +## %N - full track info (01/12 -> 01/12) +## %g - genre +## %c - composer +## %p - performer +## %d - disc +## %C - comment +## %P - priority +## $R - begin right alignment +## +## If you want to make sure that a part of the format is displayed +## only when certain tags are present, you can archieve it by +## grouping them with brackets, e.g. '{%a - %t}' will be evaluated +## to 'ARTIST - TITLE' if both tags are present or '' otherwise. +## It is also possible to define a list of alternatives by providing +## several groups and separating them with '|', e.g. '{%t}|{%f}' +## will be evaluated to 'TITLE' or 'FILENAME' if the former is not +## present. +## +## Note: If you want to set limit on maximal length of a tag, just +## put the appropriate number between % and character that defines +## tag type, e.g. to make album take max. 20 terminal cells, use '%20b'. +## +## In addition, formats support markers used for text attributes. +## They are followed by character '$'. After that you can put: +## +## - 0 - default window color (discards all other colors) +## - 1 - black +## - 2 - red +## - 3 - green +## - 4 - yellow +## - 5 - blue +## - 6 - magenta +## - 7 - cyan +## - 8 - white +## - 9 - end of current color +## - b - bold text +## - u - underline text +## - r - reverse colors +## - a - use alternative character set +## +## If you don't want to use a non-color attribute anymore, just put it +## again, but this time insert character '/' between '$' and attribute +## character, e.g. {$b%t$/b}|{$r%f$/r} will display bolded title tag +## or filename with reversed colors. +## +## If you want to use 256 colors and/or background colors in formats +## (the naming scheme is described below in section about color +## definitions), it can be done with the syntax $(COLOR), e.g. to set +## the artist tag to one of the non-standard colors and make it have +## yellow background, you need to write $(197_yellow)%a$(end). Note +## that for standard colors this is interchangable with attributes +## listed above. +## +## Note: colors can be nested. +## +# +# song_list_format = {%a }$2%t$9 %C $R{%b }$2%25l$9 +#song_list_format = {{$5%a$9 }{$7%A$9 }}{%n }$(16)%t$(end) $R{$8%l$9} +# song_status_format = {{%a{ $3%b{ (%y)}$9} }{$8%t$9}}|{%f} +#song_library_format = {%n - }{%t}|{%f} +song_list_format = "{$2%a$9} {$5%t$9} $R {$3%b$3} $9({$9%l$9})" +song_library_format = "%n %t" +song_status_format = "$b{$6%a$9 $1|$9} {$7%t$9} $1|$9 {$2%b$9} $1|$9 {$6%y$9} $1|$9" +# +# +#alternative_header_first_line_format = $b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b +#alternative_header_second_line_format = {{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D} +# +now_playing_prefix = "$(3_236)> " +now_playing_suffix = $0 +# +#browser_playlist_prefix = "$2playlist$9 " +# +selected_item_prefix = "$(6_237)+ " +selected_item_suffix = $9 +# +# modified_item_prefix = $3> $9 +# +## +## Note: attributes are not supported for the following variables. +## +#song_window_title_format = {%a - }{%t}|{%f} +## +## Note: Below variables are used for sorting songs in browser. +## The sort mode determines how songs are sorted, and can be used +## in combination with a sort format to specify a custom sorting format. +## Available values for browser_sort_mode are "name", "mtime", "format" +## and "noop". +## +# +#browser_sort_mode = name +#browser_sort_format = {%a - }{%t}|{%f} {(%l)} +# +##### columns settings ##### +## +## syntax of song columns list format is "column column etc." +## +## - syntax for each column is: +## +## (width of the column)[color of the column]{displayed tag} +## +## Note: Width is by default in %, if you want a column to +## have fixed size, add 'f' after the value, e.g. (10)[white]{a} +## will be the column that take 10% of screen (so the real width +## will depend on actual screen size), whereas (10f)[white]{a} +## will take 10 terminal cells, no matter how wide the screen is. +## +## - color is optional (if you want the default one, +## leave the field empty). +## +## Note: You can give a column additional attributes by putting appropriate +## character after displayed tag character. Available attributes are: +## +## - r - column will be right aligned +## - E - if tag is empty, empty tag marker won't be displayed +## +## You can also: +## +## - give a column custom name by putting it after attributes, +## separated with character ':', e.g. {lr:Length} gives you +## right aligned column of lengths named "Length". +## +## - define sequence of tags, that have to be displayed in case +## predecessor is empty in a way similar to the one in classic +## song format, i.e. using '|' character, e.g. {a|c|p:Owner} +## creates column named "Owner" that tries to display artist +## tag and then composer and performer if previous ones are +## not available. +## +song_columns_list_format = (3f)[241]{NEr:#} (8f)[5]{l} (22)[6]{a|A:Artist} (40)[9]{t|f:Title} (30)[13]{b} +# +##### various settings ##### +# +## +## Note: Custom command that will be executed each +## time song changes. Useful for notifications etc. +## +execute_on_song_change = "mpd-notify-song" +#playlist_show_mpd_host = no +playlist_show_remaining_time = yes +playlist_shorten_total_times = yes +#playlist_separate_albums = no +# +## +## Note: Possible display modes: classic, columns. +## +playlist_display_mode = classic +browser_display_mode = columns +#search_engine_display_mode = classic +#playlist_editor_display_mode = classic +discard_colors_if_item_is_selected = yes +incremental_seeking = yes +seek_time = 2 +volume_change_step = 10 +#autocenter_mode = no +#centered_cursor = no +# +## +## Note: You can specify third character which will +## be used to build 'empty' part of progressbar. +## +progressbar_look = "─╼ " +#progressbar_boldness +progressbar_color = 1 +progressbar_elapsed_color = 4 +# +## Available values: database, playlist. +## +#default_place_to_search_in = database +# +## Available values: classic, alternative. +## +user_interface = alternative +#data_fetching_delay = yes +# +## Available values: artist, album_artist, date, genre, composer, performer. +## +#media_library_primary_tag = artist +# +## Available values: wrapped, normal. +## +#default_find_mode = wrapped +#default_tag_editor_pattern = %n - %t +header_visibility = yes +statusbar_visibility = yes +titles_visibility = no +#header_text_scrolling = yes +#cyclic_scrolling = no +#lines_scrolled = 2 +#follow_now_playing_lyrics = no +#fetch_lyrics_for_current_song_in_background = no +#store_lyrics_in_song_dir = no +#generate_win32_compatible_filenames = yes +allow_for_physical_item_deletion = yes +# +## +## Note: If you set this variable, ncmpcpp will try to +## get info from last.fm in language you set and if it +## fails, it will fall back to english. Otherwise it will +## use english the first time. +## +## Note: Language has to be expressed as an ISO 639 alpha-2 code. +## +#lastfm_preferred_language = en +#show_hidden_files_in_local_browser = no +# +## +## How shall screen switcher work? +## +## - "previous" - switch between the current and previous screen. +## - "screen1,...,screenN" - switch between given sequence of screens. +## +## Screens available for use: help, playlist, browser, search_engine, +## media_library, playlist_editor, tag_editor, outputs, visualizer, clock. +## +#screen_switcher_mode = playlist, browser +# +## +## Note: You can define startup screen +## by choosing screen from the list above. +## +#startup_screen = playlist +# +## +## Note: You can define startup slave screen +## by choosing screen from the list above or +## an empty value for no slave screen. +## +#startup_slave_screen = "" +#startup_slave_screen_focus = no +# +## +## Default width of locked screen (in %). +## Acceptable values are from 20 to 80. +## +# +#locked_screen_width_part = 50 +#ask_for_locked_screen_width_part = yes +jump_to_now_playing_song_at_start = yes +#ask_before_clearing_playlists = yes +#clock_display_seconds = no +#display_volume_level = no +#display_bitrate = yes +display_remaining_time = no +# +## Available values: none, basic, extended, perl. +## +#regular_expressions = perl +# +## +## Note: If below is enabled, ncmpcpp will ignore leading +## "The" word while sorting items in browser, tags in +## media library, etc. +## +#ignore_leading_the = no +#block_search_constraints_change_if_items_found = yes +#mouse_support = yes +#mouse_list_scroll_whole_page = yes +empty_tag_marker = +#tags_separator = " | " +tag_editor_extended_numeration = yes +#media_library_sort_by_mtime = no +enable_window_title = yes +# +## +## Note: You can choose default search mode for search +## engine. Available modes are: +## +## - 1 - use mpd built-in searching (no regexes, pattern matching) +## - 2 - use ncmpcpp searching (pattern matching with support for regexes, +## but if your mpd is on a remote machine, downloading big database +## to process it can take a while +## - 3 - match only exact values (this mode uses mpd function for searching +## in database and local one for searching in current playlist) +## +search_engine_default_search_mode = 2 +external_editor = vim + +## Note: set to yes if external editor is a console application. +use_console_editor = yes + +##### colors definitions ##### +## +## It is possible to set a background color by setting a color +## value "_", e.g. red_black will set +## foregound color to red and background color to black. +## +## In addition, for terminals that support 256 colors it +## is possible to set one of them by using a number in range +## [1, 256] instead of color name, e.g. numerical value +## corresponding to red_black is 2_1. To find out if the +## terminal supports 256 colors, run ncmpcpp and check out +## the bottom of the help screen for list of available colors +## and their numerical values. +## +## Note: due to technical limitations of ncurses, if 256 colors +## are used, it is possible to either use only the colors with +## default background color, or all pairs from 1_1 up to 254_127, +## depending on the ncurses version used. +## +colors_enabled = yes +empty_tag_color = 13 +header_window_color = 241 +volume_color = 239 +state_line_color = 13 +state_flags_color = 13 +main_window_color = green +color1 = white +color2 = blue +# main_window_highlight_color = yellow +statusbar_color = 13 +statusbar_time_color = 6 +player_state_color = 14 +alternative_ui_separator_color = 2 +current_item_prefix = $(9_238) +current_item_suffix = $(end) +# active_column_color = red +window_border_color = 13 +active_window_border = 13 diff --git a/config/tmux/aliases.zsh b/config/tmux/aliases.zsh new file mode 100644 index 0000000..4480414 --- /dev/null +++ b/config/tmux/aliases.zsh @@ -0,0 +1,27 @@ +#!/usr/bin/env zsh + +alias ta='tmux attach' +alias tl='tmux ls' + +if [[ -n $TMUX ]]; then # From inside tmux + alias tf='tmux find-window' + # Detach all other clients to this session + alias mine='tmux detach -a' + # Send command to other tmux window + tt() { + tmux send-keys -t .+ C-u && \ + tmux set-buffer "$*" && \ + tmux paste-buffer -t .+ && \ + tmux send-keys -t .+ Enter; + } + # Create new session (from inside one) + tn() { + local name="${1:-`basename $PWD`}" + TMUX= tmux new-session -d -s "$name" + tmux switch-client -t "$name" + tmux display-message "Session #S created" + } +else # From outside tmux + # Start grouped session so I can be in two different windows in one session + tdup() { tmux new-session -t "${1:-`tmux display-message -p '#S'`}"; } +fi diff --git a/config/tmux/swap-pane.sh b/config/tmux/swap-pane.sh new file mode 100755 index 0000000..f5a6e98 --- /dev/null +++ b/config/tmux/swap-pane.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +CONF="$TMUX_PLUGINS_PATH/lastpane" +swap() { tmux swap-pane -s"$1" -t"$2"; } + +target= +case $1 in + up) target="U" ;; + down) target="D" ;; + left) target="L" ;; + right) target="R" ;; + master) target="M" ;; + *) exit 1 ;; +esac + +src_pane=$(tmux display-message -p "#P") +tmux select-pane -${target} + +dst_pane=$(tmux display-message -p "#P") +tmux select-pane -${src_pane} + +[[ "$target" == M ]] && dst_pane=0 +swap "$src_pane" "$dst_pane" +tmux select-pane -t"$dst_pane" diff --git a/config/tmux/tmux.conf b/config/tmux/tmux.conf new file mode 100644 index 0000000..d4e92a2 --- /dev/null +++ b/config/tmux/tmux.conf @@ -0,0 +1,123 @@ +# tmux.conf +######################################## + +set -s default-terminal "xterm-256color" + +setw -g automatic-rename on # rename window after current program +set -g renumber-windows on # renumber windows when one is closed +# Zero-based indexing is fine in programming languages, but not so much in a +# multiplexer when zero is on the other side of the keyboard. +set -g base-index 1 +setw -g pane-base-index 1 +# display tmux messages longer +set -g display-time 1500 +set -g display-panes-time 800 +# Address vim-mode switching delay (http://superuser.com/a/252717/65504) +set -s escape-time 0 +set -sg repeat-time 600 +set -g history-limit 50000 +# Update status-{left,right} more often (default: 15) +set -g status-interval 5 +# Rather than constraining window size to the maximum size of any client +# connected to the *session*, constrain window size to the maximum size of any +# client connected to *that window*. Much more reasonable. +setw -g aggressive-resize off +# For terminals that support them, propagate these events to programs that +# understand them. +set -s focus-events on +# Enable mouse + mouse wheel +set -g mouse on + +######################################## +# Keybinds # +######################################## + +# Rebind prefix to C-c. Press twice to send literal C-c. +unbind C-b +set -g prefix C-c +bind C-c send-prefix + +# Vi-style keybinds +set -g status-keys vi +set -g mode-keys vi + +bind c new-window -c "#{pane_current_path}" +bind v split-window -h -c "#{pane_current_path}" +bind s split-window -v -c "#{pane_current_path}" + +bind h select-pane -L +bind j select-pane -D +bind k select-pane -U +bind l select-pane -R +bind H run '$TMUX_HOME/swap-pane.sh left' +bind J run '$TMUX_HOME/swap-pane.sh down' +bind K run '$TMUX_HOME/swap-pane.sh up' +bind L run '$TMUX_HOME/swap-pane.sh right' +bind M run '$TMUX_HOME/swap-pane.sh master' + +bind o resize-pane -Z +bind S choose-session +bind W choose-window +bind / choose-session +bind . choose-window + +# bind = select-layout tiled +bind | select-layout even-horizontal +bind _ select-layout even-vertical + +# Disable confirmation +bind x kill-pane +bind X kill-window +bind q kill-session +bind Q kill-server + +# Smart pane switching with awareness of vim splits +# See: https://github.com/christoomey/vim-tmux-navigator +is_vim='echo "#{pane_current_command}" | grep -iqE "(^|\/)g?(view|n?vim?x?)(diff)?$"' +bind -n C-h if-shell "$is_vim" "send-keys C-h" "select-pane -L" +bind -n C-j if-shell "$is_vim" "send-keys C-j" "select-pane -D" +bind -n C-k if-shell "$is_vim" "send-keys C-k" "select-pane -U" +bind -n C-l if-shell "$is_vim" "send-keys C-l" "select-pane -R" +bind -n C-\\ if-shell "$is_vim" "send-keys C-\\" "select-pane -l" +bind C-w last-pane +bind C-n next-window +bind C-p previous-window + +# break pane into a window +bind = select-layout even-vertical +bind + select-layout even-horizontal +bind - break-pane +bind _ join-pane + +# reload config without killing server +bind r source-file $DOTFILES/config/tmux/tmux.conf \; display-message " Config reloaded..". +bind ^r refresh-client + + +######################################## +# Copy mode # +######################################## + +bind Enter copy-mode # enter copy mode +bind b list-buffers # list paster buffers +bind B choose-buffer # choose which buffer to paste from +bind p paste-buffer # paste from the top paste buffer +bind P run "xclip -selection clipboard -o | tmux load-buffer - ; tmux paste-buffer" + +bind -T copy-mode-vi v send-keys -X begin-selection +bind -T copy-mode-vi C-v send-keys -X rectangle-toggle +bind -T copy-mode-vi Escape send-keys -X cancel +bind -T copy-mode-vi C-g send-keys -X cancel +bind -T copy-mode-vi H send-keys -X start-of-line +bind -T copy-mode-vi L send-keys -X end-of-line + + +######################################## +# Local config # +######################################## +set -g @open-editor 'C-e' +set -g @open-S 'https://www.duckduckgo.com/' +set -g @resurrect-processes 'ssh sqlite3 "git log"' + +run-shell $TMUX_HOME/extraInit +if '[ -f ~/.tmux.conf ]' 'source-file ~/.tmux.conf' diff --git a/config/zsh/.zshenv b/config/zsh/.zshenv new file mode 100644 index 0000000..5f8de57 --- /dev/null +++ b/config/zsh/.zshenv @@ -0,0 +1,34 @@ +function _cache { + (( $+commands[$1] )) || return 1 + local cache_dir="$XDG_CACHE_HOME/${SHELL##*/}" + local cache="$cache_dir/$1" + if [[ ! -f $cache || ! -s $cache ]]; then + echo "Caching $1" + mkdir -p $cache_dir + "$@" >$cache + chmod 600 $cache + fi + if [[ -o interactive ]]; then + source $cache || rm -f $cache + fi +} + +function _source { + for file in "$@"; do + [ -r $file ] && source $file + done +} + +# Be more restrictive with permissions; no one has any business reading things +# that don't belong to them. +if (( EUID != 0 )); then + umask 027 +else + # Be even less permissive if root. + umask 077 +fi + +# Auto-generated by my nix config +_source ${0:a:h}/extra.zshenv +# If you have host-local configuration, this is where you'd put it +_source $ZDOTDIR/local.zshenv diff --git a/config/zsh/.zshrc b/config/zsh/.zshrc new file mode 100644 index 0000000..b171867 --- /dev/null +++ b/config/zsh/.zshrc @@ -0,0 +1,52 @@ +#!/usr/bin/env zsh +source $ZDOTDIR/config.zsh + +# NOTE ZGEN_DIR and ZGEN_SOURCE are forward-declared in modules/shell/zsh.nix +# NOTE Using zgenom because zgen is no longer maintained +if [ ! -d "$ZGEN_DIR" ]; then + echo "Installing jandamm/zgenom" + git clone https://github.com/jandamm/zgenom "$ZGEN_DIR" +fi +source $ZGEN_DIR/zgenom.zsh + +# Check for plugin and zgenom updates every 7 days +# This does not increase the startup time. +zgenom autoupdate + +if ! zgenom saved; then + echo "Initializing zgenom" + rm -f $ZDOTDIR/*.zwc(N) \ + $XDG_CACHE_HOME/zsh/*(N) \ + $ZGEN_INIT.zwc + + # NOTE Be extra careful about plugin load order, or subtle breakage can + # emerge. This is the best order I've sussed out for these plugins. + zgenom load junegunn/fzf shell + zgenom load jeffreytse/zsh-vi-mode + zgenom load zdharma-continuum/fast-syntax-highlighting + zgenom load zsh-users/zsh-completions src + zgenom load zsh-users/zsh-autosuggestions + zgenom load zsh-users/zsh-history-substring-search + zgenom load romkatv/powerlevel10k powerlevel10k + zgenom load hlissner/zsh-autopair autopair.zsh + + zgenom save + zgenom compile $ZDOTDIR +fi + +## Bootstrap interactive sessions +if [[ $TERM != dumb ]]; then + autoload -Uz compinit && compinit -u -d $ZSH_CACHE/zcompdump + + source $ZDOTDIR/keybinds.zsh + source $ZDOTDIR/completion.zsh + source $ZDOTDIR/aliases.zsh + + # Auto-generated by nixos + _source $ZDOTDIR/extra.zshrc + # If you have host-local configuration, put it here + _source $ZDOTDIR/local.zshrc + + _cache fasd --init posix-alias zsh-{hook,{c,w}comp{,-install}} + autopair-init +fi diff --git a/config/zsh/aliases.zsh b/config/zsh/aliases.zsh new file mode 100644 index 0000000..d19c35d --- /dev/null +++ b/config/zsh/aliases.zsh @@ -0,0 +1,84 @@ +alias ..='cd ..' +alias ...='cd ../..' +alias ....='cd ../../..' +alias -- -='cd -' + +alias q=exit +alias clr=clear +alias sudo='sudo ' +alias rm='rm -i' +alias cp='cp -i' +alias mv='mv -i' +alias mkdir='mkdir -pv' +alias wget='wget -c' +alias path='echo -e ${PATH//:/\\n}' +alias ports='netstat -tulanp' + +alias mk=make +alias gurl='curl --compressed' + +alias shutdown='sudo shutdown' +alias reboot='sudo reboot' + +# An rsync that respects gitignore +rcp() { + # -a = -rlptgoD + # -r = recursive + # -l = copy symlinks as symlinks + # -p = preserve permissions + # -t = preserve mtimes + # -g = preserve owning group + # -o = preserve owner + # -z = use compression + # -P = show progress on transferred file + # -J = don't touch mtimes on symlinks (always errors) + rsync -azPJ \ + --include=.git/ \ + --filter=':- .gitignore' \ + --filter=":- $XDG_CONFIG_HOME/git/ignore" \ + "$@" +}; compdef rcp=rsync +alias rcpd='rcp --delete --delete-after' +alias rcpu='rcp --chmod=go=' +alias rcpdu='rcpd --chmod=go=' + +alias y='xclip -selection clipboard -in' +alias p='xclip -selection clipboard -out' + +alias jc='journalctl -xe' +alias sc=systemctl +alias ssc='sudo systemctl' + +if (( $+commands[exa] )); then + alias exa="exa --group-directories-first --git"; + alias l="exa -blF"; + alias ll="exa -abghilmu"; + alias llm='ll --sort=modified' + alias la="LC_COLLATE=C exa -ablF"; + alias tree='exa --tree' +fi + +if (( $+commands[fasd] )); then + # fuzzy completion with 'z' when called without args + unalias z 2>/dev/null + function z { + [ $# -gt 0 ] && _z "$*" && return + cd "$(_z -l 2>&1 | fzf --height 40% --nth 2.. --reverse --inline-info +s --tac --query "${*##-* }" | sed 's/^[0-9,.]* *//')" + } +fi + +autoload -U zmv + +function take { + mkdir "$1" && cd "$1"; +}; compdef take=mkdir + +function zman { + PAGER="less -g -I -s '+/^ "$1"'" man zshall; +} + +# Create a reminder with human-readable durations, e.g. 15m, 1h, 40s, etc +function r { + local time=$1; shift + sched "$time" "notify-send --urgency=critical 'Reminder' '$@'; ding"; +}; compdef r=sched diff --git a/config/zsh/completion.zsh b/config/zsh/completion.zsh new file mode 100644 index 0000000..89f5d0a --- /dev/null +++ b/config/zsh/completion.zsh @@ -0,0 +1,97 @@ +fpath+=( $ZDOTDIR/completions ) + +# Don't offer history completion; we have fzf, C-r, and +# zsh-history-substring-search for that. +ZSH_AUTOSUGGEST_STRATEGY=(completion) +ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=30 + +# Expand partial paths, e.g. cd f/b/z == cd foo/bar/baz (assuming no ambiguity) +zstyle ':completion:*:paths' path-completion yes + +# Fix slow one-by-one character pasting when bracketed-paste-magic is on. See +# zsh-users/zsh-syntax-highlighting#295 +zstyle ':bracketed-paste-magic' active-widgets '.self-*' + +# Options +setopt COMPLETE_IN_WORD # Complete from both ends of a word. +setopt PATH_DIRS # Perform path search even on command names with slashes. +setopt AUTO_MENU # Show completion menu on a successive tab press. +setopt AUTO_LIST # Automatically list choices on ambiguous completion. +# setopt AUTO_PARAM_SLASH # If completed parameter is a directory, add a trailing slash. +# setopt AUTO_PARAM_KEYS +# setopt FLOW_CONTROL # Disable start/stop characters in shell editor. +unsetopt MENU_COMPLETE # Do not autoselect the first completion entry. +unsetopt COMPLETE_ALIASES # Completion for aliases +# unsetopt ALWAYS_TO_END # Move cursor to the end of a completed word. +unsetopt CASE_GLOB + +# Fuzzy match mistyped completions. +zstyle ':completion:*' completer _complete _list _match _approximate +zstyle ':completion:*:match:*' original only +zstyle ':completion:*:approximate:*' max-errors 1 numeric +# Increase the number of errors based on the length of the typed word. +zstyle -e ':completion:*:approximate:*' max-errors 'reply=($((($#PREFIX+$#SUFFIX)/3))numeric)' +# Don't complete unavailable commands. +zstyle ':completion:*:functions' ignored-patterns '(_*|.*|pre(cmd|exec))' +# Group matches and describe. +zstyle ':completion:*:corrections' format '%B%F{green}%d (errors: %e)%f%b' +zstyle ':completion:*:messages' format '%B%F{yellow}%d%f%b' +zstyle ':completion:*:warnings' format '%B%F{red}No such %d%f%b' +zstyle ':completion:*:errors' format '%B%F{red}No such %d%f%b' +zstyle ':completion:*:descriptions' format $'%{\e[35;1m%}%d%{\e[0m%}' +zstyle ':completion:*:default' list-prompt '%S%M matches%s' +# Omit parent and current directories from completion results when they are +# already named in the input. +zstyle ':completion:*:*:cd:*' ignore-parents parent pwd +# Merge multiple, consecutive slashes in paths +zstyle ':completion:*' squeeze-slashes true +# Don't wrap around when navigating to either end of history +zstyle ':completion:*:history-words' stop yes +zstyle ':completion:*:history-words' remove-all-dups yes +zstyle ':completion:*:history-words' list false +zstyle ':completion:*:history-words' menu yes +# Exclude internal/fake envvars +zstyle ':completion::*:(-command-|export):*' fake-parameters ${${${_comps[(I)-value-*]#*,}%%,*}:#-*-} +# Sory array completion candidates +zstyle ':completion:*:*:-subscript-:*' tag-order indexes parameters +# Complete hostnames from ssh files too +zstyle -e ':completion:*:hosts' hosts 'reply=( + ${=${=${=${${(f)"$(cat {/etc/ssh_,~/.ssh/known_}hosts(|2)(N) 2>/dev/null)"}%%[#| ]*}//\]:[0-9]*/ }//,/ }//\[/ } + ${=${${${${(@M)${(f)"$(cat ~/.ssh/config 2>/dev/null)"}:#Host *}#Host }:#*\**}:#*\?*}} +)' +# Don't complete uninteresting users +zstyle ':completion:*:users' ignored-patterns \ + adm amanda apache avahi beaglidx bin cacti canna clamav daemon \ + dbus distcache dovecot fax ftp games gdm gkrellmd gopher \ + hacluster haldaemon halt hsqldb ident junkbust ldap lp mail \ + mailman mailnull mldonkey mysql nagios \ + named netdump news nfsnobody nobody 'nixbld*' nscd ntp nut nx openvpn \ + operator pcap postfix postgres privoxy pulse pvm quagga radvd \ + rpc rpcuser rpm shutdown squid sshd sync 'systemd-*' uucp vcsa xfs '_*' +# ... unless we really want to. +zstyle '*' single-ignored show +# Ignore multiple entries. +zstyle ':completion:*:(rm|kill|diff):*' ignore-line other +zstyle ':completion:*:rm:*' file-patterns '*:all-files' +# PID completion for kill +zstyle ':completion:*:*:*:*:processes' command 'ps -u $LOGNAME -o pid,user,command -w' +zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#) ([0-9a-z-]#)*=01;36=0=01' +zstyle ':completion:*:*:kill:*' menu yes select +zstyle ':completion:*:*:kill:*' force-list always +zstyle ':completion:*:*:kill:*' insert-ids single +# Man +zstyle ':completion:*:manuals' separate-sections true +zstyle ':completion:*:manuals.(^1*)' insert-sections true +# Media Players +zstyle ':completion:*:*:mpg123:*' file-patterns '*.(mp3|MP3):mp3\ files *(-/):directories' +zstyle ':completion:*:*:mpg321:*' file-patterns '*.(mp3|MP3):mp3\ files *(-/):directories' +zstyle ':completion:*:*:ogg123:*' file-patterns '*.(ogg|OGG|flac):ogg\ files *(-/):directories' +zstyle ':completion:*:*:mocp:*' file-patterns '*.(wav|WAV|mp3|MP3|ogg|OGG|flac):ogg\ files *(-/):directories' +# SSH/SCP/RSYNC +zstyle ':completion:*:(scp|rsync):*' tag-order 'hosts:-host:host hosts:-domain:domain hosts:-ipaddr:ip\ address *' +zstyle ':completion:*:(scp|rsync):*' group-order users files all-files hosts-domain hosts-host hosts-ipaddr +zstyle ':completion:*:ssh:*' tag-order 'hosts:-host:host hosts:-domain:domain hosts:-ipaddr:ip\ address *' +zstyle ':completion:*:ssh:*' group-order users hosts-domain hosts-host users hosts-ipaddr +zstyle ':completion:*:(ssh|scp|rsync):*:hosts-host' ignored-patterns '*(.|:)*' loopback ip6-loopback localhost ip6-localhost broadcasthost +zstyle ':completion:*:(ssh|scp|rsync):*:hosts-domain' ignored-patterns '<->.<->.<->.<->' '^[-[:alnum:]]##(.[-[:alnum:]]##)##' '*@*' +zstyle ':completion:*:(ssh|scp|rsync):*:hosts-ipaddr' ignored-patterns '^(<->.<->.<->.<->|(|::)([[:xdigit:].]##:(#c,2))##(|%*))' '127.0.0.<->' '255.255.255.255' '::1' 'fe80::*' diff --git a/config/zsh/completions/_hey b/config/zsh/completions/_hey new file mode 100755 index 0000000..fe091bf --- /dev/null +++ b/config/zsh/completions/_hey @@ -0,0 +1,36 @@ +#compdef hey + +_hey_dispatch () { + local cmd="$@" + local offset=$# + CURRENT=$((CURRENT-$offset)) + words=($cmd "${words[$((2 + offset)),-1]}") + _$cmd +} + +_hey_command_list () { + hey | sed '1,/Available commands:/d' | awk '{ print $1 }' +} + +_hey () { + local -a commands + IFS=$'\n' commands=($(_hey_command_list)) + + if (( CURRENT == 2 )); then + _describe -t commands "hey commands" commands + return + fi + integer ret=1 + case ${words[2]} in + ch|channel) _hey_dispatch nix-channel ;; + b|build) _hey_dispatch nix-build ;; + ch|check) _hey_dispatch nix flake check ;; + sh|show) _hey_dispatch nix flake show ;; + s|search) _hey_dispatch nix search nixpkgs ;; + -*) _hey_dispatch nix-env ;; + *) _message "Command not found: ${words[2]}" ;; + esac + return ret +} + +_hey diff --git a/config/zsh/config.zsh b/config/zsh/config.zsh new file mode 100644 index 0000000..19929d0 --- /dev/null +++ b/config/zsh/config.zsh @@ -0,0 +1,84 @@ +# Stop TRAMP (in Emacs) from hanging or term/shell from echoing back commands +if [[ $TERM == dumb || -n $INSIDE_EMACS ]]; then + unsetopt zle prompt_cr prompt_subst + whence -w precmd >/dev/null && unfunction precmd + whence -w preexec >/dev/null && unfunction preexec + PS1='$ ' +fi + +## Plugins +# zgen +# we handle compinit ourselves... +export ZGEN_AUTOLOAD_COMPINIT=0 + +# zsh-vi-mode +export ZVM_INIT_MODE=sourcing +export ZVM_VI_INSERT_ESCAPE_BINDKEY=jk + +# fasd +export _FASD_DATA="$XDG_CACHE_HOME/fasd" +export _FASD_VIMINFO="$XDG_CACHE_HOME/viminfo" + +# fzf +if (( $+commands[fd] )); then + export FZF_DEFAULT_OPTS="--reverse --ansi" + export FZF_DEFAULT_COMMAND="fd ." + export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND" + export FZF_ALT_C_COMMAND="fd -t d . $HOME" +fi + + +## ZSH configuration +# Treat these characters as part of a word. +WORDCHARS='_-*?[]~&.;!#$%^(){}<>' + +unsetopt BRACE_CCL # Allow brace character class list expansion. +setopt COMBINING_CHARS # Combine zero-length punc chars (accents) with base char +setopt RC_QUOTES # Allow 'Henry''s Garage' instead of 'Henry'\''s Garage' +setopt HASH_LIST_ALL +unsetopt CORRECT_ALL +unsetopt NOMATCH +unsetopt MAIL_WARNING # Don't print a warning message if a mail file has been accessed. +unsetopt BEEP # Hush now, quiet now. +setopt IGNOREEOF + +## Jobs +setopt LONG_LIST_JOBS # List jobs in the long format by default. +setopt AUTO_RESUME # Attempt to resume existing job before creating a new process. +setopt NOTIFY # Report status of background jobs immediately. +unsetopt BG_NICE # Don't run all background jobs at a lower priority. +unsetopt HUP # Don't kill jobs on shell exit. +unsetopt CHECK_JOBS # Don't report on jobs when shell exit. + +## History +HISTFILE="$XDG_CACHE_HOME/zhistory" +HISTSIZE=100000 # Max events to store in internal history. +SAVEHIST=100000 # Max events to store in history file. + +setopt BANG_HIST # Don't treat '!' specially during expansion. +setopt EXTENDED_HISTORY # Include start time in history records +setopt APPEND_HISTORY # Appends history to history file on exit +setopt INC_APPEND_HISTORY # Write to the history file immediately, not when the shell exits. +setopt SHARE_HISTORY # Share history between all sessions. +setopt HIST_EXPIRE_DUPS_FIRST # Expire a duplicate event first when trimming history. +setopt HIST_IGNORE_DUPS # Do not record an event that was just recorded again. +setopt HIST_IGNORE_ALL_DUPS # Remove old events if new event is a duplicate +setopt HIST_FIND_NO_DUPS # Do not display a previously found event. +setopt HIST_IGNORE_SPACE # Do not record an event starting with a space. +setopt HIST_SAVE_NO_DUPS # Do not write a duplicate event to the history file. +setopt HIST_REDUCE_BLANKS # Minimize unnecessary whitespace +setopt HIST_VERIFY # Do not execute immediately upon history expansion. +setopt HIST_BEEP # Beep when accessing non-existent history. + +## Directories +DIRSTACKSIZE=9 +unsetopt AUTO_CD # Implicit CD slows down plugins +setopt AUTO_PUSHD # Push the old directory onto the stack on cd. +setopt PUSHD_IGNORE_DUPS # Do not store duplicates in the stack. +setopt PUSHD_SILENT # Do not print the directory stack after pushd or popd. +setopt PUSHD_TO_HOME # Push to home directory when no argument is given. +setopt CDABLE_VARS # Change directory to a path stored in a variable. +setopt MULTIOS # Write to multiple descriptors. +setopt EXTENDED_GLOB # Use extended globbing syntax. +unsetopt GLOB_DOTS +unsetopt AUTO_NAME_DIRS # Don't add variable-stored paths to ~ list diff --git a/config/zsh/keybinds.zsh b/config/zsh/keybinds.zsh new file mode 100644 index 0000000..94827d4 --- /dev/null +++ b/config/zsh/keybinds.zsh @@ -0,0 +1,65 @@ +# Other conveniences +bindkey -M viins '^a' beginning-of-line +bindkey -M viins '^d' push-line-or-edit + +# Up arrow: +bindkey '\e[A' history-substring-search-up +bindkey '\eOA' history-substring-search-up +# Down arrow: +bindkey '\e[B' history-substring-search-down +bindkey '\eOB' history-substring-search-down + +# C-z to toggle current process (background/foreground) +fancy-ctrl-z () { + if [[ $#BUFFER -eq 0 ]]; then + BUFFER="fg" + zle accept-line + else + zle push-input + zle clear-screen + fi +} +zle -N fancy-ctrl-z +bindkey '^Z' fancy-ctrl-z + +if (( $+commands[fzf] )); then + bindkey '^R' fzf-history-widget +fi + +# Omni-Completion +if (( $+commands[fasd] )); then + bindkey -M viins '^x^f' fasd-complete-f # C-x C-f to do fasd-complete-f (only files) + bindkey -M viins '^x^d' fasd-complete-d # C-x C-d to do fasd-complete-d (only directories) +fi + +# Completing words in buffer in tmux +if [ -n "$TMUX" ]; then + _tmux_pane_words() { + local expl + local -a w + if [[ -z "$TMUX_PANE" ]]; then + _message "not running inside tmux!" + return 1 + fi + w=( ${(u)=$(tmux capture-pane \; show-buffer \; delete-buffer)} ) + _wanted values expl 'words from current tmux pane' compadd -a w + } + + zle -C tmux-pane-words-prefix complete-word _generic + zle -C tmux-pane-words-anywhere complete-word _generic + + bindkey -M viins '^x^n' tmux-pane-words-prefix + bindkey -M viins '^x^o' tmux-pane-words-anywhere + + zstyle ':completion:tmux-pane-words-(prefix|anywhere):*' completer _tmux_pane_words + zstyle ':completion:tmux-pane-words-(prefix|anywhere):*' ignore-line current + zstyle ':completion:tmux-pane-words-anywhere:*' matcher-list 'b:=* m:{A-Za-z}={a-zA-Z}' +fi + +# Vim's C-x C-l in zsh +history-beginning-search-backward-then-append() { + zle history-beginning-search-backward + zle vi-add-eol +} +zle -N history-beginning-search-backward-then-append +bindkey -M viins '^x^l' history-beginning-search-backward-then-append diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..67d6635 --- /dev/null +++ b/default.nix @@ -0,0 +1,80 @@ +{ inputs, config, lib, pkgs, ... }: +with lib; +with lib.custom; +{ + imports = [ + inputs.home-manager.nixosModules.home-manager + ] ++ (mapModulesRec' (toString ./modules) import); + + # Common config for all nixos machines + environment.variables = { + DOTFILES = config.dotfiles.dir; + DOTFILES_BIN = config.dotfiles.binDir; + }; + + # Configure nix and nixpkgs + environment.variables.NIXPKGS_ALLOW_UNFREE = "1"; + nix = + let + filteredInputs = filterAttrs (n: _: n != "self") inputs; + nixPathInputs = mapAttrsToList (n: v: "${n}=${v}") filteredInputs; + registryInputs = mapAttrs (_: v: { flake = v; }) filteredInputs; + in { + package = pkgs.nixFlakes; + extraOptions = "experimental-features = nix-command flakes"; + nixPath = nixPathInputs ++ [ + "nixpkgs-overlays=${config.dotfiles.dir}/overlays" + "dotfiles=${config.dotfiles.dir}" + ]; + registry = registryInputs // { dotfiles.flake = inputs.self; }; + settings = { + substituters = [ + "https://nix-community.cachix.org" + ]; + trusted-public-keys = [ + "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" + ]; + auto-optimise-store = true; + }; + gc = { + automatic = mkDefault true; + dates = mkDefault "weekly"; + options = mkDefault "--delete-older-than 14d"; + }; + }; + + system.configurationRevision = with inputs; mkIf (self ? rev) self.rev; + system.stateVersion = "21.05"; + + ## Some reasonable, global defaults + # This is here to appease 'nix flake check' for generic hosts with no + # hardware-configuration.nix or fileSystem config. + fileSystems."/".device = mkDefault "/dev/disk/by-label/nixos"; + + # The global useDHCP flag is deprecated, therefore explicitly set to false + # here. Per-interface useDHCP will be mandatory in the future, so we enforce + # this default behavior here. + networking.useDHCP = mkDefault false; + + # Use the latest kernel + boot = { + kernelPackages = mkDefault pkgs.linuxPackages_latest; + loader = { + efi.canTouchEfiVariables = mkDefault true; + systemd-boot.configurationLimit = 10; + systemd-boot.enable = mkDefault true; + }; + }; + + environment.systemPackages = with pkgs; [ + bind + cached-nix-shell + coreutils + git + vim + wget + curl + gnumake + unzip + ]; +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..5745ac8 --- /dev/null +++ b/flake.nix @@ -0,0 +1,70 @@ +{ + description = "Who said nix-nix? It's a dotfiles!"; + + inputs = { + # Core dependencies. + nixpkgs.url = "nixpkgs/nixos-unstable"; + nixpkgs-unstable.url = "nixpkgs/nixpkgs-unstable"; + home-manager = { + url = "github:rycee/home-manager/master"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nur = { + url = "github:nix-community/NUR"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # Extras + emacs-overlay.url = "github:nix-community/emacs-overlay"; + nixos-hardware.url = "github:nixos/nixos-hardware"; + devenv.url = "github:cachix/devenv/v0.6.2"; + }; + + outputs = inputs @ { + self, nixpkgs, nixpkgs-unstable, nur, + emacs-overlay, nixos-hardware, devenv, + ... + }: + let + inherit (builtins) baseNameOf; + inherit (lib) nixosSystem mkIf removeSuffix attrNames attrValues; + inherit (lib.custom) mapModules mapModulesRec mapHosts; + + system = "x86_64-linux"; + + lib = nixpkgs.lib.extend (self: super: { + custom = import ./lib { + inherit pkgs inputs; lib = self; + }; + }); + + mkPkgs = pkgs: extraOverlays: import pkgs { + inherit system; + config.allowUnfree = true; + overlays = extraOverlays ++ (lib.attrValues self.overlays); + }; + + pkgs = mkPkgs nixpkgs [ self.overlay ]; + unstable = mkPkgs nixpkgs-unstable []; + + in { + lib = lib.custom; + + overlay = final: prev: { + inherit unstable; + user = self.packages.${system}; + devenv = devenv.packages.${system}.devenv; + }; + + overlays = mapModules ./overlays import; + + packages.${system} = mapModules ./packages (p: pkgs.callPackage p {}); + + nixosModules = { dotfiles = import ./.; } // mapModulesRec ./modules import; + + nixosConfigurations = mapHosts ./hosts { inherit system; }; + + devShell.${system} = import ./shell.nix { inherit pkgs; }; + + }; +} diff --git a/hosts/common.nix b/hosts/common.nix new file mode 100644 index 0000000..b5518e5 --- /dev/null +++ b/hosts/common.nix @@ -0,0 +1,32 @@ +{ config, lib, ... }: +with builtins; +with lib; +let + blocklist = fetchurl https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts; +in { + networking.extraHosts = '' + 192.168.156.1 router.home + + # Hosts + 192.168.1156.28 elnafo.home + + # Block garbage + ${optionalString config.services.xserver.enable (readFile blocklist)} + ''; + + ## Location config -- since Toronto is my 127.0.0.1 + time.timeZone = mkDefault "Asia/Yekaterinburg"; + + i18n.defaultLocale = mkDefault "en_US.UTF-8"; + i18n.extraLocaleSettings = { + LC_ADDRESS = "en_US.UTF-8"; + LC_IDENTIFICATION = "en_US.UTF-8"; + LC_MEASUREMENT = "en_US.UTF-8"; + LC_MONETARY = "en_US.UTF-8"; + LC_NAME = "en_US.UTF-8"; + LC_NUMERIC = "en_US.UTF-8"; + LC_PAPER = "en_US.UTF-8"; + LC_TELEPHONE = "en_US.UTF-8"; + LC_TIME = "en_US.UTF-8"; + }; +} diff --git a/hosts/elnafo/default.nix b/hosts/elnafo/default.nix new file mode 100644 index 0000000..c4310aa --- /dev/null +++ b/hosts/elnafo/default.nix @@ -0,0 +1,78 @@ +{ pkgs, config, lib, ... }: +{ + imports = [ + ../common.nix + ./hardware-configuration.nix + ]; + + ## Modules + modules = { + desktop = { + gnome.enable = true; + audio.enable = true; + browsers = { + default = "firefox"; + firefox.enable = true; + }; + gaming = { + steam.enable = true; + }; + graphics.enable = true; + media = { + recording.enable = true; + }; + term = { + default = "kgx"; + }; + vm = { + qemu.enable = true; + }; + }; + dev = { + cc.enable = true; + rust.enable = true; + python.enable = true; + }; + editors = { + default = "nvim"; + emacs.enable = true; + vim.enable = true; + }; + shell = { + direnv.enable = true; + git.enable = true; + gnupg.enable = true; + tmux.enable = true; + zsh.enable = true; + }; + services = { + ssh.enable = true; + }; + }; + + networking = { + networkmanager.enable = true; + useDHCP = lib.mkDefault true; + firewall.enable = true; + }; + + ## Local config + programs = { + dconf.enable = true; + ssh.startAgent = true; + }; + + ## Services + services.printing.enable = true; + + services.xserver = { + layout = "us"; + xkbVariant = ""; + videoDrivers = [ "nvidia" ]; + }; + + services.openssh.startWhenNeeded = true; + + + system.stateVersion = "22.11"; +} diff --git a/hosts/elnafo/hardware-configuration.nix b/hosts/elnafo/hardware-configuration.nix new file mode 100644 index 0000000..7510bee --- /dev/null +++ b/hosts/elnafo/hardware-configuration.nix @@ -0,0 +1,90 @@ +{ config, lib, pkgs, modulesPath, ... }: +{ + imports = [ "${modulesPath}/installer/scan/not-detected.nix" ]; + + boot = { + initrd = { + availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ]; + kernelModules = []; + }; + extraModulePackages = []; + kernelModules = [ "kvm-amd" "coretemp" ]; + kernelParams = []; + kernelPackages = pkgs.linuxPackages_latest; + + # Refuse ICMP echo requests on my desktop/laptop; nobody has any business + # pinging them, unlike my servers. + kernel.sysctl."net.ipv4.icmp_echo_ignore_broadcasts" = 1; + + loader = { + systemd-boot.enable = false; + efi = { + canTouchEfiVariables = true; + efiSysMountPoint = "/boot/efi"; + }; + grub = { + devices = [ "nodev" ]; + enable = true; + efiSupport = true; + # version = 2; + useOSProber = true; + }; + }; + }; + + # Modules + modules.hardware = { + audio.enable = true; + fs = { + enable = true; + ssd.enable = true; + }; + nvidia.enable = true; + sensors.enable = true; + }; + + # CPU + #nix.settings.max-jobs = lib.mkDefault 16; + #powerManagement.cpuFreqGovernor = "performance"; + hardware.cpu.amd.updateMicrocode = true; + + # Nvidia, OpenGL + hardware = { + nvidia.nvidiaSettings = true; + nvidia.modesetting.enable = true; + + opengl.enable = true; + opengl.driSupport32Bit = true; + }; + + # Storage + fileSystems = { + "/" = { + device = "/dev/disk/by-uuid/d53d2bcd-36c7-4273-b5b4-6563692ee16c"; + fsType = "ext4"; + options = [ "noatime" ]; + }; + + "/boot/efi" = { + device = "/dev/disk/by-uuid/3117-8F91"; + fsType = "vfat"; + }; + + "/home" = { + device = "/dev/disk/by-uuid/b9e2a42a-4db9-4389-bf75-457bb4da2a30"; + fsType = "ext4"; + options = [ "noatime" ]; + }; + + "/mnt/vault" = { + device = "/dev/disk/by-uuid/34cbaf1c-19c7-412f-8b51-41410f3fee2a"; + fsType = "btrfs"; + options = [ + "nofail" "noauto" "noatime" "x-systemd.automount" "x-systemd.idle-timeout=5min" + "nodev" "nosuid" "noexec" + ]; + }; + }; + + swapDevices = []; +} diff --git a/hosts/niximg.nix b/hosts/niximg.nix new file mode 100644 index 0000000..cfd9282 --- /dev/null +++ b/hosts/niximg.nix @@ -0,0 +1,21 @@ +{ modulesPath, pkgs, config, ... }: +{ + imports = [ + "${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix" + ]; + + # In case of proprietary wireless drivers + nixpkgs.config.allowUnfree = true; + hardware.enableRedistributableFirmware = true; + boot.kernelPackages = pkgs.linuxKernel.packages.linux_5_16; + boot.kernelModules = [ "wl" ]; + boot.extraModulePackages = [ config.boot.kernelPackages.broadcom_sta ]; + + environment.systemPackages = with pkgs; [ + nixFlakes + zsh + git + ]; +} + +# nix-build '' -A config.system.build.isoImage -I nixos-config=./default.nix diff --git a/lib/attrs.nix b/lib/attrs.nix new file mode 100644 index 0000000..6b4ce09 --- /dev/null +++ b/lib/attrs.nix @@ -0,0 +1,22 @@ +{ lib, ... }: +with builtins; +with lib; +rec { + # attrsToList + attrsToList = attrs: mapAttrsToList (name: value: { inherit name value; }) attrs; + + # mapFilterAttrs :: + # (name -> value -> bool) + # (name -> value -> { name = any; value = any; }) + # attrs + mapFilterAttrs = pred: f: attrs: filterAttrs pred (mapAttrs' f attrs); + + # Generate an attribute set by mapping a function over a list of values. + genAttrs' = values: f: listToAttrs (map f values); + + # anyAttrs :: (name -> value -> bool) attrs + anyAttrs = pred: attrs: any (attr: pred attr.name attr.value) (attrsToList attrs); + + # countAttrs :: (name -> value -> bool) attrs + countAttrs = pred: attrs: count (attr: pred attr.name attr.value) (attrsToList attrs); +} diff --git a/lib/default.nix b/lib/default.nix new file mode 100644 index 0000000..1582950 --- /dev/null +++ b/lib/default.nix @@ -0,0 +1,15 @@ +{ inputs, lib, pkgs, ... }: +let + inherit (lib) makeExtensible attrValues foldr; + inherit (modules) mapModules; + + modules = import ./modules.nix { + inherit lib; + self.attrs = import ./attrs.nix { inherit lib; self = {}; }; + }; + + customlib = makeExtensible (self: with self; + mapModules ./. (file: import file { inherit self lib pkgs inputs; }) + ); +in + customlib.extend (self: super: foldr (a: b: a // b) {} (attrValues super)) diff --git a/lib/modules.nix b/lib/modules.nix new file mode 100644 index 0000000..3b0f292 --- /dev/null +++ b/lib/modules.nix @@ -0,0 +1,52 @@ +{ self, lib, ... }: +let + inherit (builtins) attrValues readDir pathExists concatLists; + inherit (lib) id mapAttrsToList filterAttrs hasPrefix hasSuffix nameValuePair removeSuffix; + inherit (self.attrs) mapFilterAttrs; +in +rec { + mapModules = dir: fn: + mapFilterAttrs + (n: v: v != null && !(hasPrefix "_" n)) + (n: v: + let + path = "${toString dir}/${n}"; + in + if v == "directory" && pathExists "${path}/default.nix" + then nameValuePair n (fn path) + else + if v == "regular" && n != "default.nix" && hasSuffix ".nix" n + then nameValuePair (removeSuffix ".nix" n) (fn path) + else nameValuePair "" null + ) + (readDir dir); + + mapModules' = dir: fn: attrValues (mapModules dir fn); + + mapModulesRec = dir: fn: + mapFilterAttrs + (n: v: v != null && !(hasPrefix "_" n)) + (n: v: + let + path = "${toString dir}/${n}"; + in + if v == "directory" + then nameValuePair n (mapModulesRec path fn) + else + if v == "regular" && n != "default.nix" && hasSuffix ".nix" n + then nameValuePair (removeSuffix ".nix" n) (fn path) + else nameValuePair "" null + ) + (readDir dir); + + mapModulesRec' = dir: fn: + let + dirs = mapAttrsToList + (k: _: "${dir}/${k}") + (filterAttrs (n: v: v == "directory" && !(hasPrefix "_" n)) + (readDir dir)); + files = attrValues (mapModules dir id); + paths = files ++ concatLists (map (d: mapModulesRec' d id) dirs); + in + map fn paths; +} diff --git a/lib/nixos.nix b/lib/nixos.nix new file mode 100644 index 0000000..b88be15 --- /dev/null +++ b/lib/nixos.nix @@ -0,0 +1,23 @@ +{ inputs, lib, pkgs, ... }: +with lib; +with lib.custom; +let + sys = "x86_64-linux"; +in { + mkHost = path: attrs @ { system ? sys, ... }: + nixosSystem { + inherit system; + specialArgs = { inherit lib inputs system; }; + modules = [ + { + nixpkgs.pkgs = pkgs; + networking.hostName = mkDefault (removeSuffix ".nix" (baseNameOf path)); + } + (filterAttrs (n: v: !elem n [ "system" ]) attrs) + ../. # /default.nix + (import path) + ]; + }; + + mapHosts = dir: attrs @ { system ? system, ... }: mapModules dir (hostPath: mkHost hostPath attrs); +} diff --git a/lib/options.nix b/lib/options.nix new file mode 100644 index 0000000..6fd6090 --- /dev/null +++ b/lib/options.nix @@ -0,0 +1,15 @@ +{ lib, ... }: +let + inherit (lib) mkOption types; +in +rec { + mkOpt = type: default: mkOption { inherit type default; }; + + mkOpt' = type: default: description: mkOption { inherit type default description; }; + + mkBoolOpt = default: mkOption { + inherit default; + type = types.bool; + example = true; + }; +} diff --git a/modules/desktop/apps/godot.nix b/modules/desktop/apps/godot.nix new file mode 100644 index 0000000..93ca353 --- /dev/null +++ b/modules/desktop/apps/godot.nix @@ -0,0 +1,16 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.desktop.apps.godot; +in { + options.modules.desktop.apps.godot = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + user.packages = with pkgs; [ + godot + ]; + }; +} diff --git a/modules/desktop/audio/default.nix b/modules/desktop/audio/default.nix new file mode 100644 index 0000000..b1bd19b --- /dev/null +++ b/modules/desktop/audio/default.nix @@ -0,0 +1,19 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.desktop.audio; +in { + options.modules.desktop.audio = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + user.packages = with pkgs; [ + lollypop + vlc + beets + flacon + ]; + }; +} diff --git a/modules/desktop/browsers/default.nix b/modules/desktop/browsers/default.nix new file mode 100644 index 0000000..6446f54 --- /dev/null +++ b/modules/desktop/browsers/default.nix @@ -0,0 +1,14 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.desktop.browsers; +in { + options.modules.desktop.browsers = { + default = mkOpt (with types; nullOr str) null; + }; + + config = mkIf (cfg.default != null) { + env.BROWSER = cfg.default; + }; +} diff --git a/modules/desktop/browsers/firefox.nix b/modules/desktop/browsers/firefox.nix new file mode 100644 index 0000000..5daa09d --- /dev/null +++ b/modules/desktop/browsers/firefox.nix @@ -0,0 +1,226 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.desktop.browsers.firefox; +in { + options.modules.desktop.browsers.firefox = with types; { + enable = mkBoolOpt false; + profileName = mkOpt types.str config.user.name; + + settings = mkOpt' (attrsOf (oneOf [ bool int str ])) {} '' + Firefox preferences to set in user.js + ''; + extraConfig = mkOpt' lines "" '' + Extra lines to add to user.js + ''; + + userChrome = mkOpt' lines "" "CSS Styles for Firefox's interface"; + userContent = mkOpt' lines "" "Global CSS Styles for websites"; + }; + + config = mkIf cfg.enable (mkMerge [ { + user.packages = with pkgs; [ + # firefox-wayland + unstable.firefox-bin + (makeDesktopItem { + name = "firefox-private"; + desktopName = "Firefox (Private)"; + genericName = "Open a private Firefox window"; + icon = "firefox"; + exec = "${unstable.firefox-bin}/bin/firefox --private-window"; + categories = [ "Network" ]; + }) + ]; + + # Prevent auto-creation of ~/Desktop. The trailing slash is necessary; see + # https://bugzilla.mozilla.org/show_bug.cgi?id=1082717 + env.XDG_DESKTOP_DIR = "$HOME/"; + + modules.desktop.browsers.firefox.settings = { + # Default to dark theme in DevTools panel + "devtools.theme" = "dark"; + # Enable ETP for decent security (makes firefox containers and many + # common security/privacy add-ons redundant). + "browser.contentblocking.category" = "strict"; + "privacy.donottrackheader.enabled" = true; + "privacy.donottrackheader.value" = 1; + "privacy.purge_trackers.enabled" = true; + # Your customized toolbar settings are stored in + # 'browser.uiCustomization.state'. This tells firefox to sync it between + # machines. WARNING: This may not work across OSes. Since I use NixOS on + # all the machines I use Firefox on, this is no concern to me. + "services.sync.prefs.sync.browser.uiCustomization.state" = true; + # Enable userContent.css and userChrome.css for our theme modules + "toolkit.legacyUserProfileCustomizations.stylesheets" = true; + # Stop creating ~/Downloads! + "browser.download.dir" = "${config.user.home}/downloads"; + # Don't use the built-in password manager. A nixos user is more likely + # using an external one (you are using one, right?). + "signon.rememberSignons" = false; + # Do not check if Firefox is the default browser + "browser.shell.checkDefaultBrowser" = false; + # Disable the "new tab page" feature and show a blank tab instead + # https://wiki.mozilla.org/Privacy/Reviews/New_Tab + # https://support.mozilla.org/en-US/kb/new-tab-page-show-hide-and-customize-top-sites#w_how-do-i-turn-the-new-tab-page-off + "browser.newtabpage.enabled" = false; + "browser.newtab.url" = "about:blank"; + # Disable Activity Stream + # https://wiki.mozilla.org/Firefox/Activity_Stream + "browser.newtabpage.activity-stream.enabled" = false; + "browser.newtabpage.activity-stream.telemetry" = false; + # Disable new tab tile ads & preload + # http://www.thewindowsclub.com/disable-remove-ad-tiles-from-firefox + # http://forums.mozillazine.org/viewtopic.php?p=13876331#p13876331 + # https://wiki.mozilla.org/Tiles/Technical_Documentation#Ping + # https://gecko.readthedocs.org/en/latest/browser/browser/DirectoryLinksProvider.html#browser-newtabpage-directory-source + # https://gecko.readthedocs.org/en/latest/browser/browser/DirectoryLinksProvider.html#browser-newtabpage-directory-ping + "browser.newtabpage.enhanced" = false; + "browser.newtabpage.introShown" = true; + "browser.newtab.preload" = false; + "browser.newtabpage.directory.ping" = ""; + "browser.newtabpage.directory.source" = "data:text/plain,{}"; + # Reduce search engine noise in the urlbar's completion window. The + # shortcuts and suggestions will still work, but Firefox won't clutter + # its UI with reminders that they exist. + "browser.urlbar.suggest.searches" = false; + "browser.urlbar.shortcuts.bookmarks" = false; + "browser.urlbar.shortcuts.history" = false; + "browser.urlbar.shortcuts.tabs" = false; + "browser.urlbar.showSearchSuggestionsFirst" = false; + "browser.urlbar.speculativeConnect.enabled" = false; + # https://bugzilla.mozilla.org/1642623 + "browser.urlbar.dnsResolveSingleWordsAfterSearch" = 0; + # https://blog.mozilla.org/data/2021/09/15/data-and-firefox-suggest/ + "browser.urlbar.suggest.quicksuggest.nonsponsored" = false; + "browser.urlbar.suggest.quicksuggest.sponsored" = false; + # Show whole URL in address bar + "browser.urlbar.trimURLs" = false; + # Disable some not so useful functionality. + "browser.disableResetPrompt" = true; # "Looks like you haven't started Firefox in a while." + "browser.onboarding.enabled" = false; # "New to Firefox? Let's get started!" tour + "browser.aboutConfig.showWarning" = false; # Warning when opening about:config + "media.videocontrols.picture-in-picture.video-toggle.enabled" = false; + "extensions.pocket.enabled" = false; + "extensions.shield-recipe-client.enabled" = false; + "reader.parse-on-load.enabled" = false; # "reader view" + + # Security-oriented defaults + "security.family_safety.mode" = 0; + # https://blog.mozilla.org/security/2016/10/18/phasing-out-sha-1-on-the-public-web/ + "security.pki.sha1_enforcement_level" = 1; + # https://github.com/tlswg/tls13-spec/issues/1001 + "security.tls.enable_0rtt_data" = false; + # Use Mozilla geolocation service instead of Google if given permission + "geo.provider.network.url" = "https://location.services.mozilla.com/v1/geolocate?key=%MOZILLA_API_KEY%"; + "geo.provider.use_gpsd" = false; + # https://support.mozilla.org/en-US/kb/extension-recommendations + "browser.newtabpage.activity-stream.asrouter.userprefs.cfr" = false; + "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons" = false; + "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features" = false; + "extensions.htmlaboutaddons.recommendations.enabled" = false; + "extensions.htmlaboutaddons.discover.enabled" = false; + "extensions.getAddons.showPane" = false; # uses Google Analytics + "browser.discovery.enabled" = false; + # Reduce File IO / SSD abuse + # Otherwise, Firefox bombards the HD with writes. Not so nice for SSDs. + # This forces it to write every 30 minutes, rather than 15 seconds. + "browser.sessionstore.interval" = "1800000"; + # Disable battery API + # https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager + # https://bugzilla.mozilla.org/show_bug.cgi?id=1313580 + "dom.battery.enabled" = false; + # Disable "beacon" asynchronous HTTP transfers (used for analytics) + # https://developer.mozilla.org/en-US/docs/Web/API/navigator.sendBeacon + "beacon.enabled" = false; + # Disable pinging URIs specified in HTML ping= attributes + # http://kb.mozillazine.org/Browser.send_pings + "browser.send_pings" = false; + # Disable gamepad API to prevent USB device enumeration + # https://www.w3.org/TR/gamepad/ + # https://trac.torproject.org/projects/tor/ticket/13023 + "dom.gamepad.enabled" = false; + # Don't try to guess domain names when entering an invalid domain name in URL bar + # http://www-archive.mozilla.org/docs/end-user/domain-guessing.html + "browser.fixup.alternate.enabled" = false; + # Disable telemetry + # https://wiki.mozilla.org/Platform/Features/Telemetry + # https://wiki.mozilla.org/Privacy/Reviews/Telemetry + # https://wiki.mozilla.org/Telemetry + # https://www.mozilla.org/en-US/legal/privacy/firefox.html#telemetry + # https://support.mozilla.org/t5/Firefox-crashes/Mozilla-Crash-Reporter/ta-p/1715 + # https://wiki.mozilla.org/Security/Reviews/Firefox6/ReviewNotes/telemetry + # https://gecko.readthedocs.io/en/latest/browser/experiments/experiments/manifest.html + # https://wiki.mozilla.org/Telemetry/Experiments + # https://support.mozilla.org/en-US/questions/1197144 + # https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/telemetry/internals/preferences.html#id1 + "toolkit.telemetry.unified" = false; + "toolkit.telemetry.enabled" = false; + "toolkit.telemetry.server" = "data:,"; + "toolkit.telemetry.archive.enabled" = false; + "toolkit.telemetry.coverage.opt-out" = true; + "toolkit.coverage.opt-out" = true; + "toolkit.coverage.endpoint.base" = ""; + "experiments.supported" = false; + "experiments.enabled" = false; + "experiments.manifest.uri" = ""; + "browser.ping-centre.telemetry" = false; + # https://mozilla.github.io/normandy/ + "app.normandy.enabled" = false; + "app.normandy.api_url" = ""; + "app.shield.optoutstudies.enabled" = false; + # Disable health reports (basically more telemetry) + # https://support.mozilla.org/en-US/kb/firefox-health-report-understand-your-browser-perf + # https://gecko.readthedocs.org/en/latest/toolkit/components/telemetry/telemetry/preferences.html + "datareporting.healthreport.uploadEnabled" = false; + "datareporting.healthreport.service.enabled" = false; + "datareporting.policy.dataSubmissionEnabled" = false; + + # Disable crash reports + "breakpad.reportURL" = ""; + "browser.tabs.crashReporting.sendReport" = false; + "browser.crashReports.unsubmittedCheck.autoSubmit2" = false; # don't submit backlogged reports + + # Disable Form autofill + # https://wiki.mozilla.org/Firefox/Features/Form_Autofill + "browser.formfill.enable" = false; + "extensions.formautofill.addresses.enabled" = false; + "extensions.formautofill.available" = "off"; + "extensions.formautofill.creditCards.available" = false; + "extensions.formautofill.creditCards.enabled" = false; + "extensions.formautofill.heuristics.enabled" = false; + }; + + # Use a stable profile name so we can target it in themes + home.file = let cfgPath = ".mozilla/firefox"; in { + "${cfgPath}/profiles.ini".text = '' + [Profile0] + Name=default + IsRelative=1 + Path=${cfg.profileName}.default + Default=1 + + [General] + StartWithLastProfile=1 + Version=2 + ''; + + "${cfgPath}/${cfg.profileName}.default/user.js" = mkIf (cfg.settings != {} || cfg.extraConfig != "") { + text = '' + ${concatStrings (mapAttrsToList (name: value: '' + user_pref("${name}", ${builtins.toJSON value}); + '') cfg.settings)} + ${cfg.extraConfig} + ''; + }; + + "${cfgPath}/${cfg.profileName}.default/chrome/userChrome.css" = mkIf (cfg.userChrome != "") { + text = cfg.userChrome; + }; + + "${cfgPath}/${cfg.profileName}.default/chrome/userContent.css" = mkIf (cfg.userContent != "") { + text = cfg.userContent; + }; + }; + }]); +} diff --git a/modules/desktop/default.nix b/modules/desktop/default.nix new file mode 100644 index 0000000..ecb1af0 --- /dev/null +++ b/modules/desktop/default.nix @@ -0,0 +1,62 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.desktop; +in { + config = mkIf config.services.xserver.enable { + assertions = [ + { + assertion = (countAttrs (n: v: n == "enable" && value) cfg) < 2; + message = "Can't have more than one desktop environment enabled at a time"; + } + { + assertion = + let + srv = config.services; + in + srv.xserver.enable || srv.sway.enable || + !(anyAttrs (n: v: isAttrs v && anyAttrs (n: v: isAttrs v && v.enable)) cfg); + message = "Can't enable a desktop app without a desktop environment"; + } + ]; + + user.packages = with pkgs; [ + xclip + xdotool + qgnomeplatform # QPlatformTheme for a better Qt application inclusion in GNOME + libsForQt5.qtstyleplugin-kvantum # SVG-based Qt5 theme engine plus a config tool and extra theme + ]; + + fonts = { + fontDir.enable = true; + enableGhostscriptFonts = true; + fonts = with pkgs; [ + ubuntu_font_family + dejavu_fonts + symbola + nerdfonts + ]; + }; + + # Try really hard to get QT to respect my GTK theme. + env = { + GTK_DATA_PREFIX = [ "${config.system.path}" ]; + QT_QPA_PLATFORMTHEME = "gnome"; + QT_STYLE_OVERRIDE = "kvantum"; + } + + services.xserver.displayManager.sessionCommands = '' + # GTK2_RC_FILES must be available to the display manager. + export GTK2_RC_FILES="$XDG_CONFIG_HOME/gtk-2.0/gtkrc" + ''; + + # Clean up leftovers, as much as we can + system.userActivationScripts.cleanupHome = '' + pushd "${config.user.home}" + rm -rf .compose-cache .nv .pki .dbus .fehbg + [ -s .xsession-errors ] || rm -f .xsession-errors* + popd + ''; + }; +} diff --git a/modules/desktop/documents/default.nix b/modules/desktop/documents/default.nix new file mode 100644 index 0000000..4f1f359 --- /dev/null +++ b/modules/desktop/documents/default.nix @@ -0,0 +1,17 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.desktop.documents; +in { + options.modules.desktop.documents = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + user.packages = with pkgs; [ + onlyoffice-bin + tectonic + ]; + }; +} diff --git a/modules/desktop/gaming/lutris.nix b/modules/desktop/gaming/lutris.nix new file mode 100644 index 0000000..e8278db --- /dev/null +++ b/modules/desktop/gaming/lutris.nix @@ -0,0 +1,19 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.desktop.gaming.lutris; +in { + options.modules.desktop.gaming.lutris = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + user.packages = with pkgs; [ + lutris + wine + winetricks + gamemode + ]; + }; +} diff --git a/modules/desktop/gaming/steam.nix b/modules/desktop/gaming/steam.nix new file mode 100644 index 0000000..065e22b --- /dev/null +++ b/modules/desktop/gaming/steam.nix @@ -0,0 +1,17 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.desktop.gaming.steam; +in { + options.modules.desktop.gaming.steam = with types; { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + programs.steam.enable = true; + + # better for steam proton games + systemd.extraConfig = "DefaultLimitNOFILE=1048576"; + }; +} diff --git a/modules/desktop/gnome.nix b/modules/desktop/gnome.nix new file mode 100644 index 0000000..d8b4534 --- /dev/null +++ b/modules/desktop/gnome.nix @@ -0,0 +1,25 @@ +{ config, options, lib, pkgs, ... }: +with builtins; +with lib; +with lib.custom; +let + cfg = config.modules.desktop.gnome; +in { + options.modules.desktop.gnome = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ + gnomeExtensions.containers + gnomeExtensions.tray-icons-reloaded + ]; + + services.xserver = { + enable = true; + displayManager.gdm.enable = true; + desktopManager.gnome.enable = true; + }; + + }; +} diff --git a/modules/desktop/graphics/default.nix b/modules/desktop/graphics/default.nix new file mode 100644 index 0000000..2f41452 --- /dev/null +++ b/modules/desktop/graphics/default.nix @@ -0,0 +1,50 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.desktop.media.graphics; + configDir = config.dotfiles.configDir; +in { + options.modules.desktop.media.graphics = { + enable = mkBoolOpt false; + tools.enable = mkBoolOpt true; + raster.enable = mkBoolOpt true; + vector.enable = mkBoolOpt true; + sprites.enable = mkBoolOpt false; + models.enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + user.packages = with pkgs; + (if cfg.tools.enable then [ + font-manager + imagemagick + ] else []) ++ + + # replaces illustrator & indesign + (if cfg.vector.enable then [ + unstable.inkscape + ] else []) ++ + + # Replaces photoshop + (if cfg.raster.enable then [ + krita + gimp + gimpPlugins.resynthesizer # content-aware scaling in gimp + ] else []) ++ + + # Sprite sheets & animation + (if cfg.sprites.enable then [ + aseprite-unfree + ] else []) ++ + + # 3D modelling + (if cfg.models.enable then [ + unstable.blender-hip + ] else []); + + home.configFile = mkIf cfg.raster.enable { + "GIMP/2.10" = { source = "${configDir}/gimp"; recursive = true; }; + }; + }; +} diff --git a/modules/desktop/media/recording.nix b/modules/desktop/media/recording.nix new file mode 100644 index 0000000..bbe6bae --- /dev/null +++ b/modules/desktop/media/recording.nix @@ -0,0 +1,27 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.desktop.media.recording; +in { + options.modules.desktop.media.recording = { + enable = mkBoolOpt false; + audio.enable = mkBoolOpt true; + video.enable = mkBoolOpt true; + }; + + config = mkIf cfg.enable { + services.pipewire.jack.enable = true; + + user.packages = with pkgs; + (if cfg.audio.enable then [ + unstable.audacity-gtk3 + ] else []) ++ + + (if cfg.video.enable then [ + unstable.obs-studio + unstable.handbrake + ffmpeg + ] else []); + }; +} diff --git a/modules/desktop/term/default.nix b/modules/desktop/term/default.nix new file mode 100644 index 0000000..9c551f5 --- /dev/null +++ b/modules/desktop/term/default.nix @@ -0,0 +1,16 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.desktop.term; +in { + options.modules.desktop.term = { + default = mkOpt types.str "xterm"; + }; + + config = { + services.xserver.desktopManager.xterm.enable = mkDefault (cfg.default == "xterm"); + + env.TERMINAL = cfg.default; + }; +} diff --git a/modules/desktop/vm/qemu.nix b/modules/desktop/vm/qemu.nix new file mode 100644 index 0000000..51ef420 --- /dev/null +++ b/modules/desktop/vm/qemu.nix @@ -0,0 +1,21 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.desktop.vm.qemu; +in { + options.modules.desktop.vm.qemu = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ + qemu + ]; + }; +} + +# Creating an image: +# qemu-img create -f qcow2 disk.img +# Creating a snapshot (don't tamper with disk.img): +# qemu-img create -f qcow2 -b disk.img snapshot.img diff --git a/modules/dev/cc.nix b/modules/dev/cc.nix new file mode 100644 index 0000000..a10830b --- /dev/null +++ b/modules/dev/cc.nix @@ -0,0 +1,29 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + devCfg = config.modules.dev; + cfg = devCfg.cc; +in { + options.modules.dev.cc = { + enable = mkBoolOpt false; + xdg.enable = mkBoolOpt devCfg.xdg.enable; + }; + + config = mkMerge [ + (mkIf cfg.enable { + user.packages = with pkgs; [ + clang + gcc + bear + gdb + cmake + llvmPackages.libcxx + ]; + }) + + (mkIf cfg.xdg.enable { + # TODO + }) + ]; +} diff --git a/modules/dev/common-lisp.nix b/modules/dev/common-lisp.nix new file mode 100644 index 0000000..b53a69b --- /dev/null +++ b/modules/dev/common-lisp.nix @@ -0,0 +1,25 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + devCfg = config.modules.dev; + cfg = devCfg.common-lisp; +in { + options.modules.dev.common-lisp = { + enable = mkBoolOpt false; + xdg.enable = mkBoolOpt devCfg.xdg.enable; + }; + + config = mkMerge [ + (mkIf cfg.enable { + user.packages = with pkgs; [ + sbcl + lispPackages.quicklisp + ]; + }) + + (mkIf cfg.xdg.enable { + # TODO + }) + ]; +} diff --git a/modules/dev/default.nix b/modules/dev/default.nix new file mode 100644 index 0000000..f3aee1f --- /dev/null +++ b/modules/dev/default.nix @@ -0,0 +1,14 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.dev; +in { + options.modules.dev = { + xdg.enable = mkBoolOpt true; + }; + + config = mkIf cfg.xdg.enable { + # TODO + }; +} diff --git a/modules/dev/lua.nix b/modules/dev/lua.nix new file mode 100644 index 0000000..ffbe99a --- /dev/null +++ b/modules/dev/lua.nix @@ -0,0 +1,25 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + devCfg = config.modules.dev; + cfg = devCfg.lua; +in { + options.modules.dev.lua = { + enable = mkBoolOpt false; + xdg.enable = mkBoolOpt devCfg.enableXDG; + }; + + config = mkMerge [ + (mkIf cfg.enable { + user.packages = with pkgs; [ + lua + luaPackages.moonscript + ]; + }) + + (mkIf cfg.xdg.enable { + # TODO + }) + ]; +} diff --git a/modules/dev/python.nix b/modules/dev/python.nix new file mode 100644 index 0000000..94b4f92 --- /dev/null +++ b/modules/dev/python.nix @@ -0,0 +1,41 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + devCfg = config.modules.dev; + cfg = devCfg.python; +in { + options.modules.dev.python = { + enable = mkBoolOpt false; + xdg.enable = mkBoolOpt devCfg.xdg.enable; + }; + + config = mkMerge [ + (mkIf cfg.enable { + user.packages = with pkgs; [ + python310 + python310Packages.pip + python310Packages.ipython + python310Packages.black + python310Packages.setuptools + python310Packages.pylint + python310Packages.poetry + ]; + + environment.shellAliases = { + py = "python"; + }; + }) + + (mkIf cfg.xdg.enable { + env.IPYTHONDIR = "$XDG_CONFIG_HOME/ipython"; + env.PIP_CONFIG_FILE = "$XDG_CONFIG_HOME/pip/pip.conf"; + env.PIP_LOG_FILE = "$XDG_DATA_HOME/pip/log"; + env.PYLINTHOME = "$XDG_DATA_HOME/pylint"; + env.PYLINTRC = "$XDG_CONFIG_HOME/pylint/pylintrc"; + env.PYTHONSTARTUP = "$XDG_CONFIG_HOME/python/pythonrc"; + env.PYTHON_EGG_CACHE = "$XDG_CACHE_HOME/python-eggs"; + env.JUPYTER_CONFIG_DIR = "$XDG_CONFIG_HOME/jupyter"; + }) + ]; +} diff --git a/modules/dev/rust.nix b/modules/dev/rust.nix new file mode 100644 index 0000000..88b0d57 --- /dev/null +++ b/modules/dev/rust.nix @@ -0,0 +1,31 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + devCfg = config.modules.dev; + cfg = devCfg.rust; +in { + options.modules.dev.rust = { + enable = mkBoolOpt false; + xdg.enable = mkBoolOpt devCfg.xdg.enable; + }; + + config = mkMerge [ + (mkIf cfg.enable { + user.packages = [ + pkgs.rustup + ]; + env.PATH = [ "$(${pkgs.yarn}/bin/yarn global bin)" ]; + environment.shellAliases = { + rs = "rustc"; + rsp = "rustup"; + }; + }) + + (mkIf cfg.xdg.enable { + env.RUSTUP_HOME = "$XDG_DATA_HOME/rustup"; + env.CARGO_HOME = "$XDG_DATA_HOME/cargo"; + env.PATH = [ "$CARGO_HOME/bin" ]; + }) + ]; +} diff --git a/modules/dev/shell.nix b/modules/dev/shell.nix new file mode 100644 index 0000000..b92c0c9 --- /dev/null +++ b/modules/dev/shell.nix @@ -0,0 +1,24 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + devCfg = config.modules.dev; + cfg = devCfg.shell; +in { + options.modules.dev.shell = { + enable = mkBoolOpt false; + xdg.enable = mkBoolOpt devCfg.xdg.enable; + }; + + config = mkMerge [ + (mkIf cfg.enable { + user.packages = with pkgs; [ + shellcheck + ]; + }) + + (mkIf cfg.xdg.enable { + # TODO + }) + ]; +} diff --git a/modules/editors/default.nix b/modules/editors/default.nix new file mode 100644 index 0000000..f24b5fb --- /dev/null +++ b/modules/editors/default.nix @@ -0,0 +1,14 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.editors; +in { + options.modules.editors = { + default = mkOpt types.str "vim"; + }; + + config = mkIf (cfg.default != null) { + env.EDITOR = cfg.default; + }; +} diff --git a/modules/editors/emacs.nix b/modules/editors/emacs.nix new file mode 100644 index 0000000..ec99994 --- /dev/null +++ b/modules/editors/emacs.nix @@ -0,0 +1,64 @@ +{ config, lib, pkgs, inputs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.editors.emacs; + configDir = config.dotfiles.configDir; +in { + options.modules.editors.emacs = { + enable = mkBoolOpt false; + doom = rec { + enable = mkBoolOpt false; + forgeUrl = mkOpt types.str "https://github.com"; + repoUrl = mkOpt types.str "${forgeUrl}/doomemacs/doomemacs"; + configRepoUrl = mkOpt types.str "${forgeUrl}/hlissner/doom-emacs-private"; + }; + }; + + config = mkIf cfg.enable { + nixpkgs.overlays = [ inputs.emacs-overlay.overlay ]; + + user.packages = with pkgs; [ + ## Emacs itself + binutils # native-comp needs 'as', provided by this + # 28.2 + native-comp + ((emacsPackagesFor emacsNativeComp).emacsWithPackages (epkgs: [ epkgs.vterm ])) + + ## Doom dependencies + git + (ripgrep.override {withPCRE2 = true;}) + gnutls # for TLS connectivity + + ## Optional dependencies + fd # faster projectile indexing + imagemagick # for image-dired + (mkIf (config.programs.gnupg.agent.enable) pinentry_emacs) # in-emacs gnupg prompts + zstd # for undo-fu-session/undo-tree compression + + ## Module dependencies + # :checkers spell + (aspellWithDicts (ds: with ds; [ en en-computers en-science ])) + # :tools editorconfig + editorconfig-core-c # per-project style config + # :tools lookup & :lang org +roam + sqlite + # :lang latex & :lang org (latex previews) + texlive.combined.scheme-medium + ]; + + env.PATH = [ "$XDG_CONFIG_HOME/emacs/bin" ]; + + modules.shell.zsh.rcFiles = [ "${configDir}/emacs/aliases.zsh" ]; + + fonts.fonts = [ pkgs.emacs-all-the-icons-fonts ]; + + system.userActivationScripts = mkIf cfg.doom.enable { + installDoomEmacs = '' + if [ ! -d "$XDG_CONFIG_HOME/emacs" ]; then + git clone --depth=1 --single-branch "${cfg.doom.repoUrl}" "$XDG_CONFIG_HOME/emacs" + git clone "${cfg.doom.configRepoUrl}" "$XDG_CONFIG_HOME/doom" + fi + ''; + }; + }; +} diff --git a/modules/editors/vim.nix b/modules/editors/vim.nix new file mode 100644 index 0000000..9a32eb2 --- /dev/null +++ b/modules/editors/vim.nix @@ -0,0 +1,23 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.editors.vim; +in { + options.modules.editors.vim = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + user.packages = with pkgs; [ + editorconfig-core-c + unstable.neovim + ]; + + # env.VIMINIT = "let \\$MYVIMRC='\\$XDG_CONFIG_HOME/nvim/init.vim' | source \\$MYVIMRC"; + + environment.shellAliases = { + vim = "nvim"; + }; + }; +} diff --git a/modules/hardware/audio.nix b/modules/hardware/audio.nix new file mode 100644 index 0000000..921100c --- /dev/null +++ b/modules/hardware/audio.nix @@ -0,0 +1,41 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.hardware.audio; +in { + options.modules.hardware.audio = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + services.pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + }; + + security.rtkit.enable = true; + + environment.systemPackages = with pkgs; [ + easyeffects + ]; + + # HACK Prevents ~/.esd_auth files by disabling the esound protocol module + # for pulseaudio, which I likely don't need. Is there a better way? + hardware.pulseaudio.configFile = + let + inherit (pkgs) runCommand pulseaudio; + paConfigFile = runCommand "disablePulseaudioEsoundModule" + { buildInputs = [ pulseaudio ]; } '' + mkdir "$out" + cp ${pulseaudio}/etc/pulse/default.pa "$out/default.pa" + sed -i -e 's|load-module module-esound-protocol-unix|# ...|' "$out/default.pa" + ''; + in + mkIf config.hardware.pulseaudio.enable "${paConfigFile}/default.pa"; + + user.extraGroups = [ "audio" ]; + }; +} diff --git a/modules/hardware/bluetooth.nix b/modules/hardware/bluetooth.nix new file mode 100644 index 0000000..1c9a6da --- /dev/null +++ b/modules/hardware/bluetooth.nix @@ -0,0 +1,14 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.hardware.bluetooth; +in { + options.modules.hardware.bluetooth = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + hardware.bluetooth.enable = true; + }; +} diff --git a/modules/hardware/fs.nix b/modules/hardware/fs.nix new file mode 100644 index 0000000..4953a91 --- /dev/null +++ b/modules/hardware/fs.nix @@ -0,0 +1,45 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.hardware.fs; +in { + options.modules.hardware.fs = { + enable = mkBoolOpt false; + zfs.enable = mkBoolOpt false; + ssd.enable = mkBoolOpt false; + # TODO automount.enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable (mkMerge [ + { + programs.udevil.enable = true; + + # Support for more filesystems, mostly to support external drives + environment.systemPackages = with pkgs; [ + sshfs + exfat + ntfs3g + ]; + } + + (mkIf (!cfg.zfs.enable && cfg.ssd.enable) { + services.fstrim.enable = true; + }) + + (mkIf cfg.zfs.enable (mkMerge [ + { + boot.loader.grub.copyKernels = true; + boot.supportedFilesystems = [ "zfs" ]; + boot.zfs.devNodes = "/dev/disk/by-partuuid"; + services.zfs.autoScrub.enable = true; + } + + (mkIf cfg.ssd.enable { + # Will only TRIM SSDs; skips over HDDs + services.fstrim.enable = false; + services.zfs.trim.enable = true; + }) + ])) + ]); +} diff --git a/modules/hardware/nvidia.nix b/modules/hardware/nvidia.nix new file mode 100644 index 0000000..525016d --- /dev/null +++ b/modules/hardware/nvidia.nix @@ -0,0 +1,29 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.hardware.nvidia; +in { + options.modules.hardware.nvidia = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + hardware.opengl = { + enable = true; + driSupport = true; + driSupport32Bit = true; + }; + + services.xserver.videoDrivers = [ "nvidia" ]; + + environment.systemPackages = with pkgs; [ + # Respect XDG conventions, damn it! + (writeScriptBin "nvidia-settings" '' + #!${stdenv.shell} + mkdir -p "$XDG_CONFIG_HOME/nvidia" + exec ${config.boot.kernelPackages.nvidia_x11.settings}/bin/nvidia-settings --config="$XDG_CONFIG_HOME/nvidia/settings" + '') + ]; + }; +} diff --git a/modules/hardware/sensors.nix b/modules/hardware/sensors.nix new file mode 100644 index 0000000..818f1e0 --- /dev/null +++ b/modules/hardware/sensors.nix @@ -0,0 +1,16 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custon; +let + cfg = config.modules.hardware.sensors; +in { + options.modules.hardware.sensors = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + user.packages = [ + pkgs.lm_sensors + ]; + }; +} diff --git a/modules/hardware/wacom.nix b/modules/hardware/wacom.nix new file mode 100644 index 0000000..1c762e9 --- /dev/null +++ b/modules/hardware/wacom.nix @@ -0,0 +1,24 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.hardware.wacom; +in +{ + options.modules.hardware.wacom = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + services.xserver.wacom.enable = true; + # TODO Move this to udev + system.userActivationScripts.wacom = '' + # lock tablet to main display + if xinput list --id-only "Wacom Intuos Pro S Pen stylus" 2>&1 >/dev/null; then + xinput map-to-output $(xinput list --id-only "Wacom Intuos Pro S Pen stylus") DVI-I-1 + xinput map-to-output $(xinput list --id-only "Wacom Intuos Pro S Pen eraser") DVI-I-1 + xinput map-to-output $(xinput list --id-only "Wacom Intuos Pro S Pen cursor") DVI-I-1 + fi + ''; + }; +} diff --git a/modules/options.nix b/modules/options.nix new file mode 100644 index 0000000..ac976d3 --- /dev/null +++ b/modules/options.nix @@ -0,0 +1,79 @@ +{ config, options, lib, home-manager, ... }: +with lib; +with lib.custom; +{ + options = with types; { + user = mkOpt attrs {}; + + home = { + file = mkOpt' attrs {} "Files to place directly in $HOME"; + configFile = mkOpt' attrs {} "Files to place in $XDG_CONFIG_HOME"; + dataFile = mkOpt' attrs {} "Files to place in $XDG_DATA_HOME"; + dconfSettings = mkOpt' attrs {} "Configuration of dconf settings"; + }; + + dotfiles = { + dir = mkOpt path (removePrefix "/mnt" (findFirst pathExists (toString ../.) [ + "/mnt/etc/dotfiles" + "/etc/dotfiles" + ])); + binDir = mkOpt path "${config.dotfiles.dir}/bin"; + configDir = mkOpt path "${config.dotfiles.dir}/config"; + modulesDir = mkOpt path "${config.dotfiles.dir}/modules"; + themesDir = mkOpt path "${config.dotfiles.modulesDir}/themes"; + }; + + env = mkOption { + type = attrsOf (oneOf [ str path (listOf (either str path)) ]); + apply = mapAttrs (n: v: if isList v then concatMapStringsSep ":" (x: toString x) v else (toString v)); + default = {}; + description = "TODO"; + }; + }; + + config = { + user = { + name = let name = builtins.getEnv "USER"; in if elem user [ "" "root" ] then "nafaryus" else user; + description = "L-Nafaryus"; + extraGroups = [ "wheel" ]; + isNormalUser = true; + home = "/home/${name}"; + group = "users"; + uid = 1000; + }; + + # Install user packages to /etc/profiles instead. Necessary for + # nixos-rebuild build-vm to work. + home-manager = { + useUserPackages = true; + users.${config.user.name} = { + home = { + file = mkAliasDefinitions options.home.file; + # Necessary for home-manager to work with flakes, otherwise it will + # look for a nixpkgs channel. + stateVersion = config.system.stateVersion; + }; + xdg = { + configFile = mkAliasDefinitions options.home.configFile; + dataFile = mkAliasDefinitions options.home.dataFile; + }; + dconf = { + settings = mkAliasDefinitions options.home.dconfSettings; + }; + }; + }; + + users.users.${config.user.name} = mkAliasDefinitions options.user; + + nix.settings = let users = [ "root" config.user.name ]; in { + trusted-users = users; + allowed-users = users; + }; + + # must already begin with pre-existing PATH. Also, can't use binDir here, + # because it contains a nix store path. + env.PATH = [ "$DOTFILES_BIN" "$XDG_BIN_HOME" "$PATH" ]; + + environment.extraInit = concatStringsSep "\n" (mapAttrsToList (n: v: "export ${n}=\"${v}\"") config.env); + }; +} diff --git a/modules/security.nix b/modules/security.nix new file mode 100644 index 0000000..43077e0 --- /dev/null +++ b/modules/security.nix @@ -0,0 +1,78 @@ +{ config, lib, ... }: +{ + ## System security tweaks + # sets hidepid=2 on /proc (make process info visible only to owning user) + # NOTE Was removed on nixpkgs-unstable because it doesn't do anything + # security.hideProcessInformation = true; + # Prevent replacing the running kernel w/o reboot + security.protectKernelImage = true; + + # + boot.tmp.useTmpfs = true; + + # tmpfs = /tmp is mounted in ram. Doing so makes temp file management speedy + # on ssd systems, and volatile! Because it's wiped on reboot. + boot.tmpOnTmpfs = lib.mkDefault true; + + # If not using tmpfs, which is naturally purged on reboot, we must clean it + # /tmp ourselves. /tmp should be volatile storage! + boot.cleanTmpDir = lib.mkDefault (!config.boot.tmpOnTmpfs); + + # Fix a security hole in place for backwards compatibility. See desc in + # nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix + boot.loader.systemd-boot.editor = false; + + boot.kernel.sysctl = { + # The Magic SysRq key is a key combo that allows users connected to the + # system console of a Linux kernel to perform some low-level commands. + # Disable it, since we don't need it, and is a potential security concern. + "kernel.sysrq" = 0; + + ## TCP hardening + # Prevent bogus ICMP errors from filling up logs. + "net.ipv4.icmp_ignore_bogus_error_responses" = 1; + # Reverse path filtering causes the kernel to do source validation of + # packets received from all interfaces. This can mitigate IP spoofing. + "net.ipv4.conf.default.rp_filter" = 1; + "net.ipv4.conf.all.rp_filter" = 1; + # Do not accept IP source route packets (we're not a router) + "net.ipv4.conf.all.accept_source_route" = 0; + "net.ipv6.conf.all.accept_source_route" = 0; + # Don't send ICMP redirects (again, we're on a router) + "net.ipv4.conf.all.send_redirects" = 0; + "net.ipv4.conf.default.send_redirects" = 0; + # Refuse ICMP redirects (MITM mitigations) + "net.ipv4.conf.all.accept_redirects" = 0; + "net.ipv4.conf.default.accept_redirects" = 0; + "net.ipv4.conf.all.secure_redirects" = 0; + "net.ipv4.conf.default.secure_redirects" = 0; + "net.ipv6.conf.all.accept_redirects" = 0; + "net.ipv6.conf.default.accept_redirects" = 0; + # Protects against SYN flood attacks + "net.ipv4.tcp_syncookies" = 1; + # Incomplete protection again TIME-WAIT assassination + "net.ipv4.tcp_rfc1337" = 1; + + ## TCP optimization + # TCP Fast Open is a TCP extension that reduces network latency by packing + # data in the sender’s initial TCP SYN. Setting 3 = enable TCP Fast Open for + # both incoming and outgoing connections: + "net.ipv4.tcp_fastopen" = 3; + # Bufferbloat mitigations + slight improvement in throughput & latency + "net.ipv4.tcp_congestion_control" = "bbr"; + "net.core.default_qdisc" = "cake"; + }; + boot.kernelModules = [ "tcp_bbr" ]; + + # Change me later! + user.initialPassword = "nixos"; + users.users.root.initialPassword = "nixos"; + + # So we don't have to do this later... + security.acme.acceptTerms = true; + + # Set sudo command timeout to 30 minutes + security.sudo.extraConfig = '' + Defaults timestamp_timeout=30 + ''; +} diff --git a/modules/services/calibre.nix b/modules/services/calibre.nix new file mode 100644 index 0000000..710572a --- /dev/null +++ b/modules/services/calibre.nix @@ -0,0 +1,16 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.services.calibre; +in { + options.modules.services.calibre = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + services.calibre-server.enable = true; + + networking.firewall.allowedTCPPorts = [ 8080 ]; + }; +} diff --git a/modules/services/fail2ban.nix b/modules/services/fail2ban.nix new file mode 100644 index 0000000..8003b1e --- /dev/null +++ b/modules/services/fail2ban.nix @@ -0,0 +1,38 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.services.fail2ban; +in { + options.modules.services.fail2ban = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + services.fail2ban = { + enable = true; + ignoreIP = [ "127.0.0.1/16" "192.168.1.0/24" ]; + banaction-allports = "iptables-allports"; + bantime-increment = { + enable = true; + maxtime = "168h"; + factor = "4"; + }; + jails.DEFAULT = '' + blocktype = DROP + bantime = 1h + findtime = 1h + ''; + }; + + # Extra filters + environment.etc = { + "fail2ban/filter.d/gitea.conf".text = '' + [Definition] + failregex = .*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from + ignoreregex = + journalmatch = _SYSTEMD_UNIT=gitea.service + ''; + }; + }; +} diff --git a/modules/services/gitea.nix b/modules/services/gitea.nix new file mode 100644 index 0000000..ebfecc5 --- /dev/null +++ b/modules/services/gitea.nix @@ -0,0 +1,49 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.services.gitea; +in { + options.modules.services.gitea = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + # Allows git@... clone addresses rather than gitea@... + users.users.git = { + useDefaultShell = true; + home = "/var/lib/gitea"; + group = "gitea"; + isSystemUser = true; + }; + + user.extraGroups = [ "gitea" ]; + + services.gitea = { + enable = true; + lfs.enable = true; + + user = "git"; + database.user = "git"; + + # We're assuming SSL-only connectivity + cookieSecure = true; + # Only log what's important, but Info is necessary for fail2ban to work + log.level = "Info"; + settings = { + server.DISABLE_ROUTER_LOG = true; + database.LOG_SQL = false; + service.ENABLE_BASIC_AUTHENTICATION = false; + }; + + dump.interval = "daily"; + }; + + services.fail2ban.jails.gitea = '' + enabled = true + filter = gitea + banaction = %(banaction_allports)s + maxretry = 5 + ''; + }; +} diff --git a/modules/services/jellyfin.nix b/modules/services/jellyfin.nix new file mode 100644 index 0000000..99b2c7b --- /dev/null +++ b/modules/services/jellyfin.nix @@ -0,0 +1,21 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.services.jellyfin; +in { + options.modules.services.jellyfin = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + services.jellyfin.enable = true; + + networking.firewall = { + allowedTCPPorts = [ 8096 ]; + allowedUDPPorts = [ 8096 ]; + }; + + user.extraGroups = [ "jellyfin" ]; + }; +} diff --git a/modules/services/nginx.nix b/modules/services/nginx.nix new file mode 100644 index 0000000..ed9fd56 --- /dev/null +++ b/modules/services/nginx.nix @@ -0,0 +1,74 @@ +{ config, options, lib, pkgs, ... }: +with builtins; +with lib; +with lib.custom; +let + cfg = config.modules.services.nginx; +in { + options.modules.services.nginx = { + enable = mkBoolOpt false; + enableCloudflareSupport = mkBoolOpt false; + }; + + config = mkMerge [ + (mkIf cfg.enable { + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + user.extraGroups = [ "nginx" ]; + + services.nginx = { + enable = true; + + # Use recommended settings + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + # Reduce the permitted size of client requests, to reduce the likelihood + # of buffer overflow attacks. This can be tweaked on a per-vhost basis, + # as needed. + clientMaxBodySize = "256k"; # default 10m + # Significantly speed up regex matchers + appendConfig = ''pcre_jit on;''; + commonHttpConfig = '' + client_body_buffer_size 4k; # default: 8k + large_client_header_buffers 2 4k; # default: 4 8k + + map $sent_http_content_type $expires { + default off; + text/html 10m; + text/css max; + application/javascript max; + application/pdf max; + ~image/ max; + } + ''; + }; + }) + + (mkIf cfg.enableCloudflareSupport { + services.nginx.commonHttpConfig = '' + ${concatMapStrings (ip: "set_real_ip_from ${ip};\n") + (filter (line: line != "") + (splitString "\n" '' + ${readFile (fetchurl "https://www.cloudflare.com/ips-v4/")} + ${readFile (fetchurl "https://www.cloudflare.com/ips-v6/")} + ''))} + real_ip_header CF-Connecting-IP; + ''; + }) + ]; +} + +# Helpful nginx snippets +# +# Set expires headers for static files and turn off logging. +# location ~* ^.+\.(js|css|swf|xml|txt|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|r ss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav |bmp|rtf)$ { +# access_log off; log_not_found off; expires 30d; +# } +# +# Deny all attempts to access PHP Files in the uploads directory +# location ~* /(?:uploads|files)/.*\.php$ { +# deny all; +# } diff --git a/modules/services/ssh.nix b/modules/services/ssh.nix new file mode 100644 index 0000000..7b023a7 --- /dev/null +++ b/modules/services/ssh.nix @@ -0,0 +1,23 @@ +{ options, config, lib, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.services.ssh; +in { + options.modules.services.ssh = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + services.openssh = { + enable = true; + kbdInteractiveAuthentication = false; + passwordAuthentication = false; + }; + + user.openssh.authorizedKeys.keys = + if config.user.name == "nafaryus" + then [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9pBG3Ak8hO4eQFA8roajDeZkKSPv2NsgZADQoV8bNEvsqNssqvpnoBKZCCKFv+Hqvf0tcTcdkRedUJh+9f/CI8dEuYiNzRyCFjYnfyFyUlEjNh/MaTonJEFEO4QsbapxQx+Buc+/jPCdwhUEbf1jvJV0oQy7TptXOn87cYQSuqqeubv+YwBqXUfMIFbsxH+ePZ9rX+N9sLdYpW2k9W1i8g2oNPrEpa3ICW2qhf/bshUhmDLB9te+vt1qMu0jmzpllnbaJJ57rDuL6XLaWqU/PD6uC0j1axf8AMxf00YvrLvMJ+T9hWlLe0mwNsgkhRzBE2/T+PYkUfvWvzqGLtIBZ nafaryus" ] + else []; + }; +} diff --git a/modules/services/syncthing.nix b/modules/services/syncthing.nix new file mode 100644 index 0000000..76697fd --- /dev/null +++ b/modules/services/syncthing.nix @@ -0,0 +1,20 @@ +{ config, options, pkgs, lib, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.services.syncthing; +in { + options.modules.services.syncthing = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + services.syncthing = rec { + enable = true; + openDefaultPorts = true; + user = config.user.name; + configDir = "${config.user.home}/.config/syncthing"; + dataDir = "${config.user.home}/.local/share/syncthing"; + }; + }; +} diff --git a/modules/services/transmission.nix b/modules/services/transmission.nix new file mode 100644 index 0000000..f881b83 --- /dev/null +++ b/modules/services/transmission.nix @@ -0,0 +1,32 @@ +{ config, options, pkgs, lib, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.services.transmission; +in { + options.modules.services.transmission = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + services.transmission = { + enable = true; + home = "${config.user.home}/torrents"; + settings = { + incomplete-dir-enabled = true; + rpc-whitelist = "127.0.0.1,192.168.*.*"; + rpc-host-whitelist = "*"; + rpc-host-whitelist-enabled = true; + ratio-limit = 0; + ratio-limit-enabled = true; + }; + }; + + networking.firewall = { + allowedTCPPorts = [ 51413 ]; + allowedUDPPorts = [ 51413 ]; + }; + + user.extraGroups = [ "transmission" ]; + }; +} diff --git a/modules/services/wireguard.nix b/modules/services/wireguard.nix new file mode 100644 index 0000000..0f40b12 --- /dev/null +++ b/modules/services/wireguard.nix @@ -0,0 +1,22 @@ +{ options, config, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.services.wireguard; + udpPorts = mapAttrs' (_: cfg: cfg.listenPort) config.networking.wireguard.interfaces; + interfaces = elem 0 (mapAttrs' (n: _: n) config.networking.interfaces); + wgInterfaces = elem 0 (mapAttrs' (n: _: n) config.networking.wireguard.interfaces); +in { + options.modules.services.wireguard = with types; { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + networking = { + firewall.allowedUDPPorts = udpPorts; + nat.enable = true; + nat.externalInterface = mkDefault interfaces; + nat.internalInterfaces = mkDefault wgInterfaces; + }; + }; +} diff --git a/modules/shell/direnv.nix b/modules/shell/direnv.nix new file mode 100644 index 0000000..3900c90 --- /dev/null +++ b/modules/shell/direnv.nix @@ -0,0 +1,15 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.shell.direnv; +in { + options.modules.shell.direnv = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + user.packages = [ pkgs.direnv ]; + modules.shell.zsh.rcInit = ''eval "$(direnv hook zsh)"''; + }; +} diff --git a/modules/shell/git.nix b/modules/shell/git.nix new file mode 100644 index 0000000..ff24bbc --- /dev/null +++ b/modules/shell/git.nix @@ -0,0 +1,30 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.shell.git; + configDir = config.dotfiles.configDir; +in { + options.modules.shell.git = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + user.packages = with pkgs; [ + gitAndTools.git-annex + unstable.gitAndTools.gh + gitAndTools.git-open + gitAndTools.diff-so-fancy + (mkIf config.modules.shell.gnupg.enable gitAndTools.git-crypt) + act + ]; + + home.configFile = { + "git/config".source = "${configDir}/git/config"; + "git/ignore".source = "${configDir}/git/ignore"; + "git/attributes".source = "${configDir}/git/attributes"; + }; + + modules.shell.zsh.rcFiles = [ "${configDir}/git/aliases.zsh" ]; + }; +} diff --git a/modules/shell/gnupg.nix b/modules/shell/gnupg.nix new file mode 100644 index 0000000..bb7ada8 --- /dev/null +++ b/modules/shell/gnupg.nix @@ -0,0 +1,31 @@ +{ config, options, lib, pkgs, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.shell.gnupg; +in { + options.modules.shell.gnupg = with types; { + enable = mkBoolOpt false; + cacheTTL = mkOpt int 3600; # 1hr + }; + + config = mkIf cfg.enable { + environment.variables.GNUPGHOME = "$XDG_CONFIG_HOME/gnupg"; + + programs.gnupg.agent.enable = true; + + user.packages = [ + pkgs.tomb + ]; + + # HACK Without this config file you get "No pinentry program" on 20.03. + # programs.gnupg.agent.pinentryFlavor doesn't appear to work, and this + # is cleaner than overriding the systemd unit. + home.configFile."gnupg/gpg-agent.conf" = { + text = '' + default-cache-ttl ${toString cfg.cacheTTL} + pinentry-program ${pkgs.pinentry.gtk2}/bin/pinentry + ''; + }; + }; +} diff --git a/modules/shell/pass.nix b/modules/shell/pass.nix new file mode 100644 index 0000000..66ee5f1 --- /dev/null +++ b/modules/shell/pass.nix @@ -0,0 +1,24 @@ +{ config, options, pkgs, lib, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.shell.pass; +in { + options.modules.shell.pass = with types; { + enable = mkBoolOpt false; + passwordStoreDir = mkOpt str "$HOME/.secrets/password-store"; + }; + + config = mkIf cfg.enable { + user.packages = with pkgs; [ + (pass.withExtensions (exts: [ + exts.pass-otp + exts.pass-genphrase + ] ++ + (if config.modules.shell.gnupg.enable + then [ exts.pass-tomb ] + else []))) + ]; + env.PASSWORD_STORE_DIR = cfg.passwordStoreDir; + }; +} diff --git a/modules/shell/tmux.nix b/modules/shell/tmux.nix new file mode 100644 index 0000000..ca9f7dc --- /dev/null +++ b/modules/shell/tmux.nix @@ -0,0 +1,46 @@ +{ config, options, pkgs, lib, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.shell.tmux; + configDir = config.dotfiles.configDir; +in { + options.modules.shell.tmux = with types; { + enable = mkBoolOpt false; + rcFiles = mkOpt (listOf (either str path)) []; + }; + + config = mkIf cfg.enable { + user.packages = with pkgs; [ tmux ]; + + modules.theme.onReload.tmux = "${pkgs.tmux}/bin/tmux source-file $TMUX_HOME/extraInit"; + + modules.shell.zsh = { + rcInit = "_cache tmuxifier init -"; + rcFiles = [ "${configDir}/tmux/aliases.zsh" ]; + }; + + home.configFile = { + "tmux" = { source = "${configDir}/tmux"; recursive = true; }; + "tmux/extraInit" = { + text = '' + #!/usr/bin/env bash + # This file is auto-generated by nixos, don't edit by hand! + ${concatMapStrings (path: "tmux source-file '${path}'\n") cfg.rcFiles} + tmux run-shell '${pkgs.tmuxPlugins.copycat}/share/tmux-plugins/copycat/copycat.tmux' + tmux run-shell '${pkgs.tmuxPlugins.prefix-highlight}/share/tmux-plugins/prefix-highlight/prefix_highlight.tmux' + tmux run-shell '${pkgs.tmuxPlugins.yank}/share/tmux-plugins/yank/yank.tmux' + tmux run-shell '${pkgs.tmuxPlugins.resurrect}/share/tmux-plugins/resurrect/resurrect.tmux' + ''; + executable = true; + }; + }; + + env = { + PATH = [ "$TMUXIFIER/bin" ]; + TMUX_HOME = "$XDG_CONFIG_HOME/tmux"; + TMUXIFIER = "$XDG_DATA_HOME/tmuxifier"; + TMUXIFIER_LAYOUT_PATH = "$XDG_DATA_HOME/tmuxifier"; + }; + }; +} diff --git a/modules/shell/zsh.nix b/modules/shell/zsh.nix new file mode 100644 index 0000000..32f900d --- /dev/null +++ b/modules/shell/zsh.nix @@ -0,0 +1,88 @@ +{ config, options, pkgs, lib, ... }: +with lib; +with lib.custom; +let + cfg = config.modules.shell.zsh; + configDir = config.dotfiles.configDir; +in { + options.modules.shell.zsh = with types; { + enable = mkBoolOpt false; + + aliases = mkOpt (attrsOf (either str path)) {}; + + rcInit = mkOpt' lines "" '' + Zsh lines to be written to $XDG_CONFIG_HOME/zsh/extra.zshrc and sourced by + $XDG_CONFIG_HOME/zsh/.zshrc + ''; + envInit = mkOpt' lines "" '' + Zsh lines to be written to $XDG_CONFIG_HOME/zsh/extra.zshenv and sourced + by $XDG_CONFIG_HOME/zsh/.zshenv + ''; + + rcFiles = mkOpt (listOf (either str path)) []; + envFiles = mkOpt (listOf (either str path)) []; + }; + + config = mkIf cfg.enable { + users.defaultUserShell = pkgs.zsh; + + programs.zsh = { + enable = true; + enableCompletion = true; + # I init completion myself, because enableGlobalCompInit initializes it + # too soon, which means commands initialized later in my config won't get + # completion, and running compinit twice is slow. + enableGlobalCompInit = false; + promptInit = ""; + }; + + user.packages = with pkgs; [ + zsh + nix-zsh-completions + bat + exa + fasd + fd + fzf + jq + ripgrep + tldr + ]; + + env = { + ZDOTDIR = "$XDG_CONFIG_HOME/zsh"; + ZSH_CACHE = "$XDG_CACHE_HOME/zsh"; + ZGEN_DIR = "$XDG_DATA_HOME/zgenom"; + }; + + home.configFile = { + # Write it recursively so other modules can write files to it + "zsh" = { source = "${configDir}/zsh"; recursive = true; }; + + # Why am I creating extra.zsh{rc,env} when I could be using extraInit? + # Because extraInit generates those files in /etc/profile, and mine just + # write the files to ~/.config/zsh; where it's easier to edit and tweak + # them in case of issues or when experimenting. + "zsh/extra.zshrc".text = + let + aliasLines = mapAttrsToList (n: v: "alias ${n}=\"${v}\"") cfg.aliases; + in '' + # This file was autogenerated, do not edit it! + ${concatStringsSep "\n" aliasLines} + ${concatMapStrings (path: "source '${path}'\n") cfg.rcFiles} + ${cfg.rcInit} + ''; + + "zsh/extra.zshenv".text = '' + # This file is autogenerated, do not edit it! + ${concatMapStrings (path: "source '${path}'\n") cfg.envFiles} + ${cfg.envInit} + ''; + }; + + system.userActivationScripts.cleanupZgen = '' + rm -rf $ZSH_CACHE + rm -fv $ZGEN_DIR/init.zsh{,.zwc} + ''; + }; +} diff --git a/modules/xdg.nix b/modules/xdg.nix new file mode 100644 index 0000000..2489f0e --- /dev/null +++ b/modules/xdg.nix @@ -0,0 +1,48 @@ +{ config, home-manager, pkgs, ... }: +{ + home-manager.users.${config.user.name}.xdg ={ + enable = true; + # Until https://github.com/rycee/home-manager/issues/1213 is solved. + configFile."mimeapps.list".force = true; + mime.enable = true; + mimeApps = { + enable = true; + defaultApplications = { + "text/html" = "firefox.desktop"; + "x-scheme-handler/http" = "firefox.desktop"; + "x-scheme-handler/https" = "firefox.desktop"; + "x-scheme-handler/about" = "firefox.desktop"; + "x-scheme-handler/unknown" = "firefox.desktop"; + }; + }; + }; + + environment = { + sessionVariables = { + # These are the defaults, and xdg.enable does set them, but due to load + # order, they're not set before environment.variables are set, which could + # cause race conditions. + XDG_CACHE_HOME = "$HOME/.cache"; + XDG_CONFIG_HOME = "$HOME/.config"; + XDG_DATA_HOME = "$HOME/.local/share"; + XDG_BIN_HOME = "$HOME/.local/bin"; + # Firefox really wants a desktop directory to exist + XDG_DESKTOP_DIR = "~/tmp"; + # Setting this for Electon apps that do not respect mime default apps + DEFAULT_BROWSER = "${pkgs.firefox}/bin/firefox"; + }; + variables = { + __GL_SHADER_DISK_CACHE_PATH = "$XDG_CACHE_HOME/nv"; + CUDA_CACHE_PATH = "$XDG_CACHE_HOME/nv"; + HISTFILE = "$XDG_DATA_HOME/bash/history"; + INPUTRC = "$XDG_CONFIG_HOME/readline/inputrc"; + LESSHISTFILE = "$XDG_CACHE_HOME/lesshst"; + WGETRC = "$XDG_CONFIG_HOME/wgetrc"; + }; + + extraInit = '' + export XAUTHORITY=/tmp/Xauthority + [ -e ~/.Xauthority ] && mv -f ~/.Xauthority "$XAUTHORITY" + ''; + }; +} diff --git a/overlays/.git-keep b/overlays/.git-keep new file mode 100644 index 0000000..e69de29 diff --git a/packages/.git-keep b/packages/.git-keep new file mode 100644 index 0000000..e69de29 diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..ca3dcfa --- /dev/null +++ b/shell.nix @@ -0,0 +1,17 @@ +{ pkgs ? import {} }: +with pkgs; +let + nixBin = writeShellScriptBin "nix" '' + ${nixFlakes}/bin/nix --option experimental-features "nix-command flakes" "$@" + ''; +in + mkShell { + buildInputs = [ + git + nix-zsh-completions + ]; + shellHook = '' + export FLAKE="$(pwd)" + export PATH="$FLAKE/bin:${nixBin}/bin:$PATH" + ''; + } diff --git a/templates/default.nix b/templates/default.nix new file mode 100644 index 0000000..b83300c --- /dev/null +++ b/templates/default.nix @@ -0,0 +1,7 @@ +{ + # Projects + #project-nodejs = { + # path = ./project/XYZ; + # description = ""; + #}; +} diff --git a/templates/module/default.nix b/templates/module/default.nix new file mode 100644 index 0000000..b75d32a --- /dev/null +++ b/templates/module/default.nix @@ -0,0 +1,15 @@ +{ config, options, lib, pkgs, ... }: +with builtins; +with lib; +with lib.custom; +let + cfg = config.modules.X.Y; +in { + options.modules.X.Y = { + enable = mkBoolOpt false; + }; + + config = mkIf cfg.enable { + + }; +} diff --git a/templates/project/.git-keep b/templates/project/.git-keep new file mode 100644 index 0000000..e69de29