Files
nixos/home/common/default.nix
2026-03-07 13:33:51 +02:00

971 lines
26 KiB
Nix

{
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 = "<disabled>";
prevItem = "<disabled>";
};
commits = {
moveDownCommit = "<down>";
moveUpCommit = "<up>";
};
};
customCommands = [
{
key = "<c-r>";
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 = "(?<pr_number>[0-9]+)(?<rest>.*)";
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 = "<Alt>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 = [
"<Control><Super>Down"
"<Shift><Super>j"
];
move-left = [
"<Control><Super>Left"
"<Shift><Super>h"
];
move-right = [
"<Control><Super>Right"
"<Shift><Super>l"
];
move-up = [
"<Control><Super>Up"
"<Shift><Super>k"
];
switch-down = [
"<Super>Down"
"<Super>j"
];
switch-left = [
"<Super>Left"
"<Super>h"
];
switch-right = [
"<Super>Right"
"<Super>l"
];
switch-up = [
"<Super>Up"
"<Super>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;
};
}