{ config, lib, pkgs, homeage, ... }: let hostName = "surtur"; in { home.username = "$USER"; home.sessionVariables.HOSTNAME = "${hostName}"; home.homeDirectory = "/home/$USER"; home.stateVersion = "23.11"; # build configuration and switch: # ➜ home-manager switch --no-out-link -b backup --flake~/utils/dotfiles#$HOST # alternatively, install HM with: # nix profile install --priority 0 home-manager # hit the issue described here, waiting until resolved: # https://github.com/nix-community/home-manager/issues/2848 programs.home-manager.enable = true; home.packages = with pkgs; [ direnv # lorri and arion are apparently provided by cachix#devenv alejandra statix niv rnix-lsp duf dua du-dust zellij cloak headscale btop sops neovim ccache ]; homeage = { # Absolute path to identity (created not through home-manager) identityPaths = [ "~/.ssh/theEd" ]; # "activation" if system doesn't support systemd installationType = "systemd"; file."sops-age-keys.txt" = { # Path to encrypted file tracked by the git repository source = ./secrets/sops-keys.age; # can be "copies" or "symlink" symlinks = [".config/sops/age/keys.txt"]; }; file."envs" = { source = ./secrets/envs.age; }; # infra secrets. file."infra-backend" = { source = ./secrets/infra-backend.age; }; file."infra-vars" = { source = ./secrets/infra-vars.age; }; file."pcmt_gitea_token" = { source = ./secrets/pcmt_gitea_token.age; }; }; imports = [ ./nix/programs.nix ]; home.file = { ".config/kitty/kitty.conf" = { source = .config/kitty/kitty.conf; }; ".vimrc" = { source = ./.vim/vimrc; }; ".vim/deoplete.vimrc.vim" = { source = ./.vim/deoplete.vimrc.vim; }; ".vim/gotags.vimrc.vim" = { source = ./.vim/gotags.vimrc.vim; }; ".vim/python.vimrc.vim" = { source = ./.vim/python.vimrc.vim; }; ".config/nvim/init.vim" = { source = ./.config/nvim/init.vim; }; ".config/sway/config" = { source = ./.config/sway/config; }; ".config/sway/env" = { source = ./.config/sway/env; }; ".config/sway/inputs" = { source = ./.config/sway/inputs; }; ".config/sway/config.d/dracula.sway" = { source = ./.config/sway/config.d/dracula.sway; }; ".config/waybar/config" = { source = ./.config/waybar/config; }; ".config/waybar/style.css" = { source = ./.config/waybar/style.css; }; ".config/waybar/modules/storage.sh" = { source = ./.config/waybar/modules/storage.sh; }; ".config/kanshi/config" = { source = ./.config/kanshi/config; }; ".config/mako/config" = { source = ./.config/mako/config; }; ".local/bin/swws.py" = { source = ./bin/swws.py; }; ".local/bin/sway-locker" = { source = ./bin/sway-locker; }; ".config/direnv/direnv.toml" = { source = ./.config/direnv/direnv.toml; }; ".config/fusuma/config-wl.yml" = { source = ./.config/fusuma/config-wl.yml; }; ".config/fusuma/config.yml" = { source = ./.config/fusuma/config.yml; }; ".config/git/config-common" = { source = ./.config/git/config; }; # host-specific gitconfig. ".config/git/config.${hostName}" = { text = '' [user] name = ${hostName} email = wanderer@dotya.ml signingkey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBtG6NCgdLHX4ztpfvYNRaslKWZcl6KdTc1DehVH4kAL ''; }; ".config/git/config" = { text = '' [include] path = ~/.config/git/config-common [include] path = ~/.config/git/config.${hostName} ''; }; ".config/git/allowed_signers" = { source = ./.config/git/allowed_signers; }; # begin zsh-related. ".zshrc" = { source = ./.zshrc; }; ".zshenv" = { source = ./.zshenv; }; ".zprofile" = { source = ./.zprofile; }; ".zsh" = { source = ./.zsh; recursive = true; }; ".zsh/bemenu-dracula" = { source = pkgs.fetchFromGitHub { owner = "dracula"; repo = "bemenu"; rev = "9b1165b3d97e3b2a74c6ce220781b78d8a11febf"; sha256 = "sha256-TwfkEZ1aTkHur+jCqRsaqvzOw6qpH0L4pvYqkx7iCDk="; }; }; # end zsh-related. ".cargo/config.toml" = { source = .cargo/config.toml; }; ".npmrc" = { text = '' prefix=''${HOME}/.npm-packages audit=false fund=false ''; }; ".config/swaylock/config" = { source = .config/swaylock/config; }; ".config/tridactyl/tridactylrc" = { source = .config/tridactyl/tridactylrc; }; ".config/bat/config" = { source = .config/bat/config; }; ".ncpamixer.conf" = { source = .config/ncpamixer.conf; }; ".gdbinit" = { text = '' set auto-load safe-path /nix/store set history save on set history size 10000 set history remove-duplicates 100 set history filename ~/.gdb_history ''; }; ".local/bin/fuzzypassage" = { executable = true; text = '' PREFIX="''${PASSAGE_DIR:-$HOME/.passage/store}" FZF_DEFAULT_OPTS="" name="$(find "$PREFIX" -type f -name '*.age' | \ sed -e "s|$PREFIX/||" -e 's|\.age$||' | \ fzf --height 40% --reverse --no-multi)" passage "''${@}" "$name" ''; }; ".local/bin/battery.sh" = { source = bin/battery.sh; executable = true; }; ".local/bin/localbsync" = { source = bin/localbsync; executable = true; }; ".local/bin/parec-wr" = { source = bin/parec-wr; executable = true; }; ".local/bin/pscbg" = { source = bin/pscbg; executable = true; }; ".local/bin/qst_up" = { source = bin/qst_up; executable = true; }; ".local/bin/winprint.sh" = { source = bin/winprint.sh; executable = true; }; ".local/bin/authenticator.sh" = { text = '' #!/bin/sh # adopted from https://wiki.archlinux.org/index.php/Google_Authenticator # This is the path to the Google Authenticator app file. It's typically located # in /data under Android. Copy it to your PC in a safe location and specify the # path to it here. #DB="/path/to/com.google.android.apps.authenticator/databases/databases" DB="$1" if [ $# -ne 1 ]; then printf "authenticator\n" printf "usage: authenticator \n" printf "\tThis is the path to the Authenticator app owned SQLite db file.\n" printf "\tCopy it to your PC to a safe location and specify the path to it here.\n" exit 1 fi # On most Android systems with sufficient user access, the Google Authenticator # database can be copied off the device and accessed directly, as it is an # sqlite3 database. This shell script will read a Google Authenticator database # and generate live codes for each key found: sqlite3 "$DB" 'SELECT email,secret FROM accounts;' | while read A do NAME=`echo "$A" | cut -d '|' -f 1` KEY=`echo "$A" | cut -d '|' -f 2` CODE=`oathtool --totp -b "$KEY"` echo -e "\e[1;32m$CODE\e[0m - \e[1;33m$NAME\e[0m" done ''; executable = true; }; ".local/bin/createarchive.sh" = { text = '' #!/bin/bash if [ $# -ne 1 ]; then printf "createarchive\n" printf "usage: createarchive \n" printf "warning: the archive will be moved to "backups" directory (`echo $dest`)\n" exit 1 fi # what this does in short: tar, compress, timestamp, shred the tar, mv .xz to pwd and display it logdate="$(date +%Y%m%dT%H%M%S)" basedir="$1" tmpdir=$(mktemp -d "${TMPDIR:-/tmp/}$(basename $0).XXXXXXXXXX") #/run/user/$(id -u) tmpfs 0700 perms f="`cd $basedir; pwd | tr '/' ' ' | sed 's/^.* / /' | cut -c2-`" > /dev/null g="$logdate-$f.tar" dest=~/MEGA/Private/backups doathing() { cd $basedir/.. tar cfv "$tmpdir/$g" "$f" && \ xz -vzk9e "$tmpdir/$g" -S .xz && \ rsync -avP "$tmpdir/$g.xz" "$dest" && \ shred -zuv "$tmpdir/$g" "$tmpdir/$g.xz" && \ printf "\n" ls -latr "$dest/$g.xz" } if [ ! -d $1 ]; then echo "$1 is not a directory" exit 1 else echo `pwd` echo "$f" echo "$1" doathing trap "rm -rfv $tmpdir" 0 1 3 15 exit $? fi ''; executable = true; }; ".config/qutebrowser/userscripts/localhost" = { executable = true; text = '' #!/usr/bin/env bash export BEMENU_OPTS="--tb '#6272a4' --tf '#f8f8f2' --fb '#282a36' --ff '#f8f8f2' --nb '#282a36' --nf '#6272a4' --hb '#44475a' --hf '#50fa7b' --sb '#44475a' --sf '#50fa7b' --scb '#282a36' --scf '#ff79c6' -p 'localhost: ▶' --fork -l 5 --fn 'FiraCode Retina 17'" if [[ $1 -eq 'list' ]] && [[ -z $QUTE_COUNT ]]; then PORTS="$(ss -nltp | tail -n +2 | awk '{print $4}' | awk -F: '{print $2}')" QUTE_COUNT=$(echo "$PORTS" | bemenu -n ) fi # echo open -t localhost:''${QUTE_COUNT:-8080} > $QUTE_FIFO [ -n "$QUTE_COUNT" ] && echo open -t localhost:"''${QUTE_COUNT}" > $QUTE_FIFO ''; }; ".config/qutebrowser/userscripts/speak" = { executable = true; text = '' #!/bin/bash export IFS=$'\n' pkill -f qute_speak || { ~/.local/bin/gtts-cli "$QUTE_SELECTED_TEXT" | mpv --no-video --speed=1.26 - # ~/.local/bin/gtts-cli "$QUTE_SELECTED_TEXT" > /tmp/qute_speak.mp3 # mpv /tmp/qute_speak.mp3 } ''; }; ".config/qutebrowser/userscripts/dark_mode.user" = { executable = true; text = '' #!/bin/zsh # on a new system cp DR.js DarkReader.user.js darkreader_file="$HOME/.config/qutebrowser/greasemonkey/DarkReader.user.js" enabled="^//DarkReader.disable();" darkreader_enabled="$(grep -q -e "$enabled" $darkreader_file; echo $?)" # echo $darkreader_enabled if [[ "$(echo $darkreader_enabled)" == "1" ]]; then # enable DarkReader by commenting out the line that disables it. sed -i --follow-symlink 's/DarkReader.disable()/\/\/DarkReader.disable()/' "$darkreader_file" else # disable DarkReader sed -i --follow-symlink 's/\/\/DarkReader.disable()/DarkReader.disable()/' "$darkreader_file" fi ''; }; ".config/qutebrowser/greasemonkey/DR.js" = { text = '' // ==UserScript== // @name Dark Reader (Unofficial) // @icon https://darkreader.org/images/darkreader-icon-256x256.png // @namespace DarkReader // @description Inverts the brightness of pages to reduce eye strain // @version 4.9.52 // @author https://github.com/darkreader/darkreader#contributors // @homepageURL https://darkreader.org/ | https://github.com/darkreader/darkreader // @run-at document-end // @grant none // @exclude https://git.dotya.ml* // @exclude https://dotya.ml* // @exclude https://status.dotya.ml* // @exclude https://searxng.dotya.ml* // @exclude https://grafana.dotya.ml* // @exclude https://github.com* // @exclude https://dnswatch.com* // @exclude https://docs.immudb.io* // @exclude https://woodpecker-ci.org* // @exclude https://duckduckgo.com* // @exclude https://www.redit.com* // @exclude https://codeberg.org* // @include http* // @require https://cdn.jsdelivr.net/npm/darkreader/darkreader.min.js // @noframes // ==/UserScript== DarkReader.enable({ brightness: 105, contrast: 105, sepia: 0 }); DarkReader.disable(); ''; }; ".config/qutebrowser/userscripts/code_select.py" = { executable = true; source = .local/share/qutebrowser/userscripts/code_select.py; }; ".config/qutebrowser/userscripts/getbib" = { executable = true; source = .local/share/qutebrowser/userscripts/getbib; }; ".config/qutebrowser/userscripts/qute-gemini" = { executable = true; source = .local/share/qutebrowser/userscripts/qute-gemini; }; ".config/qutebrowser/userscripts/qute-gemini-tab" = { executable = true; source = .local/share/qutebrowser/userscripts/qute-gemini; }; ".config/qutebrowser/config.py" = { source = .config/qutebrowser/config.py; }; ".local/bin/workqb" = { text = '' #!/bin/zsh qutebrowser \ --restore work \ --config ~/.config/qutebrowser/config.py \ --basedir ~/.config/qutebrowser-work \ & disown ''; executable = true; }; # ref: https://go.dev/blog/pprof ".local/bin/xtime" = { text = '' #!/bin/sh /usr/bin/time -f '%Uu %Ss %er %MkB %C' "$@" ''; executable = true; }; ".local/bin/xdp-screen-cast" = { text = '' #!/usr/bin/python3 # ref: https://gitlab.gnome.org/-/snippets/19 import re import signal import dbus from gi.repository import GLib from dbus.mainloop.glib import DBusGMainLoop import gi gi.require_version('Gst', '1.0') from gi.repository import GObject, Gst DBusGMainLoop(set_as_default=True) Gst.init(None) loop = GLib.MainLoop() bus = dbus.SessionBus() request_iface = 'org.freedesktop.portal.Request' screen_cast_iface = 'org.freedesktop.portal.ScreenCast' pipeline = None def terminate(): if pipeline is not None: self.player.set_state(Gst.State.NULL) loop.quit() request_token_counter = 0 session_token_counter = 0 sender_name = re.sub(r'\.', r'_', bus.get_unique_name()[1:]) def new_request_path(): global request_token_counter request_token_counter = request_token_counter + 1 token = 'u%d'%request_token_counter path = '/org/freedesktop/portal/desktop/request/%s/%s'%(sender_name, token) return (path, token) def new_session_path(): global session_token_counter session_token_counter = session_token_counter + 1 token = 'u%d'%session_token_counter path = '/org/freedesktop/portal/desktop/session/%s/%s'%(sender_name, token) return (path, token) def screen_cast_call(method, callback, *args, options={}): (request_path, request_token) = new_request_path() bus.add_signal_receiver(callback, 'Response', request_iface, 'org.freedesktop.portal.Desktop', request_path) options['handle_token'] = request_token method(*(args + (options, )), dbus_interface=screen_cast_iface) def on_gst_message(bus, message): type = message.type if type == Gst.MessageType.EOS or type == Gst.MessageType.ERROR: terminate() def play_pipewire_stream(node_id): empty_dict = dbus.Dictionary(signature="sv") fd_object = portal.OpenPipeWireRemote(session, empty_dict, dbus_interface=screen_cast_iface) fd = fd_object.take() pipeline = Gst.parse_launch('pipewiresrc fd=%d path=%u ! videoconvert ! xvimagesink'%(fd, node_id)) pipeline.set_state(Gst.State.PLAYING) pipeline.get_bus().connect('message', on_gst_message) def on_start_response(response, results): if response != 0: print("Failed to start: %s"%response) terminate() return print("streams:") for (node_id, stream_properties) in results['streams']: print("stream {}".format(node_id)) play_pipewire_stream(node_id) def on_select_sources_response(response, results): if response != 0: print("Failed to select sources: %d"%response) terminate() return print("sources selected") global session screen_cast_call(portal.Start, on_start_response, session, ''') def on_create_session_response(response, results): if response != 0: print("Failed to create session: %d"%response) terminate() return global session session = results['session_handle'] print("session %s created"%session) screen_cast_call(portal.SelectSources, on_select_sources_response, session, options={ 'multiple': False, 'types': dbus.UInt32(1|2) }) portal = bus.get_object('org.freedesktop.portal.Desktop', '/org/freedesktop/portal/desktop') (session_path, session_token) = new_session_path() screen_cast_call(portal.CreateSession, on_create_session_response, options={ 'session_handle_token': session_token }) try: loop.run() except KeyboardInterrupt: terminate() ''; executable = true; }; ".local/bin/playerctl.sh" = { source = ./bin/playerctl.sh; executable = true; }; }; xdg = (import ./nix/modules/xdg.nix) {inherit pkgs config hostName;}; }