{ config, osConfig, lib, pkgs, pkgs-unstable, inputs, self, ... }: { imports = [ ../gnome ./dnote.nix ../modules/zed ../modules/voxtype ../modules/elephant ./secrets.nix inputs.hastebin.nixosModules.hm inputs.agenix.homeManagerModules.default inputs.voxtype.homeManagerModules.default ]; # This value determines the Home Manager release that your configuration is # compatible with. This helps avoid breakage when a new Home Manager release # introduces backwards incompatible changes. # # You should not change this value, even if you update Home Manager. If you do # want to update the value, then make sure to first check the Home Manager # release notes. home.stateVersion = "23.11"; # Please read the comment before changing. home.packages = with pkgs; [ htop gnupg pkgs-unstable.yubioath-flutter pkgs-unstable.yubikey-manager gcc sqlite noto-fonts noto-fonts-cjk-sans noto-fonts-color-emoji source-sans-pro mosh docker-compose (signal-desktop.override { commandLineArgs = "--password-store=gnome-libsecret"; }) cargo blanket wl-clipboard gnumeric gh neovim-remote pkgs-unstable.gradia crystal shards moreutils keymapp gnumake tig jq yt-dlp ffmpeg otp manix (writeShellScriptBin "fd" '' ${fd}/bin/fd -H "$@" '') pkgs-unstable.rpi-imager picocom imagemagick ghostscript inkscape jless scribus dnscontrol protonmail-bridge hydroxide imapsync nixfmt-rfc-style tree virt-manager pkgs-unstable.ncspot pkgs-unstable.devbox pkgs-unstable.feishin openscad pkgs-unstable.shairport-sync-airplay2 (writeShellScriptBin "pw" '' ${lib.getExe rbw} ls --fields 'id,folder,name' | \ ${lib.getExe gawk} -F '\t' '{print $1 "\t" ($2 == "" ? "" : $2 "/") $3}' | \ ${lib.getExe config.services.walker.package} -d -l 2 | \ xargs ${lib.getExe rbw} get "$@" | ${lib.getExe' wl-clipboard "wl-copy"} '') (google-fonts.override { fonts = [ "Tajawal" ]; }) pkgs-unstable.opencode (writeShellScriptBin "nr" '' ${lib.getExe nix} run "nixpkgs#''${1}" "$@" '') pkgs-unstable.tidal-hifi inputs.tonearm.packages.${pkgs.stdenv.hostPlatform.system}.tonearm blueman pavucontrol (writeShellScriptBin "voxtoggle" '' status=$(${lib.getExe config.programs.voxtype.package} status) pid=$(cat ''${XDG_RUNTIME_DIR}/voxtype/pid) if [[ "$status" == "stopped" ]]; then exit 1 elif [[ "$status" == "recording" ]]; then kill -SIGUSR2 "$pid" else kill -SIGUSR1 "$pid" fi '') ]; ++ lib.optional osConfig.programs.niri.enable ( pkgs.writeShellScriptBin "handle-lid-close" '' alias niri=${lib.getExe osConfig.programs.niri.package} output_count=$(niri outputs | ${lib.getExe jq} -r 'length') if [ "$output_count" -eq 1 ]; then niri msg action spawn hyprlock systemctl suspend fi '' ); programs = { ashell = { enable = true; package = pkgs-unstable.ashell; systemd = { enable = true; target = "graphical-session.target"; }; settings = { modules = { left = [ "Workspaces" ]; center = [ "WindowTitle" ]; right = [ "CustomNotifications" "SystemInfo" [ "Clock" "Privacy" "Settings" ] ]; }; settings = { lock_cmd = "hyprlock &"; logout_cmd = "niri msg action quit"; audio_sinks_more_cmd = "pavucontrol -t 3"; audio_sources_more_cmd = "pavucontrol -t 4"; bluetooth_more_cmd = "blueman-manager"; CustomButton = let isDark = lib.getExe ( pkgs.writeShellScriptBin "is-dark" '' gsettings get org.gnome.desktop.interface color-scheme | grep -q dark '' ); toggleDark = lib.getExe ( pkgs.writeShellScriptBin "toggle-dark" '' if ${isDark}; then gsettings set org.gnome.desktop.interface color-scheme 'prefer-light' else gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' fi '' ); in [ { name = "Dark Mode"; icon = " "; command = toggleDark; status_command = isDark; } ]; }; CustomModule = [ { name = "CustomNotifications"; type = "Button"; icon = " "; command = "swaync-client -t -sw"; listen_cmd = "swaync-client -swb"; icons."dnd.*" = " "; alert = ".*notification"; } ]; }; }; nh = { enable = true; flake = self; }; dnote.enable = true; home-manager.enable = true; bat = { enable = true; extraPackages = with pkgs.bat-extras; [ batdiff batwatch ]; config = { pager = "less -FR"; }; }; direnv = { enable = true; nix-direnv.enable = true; enableZshIntegration = true; }; mpv = { enable = true; config = { hwdec = "auto-safe"; vo = "gpu"; profile = "gpu-hq"; gpu-context = "wayland"; }; }; kitty = { enable = true; font = { name = "IosevkaTerm NFM"; size = 14; }; shellIntegration.enableZshIntegration = true; themeFile = "Tomorrow_Night"; settings = { hide_window_decorations = true; enabled_layouts = "splits,stack"; }; keybindings = { "kitty_mod+q" = ""; "kitty_mod+w" = ""; "kitty_mod+t" = "new_tab_with_cwd"; "kitty_mod+tab" = "next_tab"; "kitty_mod+enter" = "launch --location=hsplit --cwd=current"; "kitty_mod+space" = "launch --location=vsplit --cwd=current"; "kitty_mod+h" = "neighboring_window left"; "kitty_mod+l" = "neighboring_window right"; "kitty_mod+k" = "neighboring_window up"; "kitty_mod+j" = "neighboring_window down"; "kitty_mod+1" = "goto_tab 1"; "kitty_mod+2" = "goto_tab 2"; "kitty_mod+3" = "goto_tab 3"; "kitty_mod+4" = "goto_tab 4"; "kitty_mod+5" = "goto_tab 5"; "kitty_mod+6" = "goto_tab 6"; "kitty_mod+7" = "goto_tab 7"; "kitty_mod+8" = "goto_tab 8"; "kitty_mod+9" = "goto_tab 9"; "kitty_mod+0" = "goto_tab 10"; "kitty_mod+f" = "toggle_layout stack"; "kitty_mod+/" = "show_scrollback"; "ctrl+alt+h" = "move_window left"; "ctrl+alt+l" = "move_window right"; "ctrl+alt+k" = "move_window up"; "ctrl+alt+j" = "move_window down"; "ctrl+alt+enter" = "layout_action rotate"; }; }; vivaldi.enable = true; git = { enable = true; settings = { alias = { st = "status"; co = "checkout"; b = "branch"; }; pull = { rebase = "true"; }; push = { autoSetupRemote = true; }; init = { defaultBranch = "main"; }; user = { name = "Joakim Repomaa"; signingKey = "F0AF1CE34733B22317A8937D05557F53CD3C6458"; }; commit.gpgSign = true; }; }; gpg = { enable = true; }; zsh = { enable = true; enableVteIntegration = true; cdpath = [ "${config.xdg.configHome}/home-manager" ]; defaultKeymap = "viins"; dirHashes = { hm = "${config.xdg.configHome}/home-manager"; }; history = { path = "${config.home.homeDirectory}/.cache/zsh/history"; save = 100000; size = 100000; }; initContent = '' . "${config.xdg.configHome}/zsh/init" . "${config.age.secrets.context7.path}" ''; }; lazygit = let getBaseBranch = pkgs.writeShellScriptBin "git-base-branch" '' current_branch="$(git rev-parse --abbrev-ref HEAD)" if [[ "$current_branch" =~ -review$ ]]; then echo "''${current_branch%-review}" else git config get init.defaultBranch fi ''; escapeGoTemplate = builtins.replaceStrings [ "{{" "}}" ] [ ''{{"{{"}}'' ''{{"}}"}}'' ]; in { enable = true; settings = { gui = { nerdFontsVersion = "3"; }; git = { showUntrackedFiles = "all"; branchLogCmd = "git log {{branchName}} --first-parent --color=always --pretty=format:'%Cgreen%h%Creset %Cblue%aN%Creset %C(cyan)%<(14)%ar%Creset %s' --abbrev-commit"; overrideGpg = true; }; os = { edit = "\${EDITOR} -- {{filename}}"; editAtLine = "\${EDITOR} -- {{filename}}:{{line}}"; editAtLineAndWait = "\${EDITOR} --wait -- {{filename}}:{{line}}"; openDirInEditor = "\${EDITOR} -- {{dir}}"; }; promptToReturnFromSubprocess = false; keybinding = { universal = { nextItem = ""; prevItem = ""; }; commits = { moveDownCommit = ""; moveUpCommit = ""; }; }; customCommands = [ { key = ""; context = "global"; command = '' git push --force-with-lease --set-upstream origin "{{.SelectedLocalBranch.Name}}" && gh pr create --title "{{.SelectedLocalBranch.Name}}" -B "$(${lib.getExe getBaseBranch})" --fill || (git log --reverse --no-merges --pretty='- %s' master..HEAD | gh pr edit --body-file -); gh pr view --web ''; } { key = "N"; context = "localBranches"; prompts = [ { type = "input"; title = "New Branch Name:"; key = "BranchName"; initialValue = "{{.SelectedLocalBranch.Name}}-review"; } ]; command = '' git checkout -b {{.Form.BranchName | quote}} ''; } { key = "G"; context = "localBranches"; prompts = [ { type = "menuFromCommand"; title = "PR:"; key = "pr"; command = escapeGoTemplate "gh pr list --search '-author:@me' --json number,title,author,updatedAt -t '{{range .}}{{tablerow .number .title .author.login}}{{end}}'"; filter = "(?[0-9]+)(?.*)"; labelFormat = "{{ .rest }}"; valueFormat = "{{ .pr_number }}"; } ]; command = '' gh pr checkout {{.Form.pr | quote}} ''; } ]; }; }; obs-studio.enable = true; ssh = { enable = true; enableDefaultConfig = false; matchBlocks = { "*" = { controlMaster = "auto"; controlPath = "${config.home.homeDirectory}/.ssh/masters-control/%r@%h:%p"; controlPersist = "1h"; forwardAgent = false; addKeysToAgent = "no"; compression = false; serverAliveInterval = 0; serverAliveCountMax = 3; hashKnownHosts = false; userKnownHostsFile = "${config.home.homeDirectory}/.ssh/known_hosts"; setEnv = { TERM = "screen-256color"; }; }; "apu" = { user = "root"; }; }; }; spotify-player = { enable = true; settings = { enable_streaming = "Always"; theme = "tomorrow_night"; copy_command = "wl-copy"; }; themes = [ { name = "tomorrow_night"; palette = { background = "#1d1f21"; foreground = "#c5c8c6"; black = "#000000"; red = "#cc6666"; green = "#b5bd68"; yellow = "#f0c674"; blue = "#81a2be"; magenta = "#b294bb"; cyan = "#8abeb7"; white = "#ffffff"; bright_black = "#000000"; bright_red = "#cc6666"; bright_green = "#b5bd68"; bright_yellow = "#f0c674"; bright_blue = "#81a2be"; bright_magenta = "#b294bb"; bright_cyan = "#8abeb7"; bright_white = "#ffffff"; }; component_style = { playback_metadata = { fg = "#8abeb7"; }; playback_progress_bar = { bg = "#1d1f21"; fg = "#c5c8c6"; }; }; } ]; }; neovide = { enable = true; settings = { font = { normal = [ "IosevkaTerm NFM" ]; size = 12.0; }; fork = true; }; }; hastebin = { enable = true; settings = { host = "https://bin.freun.dev"; auth_token_file = "${config.xdg.configHome}/hastebin/auth_token"; clipboard_command = "${pkgs.wl-copy-both}/bin/wl-copy"; default_renderers = let code = [ "js" "ts" "jsx" "tsx" "rs" "nix" "cr" "ecr" "rb" "erb" "yaml" "yml" "json" "toml" "html" "xml" "css" "scss" "sass" "less" "sql" "sh" ]; in lib.listToAttrs ( lib.map (name: { inherit name; value = "hl"; }) code ); }; }; zed-editor = { enable = true; defaultEditor = true; }; hyprlock = { enable = true; package = pkgs-unstable.hyprlock; settings = { general = { hide_cursor = true; ignore_empty_input = true; }; background = { monitor = ""; path = "screenshot"; blur_passes = 3; }; input-field = { size = "20%, 5%"; monitor = ""; dots_center = true; fade_on_empty = false; rounding = 15; shadow_passes = 2; outline_thickness = 2; placeholder_text = "Password..."; fail_text = "$PAMFAIL"; dots_spacing = "0.3"; position = "0, -20"; halign = "center"; valign = "center"; }; }; }; voxtype = { enable = true; package = inputs.voxtype.packages.${pkgs.stdenv.hostPlatform.system}.vulkan; model.name = "large-v3-turbo"; service.enable = true; settings = { hotkey.enabled = false; whisper.language = "auto"; output.notification = { on_recording_start = false; on_recording_stop = false; on_transcription = false; }; }; postProcessing = { enable = true; settings = { model = "qwen3:4b-instruct"; commonInstructions = "no quotes, no emojis, no explanations"; prompts = [ { title = "Clean up"; instructions = "Clean up this dictation. Remove filler words, fix grammar and punctuation. Output ONLY the cleaned text"; } { title = "Make a title"; instructions = "Make a concise and descriptive title for this dictation. Output ONLY the title"; } { title = "Summarize"; instructions = "Summarize this dictation in a few sentences. Output ONLY the summary"; } { title = "Commit message"; instructions = "Write a concise and descriptive git commit message for this dictation. Output ONLY the commit message"; } { title = "Translate to English"; instructions = "Translate this dictation to English. Remove filler words, fix grammar and punctuation. Output ONLY the translation"; } ]; }; }; }; }; gnome = { keyboard.sources = [ "us+altgr-intl" ]; keybindings = { builtin = { screenshot-window = [ ]; show-screenshot-ui = [ ]; }; custom = [ { binding = "Print"; command = "gradia --screenshot"; name = "Gradia"; } { binding = "Print"; command = "kooha"; name = "Kooha"; } ]; }; power = { powerButton = "hibernate"; }; extensions = { paperwm = { enable = true; winprops = [ { wm_class = "vivaldi-stable"; preferredWidth = "67%"; } { wm_class = "yubioath-flutter"; scratch_layer = true; } { wm_class = "signal"; scratch_layer = true; } { wm_class = "Slack"; scratch_layer = true; } { wm_class = "Supersonic"; scratch_layer = true; } { wm_class = "io.github.seadve.Kooha"; scratch_layer = true; } { wm_class = "/.*/"; preferredWidth = "33"; } ]; keybindings = { move-down = [ "Down" "j" ]; move-left = [ "Left" "h" ]; move-right = [ "Right" "l" ]; move-up = [ "Up" "k" ]; switch-down = [ "Down" "j" ]; switch-left = [ "Left" "h" ]; switch-right = [ "Right" "l" ]; switch-up = [ "Up" "k" ]; }; }; freon = { enable = true; }; }; nightLight.enable = true; }; fonts.fontconfig = { enable = true; defaultFonts = { emoji = [ "Noto Color Emoji" ]; monospace = [ "IosevkaTerm NFM" ]; sansSerif = [ "Source Sans Pro" ]; }; }; targets.genericLinux.enable = true; xdg.mime.enable = true; xdg = { enable = true; configFile = { tmux.source = dotfiles/tmux; zsh.source = dotfiles/zsh; "fd/ignore".text = '' /.git/ ''; "vivaldi/NativeMessagingHosts/ff2mpv.json".text = builtins.toJSON { name = "ff2mpv"; description = "ff2mpv's external manifest"; path = "${pkgs.ff2mpv-rust}/bin/ff2mpv-rust"; type = "stdio"; allowed_origins = [ "chrome-extension://ephjcajbkgplkjmelpglennepbpmdpjg/" ]; }; }; }; # Home Manager can also manage your environment variables through # 'home.sessionVariables'. If you don't want to manage your shell through Home # Manager then you have to manually source 'hm-session-vars.sh' located at # either # # ~/.nix-profile/etc/profile.d/hm-session-vars.sh # # or # # ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh # # or # # /etc/profiles/per-user/jokke/etc/profile.d/hm-session-vars.sh # home.sessionVariables = { NIXOS_OZONE_WL = 1; NVIM_LISTEN_ADDRESS = "$XDG_RUNTIME_DIR/nvimsocket"; PAGER = "bat --paging=always --style=plain"; MANPAGER = "sh -c 'col -bx | bat -l man -p'"; MANROFFOPT = "-c"; DO_NOT_TRACK = 1; }; systemd.user.sessionVariables = lib.mapAttrs (_: v: toString v) config.home.sessionVariables; programs.zsh.sessionVariables = config.home.sessionVariables; home.shellAliases = { _ = "sudo"; icr = "crystal i"; hm = "home-manager"; chat = "ssh root@ipv4.jokke.space pkill mosh; mosh weechat@ipv4.jokke.space"; wget = " wget --content-disposition"; pj = "jq | bat -l json"; ls = "ls --color=auto"; }; services = { swaync = { enable = true; package = pkgs-unstable.swaynotificationcenter; settings = { scripts = { focus-window = let jq = lib.getExe pkgs.jq; niri = lib.getExe osConfig.programs.niri.package; script = pkgs.writeShellScriptBin "swaync-focus-window" '' set -e APP_NAME="''${SWAYNC_APP_NAME:-}" DESKTOP_ENTRY="''${SWAYNC_DESKTOP_ENTRY:-}" APP_ID="" if [[ -n "$DESKTOP_ENTRY" ]]; then APP_ID="$DESKTOP_ENTRY" elif [[ -n "$APP_NAME" ]]; then APP_ID=$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/ //g') fi [[ -z "$APP_ID" ]] && exit # Find window ID for this app in niri and focus it ${jq} -r --arg app_id "$APP_ID" '.[] | select(.app_id | ascii_downcase | contains($app_id)) | .id' \ <(${niri} msg --json windows 2>/dev/null) | head -n1 | while read -r WINDOW_ID; do if [[ -n "$WINDOW_ID" && "$WINDOW_ID" != "null" ]]; then ${niri} msg action focus-window --id "$WINDOW_ID" fi done ''; in { exec = lib.getExe script; run-on = "action"; }; }; }; }; gpg-agent = with pkgs; { enable = true; enableSshSupport = true; pinentry.package = pinentry-gnome3; }; }; services.walker = { enable = true; package = pkgs.symlinkJoin { inherit (pkgs-unstable.walker) name meta; paths = [ pkgs-unstable.walker ]; nativeBuildInputs = [ pkgs.makeBinaryWrapper ]; postBuild = '' wrapProgram $out/bin/walker \ --prefix PATH : ${lib.makeBinPath [ config.services.elephant.package ]} ''; }; systemd = { enable = true; }; settings = { providers = { default = [ "desktopapplications" "calc" "websearch" "bitwarden" ]; }; }; }; services.elephant.enable = true; systemd.user.services.walker.Install.WantedBy = lib.mkForce [ "graphical-session.target" ]; systemd.user.services.shairport-sync = { Unit = { Description = "AirPlay audio server"; After = [ "pipewire-pulse.service" "network.target" ]; }; Service = { ExecStart = "${lib.getExe pkgs-unstable.shairport-sync-airplay2} -- pw"; Restart = "on-failure"; }; Install = { WantedBy = [ "graphical-session.target" ]; }; }; programs.gpg.scdaemonSettings = { disable-ccid = true; }; programs.rbw.enable = true; programs.rclone.enable = true; programs.firefox.enable = true; xdg.configFile."opencode/opencode.jsonc".text = builtins.toJSON { "$schema" = "https://opencode.ai/config.json"; model = "opencode-go/glm-5"; small_model = "opencode-go/kimi-k2.5"; agent = { explore.model = "opencode-go/kimi-k2.5"; }; theme = "system"; lsp = { ruby-lsp = { initialization = { enabledFeatures = { codeActions = true; codeLens = true; completion = true; definition = true; diagnostics = true; documentHighlights = true; documentLink = true; documentSymbols = true; foldingRanges = true; formatting = false; hover = true; inlayHint = true; onTypeFormatting = true; selectionRanges = true; semanticHighlighting = true; signatureHelp = true; typeHierarchy = true; workspaceSymbol = true; }; linters = [ "standard" ]; }; extensions = [ ".rb" ".erb" ".haml" ]; command = [ "bundle" "exec" "ruby-lsp" ]; }; rubocop = { command = [ "bundle" "exec" "rubocop" "--lsp" ]; extensions = [ ".rb" ".erb" ".haml" ]; }; nil = { command = [ (lib.getExe pkgs.nil) ]; extensions = [ ".nix" ]; }; ameba-ls = { command = [ (lib.getExe pkgs-unstable.ameba-ls) ]; extensions = [ ".cr" ]; }; }; mcp = { context7 = { type = "remote"; url = "https://mcp.context7.com/mcp"; headers = { CONTEXT7_API_KEY = "{env:CONTEXT7_API_KEY}"; }; enabled = true; }; }; }; xdg.configFile."niri/config.kdl".source = ./dotfiles/niri.kdl; gnome.automaticTimeZone = true; gtk.enable = true; home.pointerCursor = { enable = true; name = "Adwaita"; package = pkgs.adwaita-icon-theme; gtk.enable = true; }; }