feat: home-manager entry point and per-user shims
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
8d2a0357c1
commit
7c9ce3fe10
3 changed files with 928 additions and 0 deletions
896
home.nix
Normal file
896
home.nix
Normal file
|
|
@ -0,0 +1,896 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
inputs,
|
||||
settings,
|
||||
osConfig,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
features = osConfig.omni.features;
|
||||
featureEnabled = name: features.${name} or false;
|
||||
|
||||
# Launches rpi-imager as root while preserving the Wayland session.
|
||||
# `sudo -A` shows a graphical password prompt via SUDO_ASKPASS; `env`
|
||||
# is executed as root and re-injects the env vars sudo would otherwise
|
||||
# strip — no sudoers env_keep changes needed.
|
||||
rpiImagerLauncher = pkgs.writeShellScriptBin "rpi-imager-launcher" ''
|
||||
export SUDO_ASKPASS=${pkgs.lxqt.lxqt-openssh-askpass}/bin/lxqt-openssh-askpass
|
||||
exec sudo -A ${pkgs.coreutils}/bin/env \
|
||||
WAYLAND_DISPLAY="$WAYLAND_DISPLAY" \
|
||||
XDG_RUNTIME_DIR="/run/user/$(id -u)" \
|
||||
XDG_SESSION_TYPE=wayland \
|
||||
${pkgs.rpi-imager}/bin/rpi-imager "$@"
|
||||
'';
|
||||
in
|
||||
{
|
||||
home.username = settings.user;
|
||||
home.homeDirectory = "/home/${settings.user}";
|
||||
home.stateVersion = settings.stateVersion;
|
||||
|
||||
programs.home-manager.enable = true;
|
||||
|
||||
# ~/dev/CLAUDE.md — workspace-level instructions for Claude across
|
||||
# the aiolabs/AIO stack. ~/dev/ isn't itself a git repo (it's a
|
||||
# workspace of sibling repos), so we track this file from /etc/nixos
|
||||
# instead. mkOutOfStoreSymlink → mutable: edits in
|
||||
# /etc/nixos/files/dev-CLAUDE.md take effect immediately without a
|
||||
# rebuild; commit changes from /etc/nixos.
|
||||
home.file."dev/CLAUDE.md".source =
|
||||
config.lib.file.mkOutOfStoreSymlink /etc/nixos/files/dev-CLAUDE.md;
|
||||
|
||||
# ~/Anki — spaced-repetition source tree. README is managed (symlink to
|
||||
# /etc/nixos/files/anki-README.md, edit + commit there). Deck dirs and CSV
|
||||
# stubs are seeded once via activation and then owned by the user — Anki's
|
||||
# own collection lives at ~/.local/share/Anki2/ and is unrelated.
|
||||
home.file."Anki/README.md".source =
|
||||
config.lib.file.mkOutOfStoreSymlink /etc/nixos/files/anki-README.md;
|
||||
|
||||
home.activation.ankiBootstrap = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||
ANKI_DIR="$HOME/Anki"
|
||||
mkdir -p "$ANKI_DIR/dev" "$ANKI_DIR/french" "$ANKI_DIR/protocols" "$ANKI_DIR/exports" "$ANKI_DIR/sources"
|
||||
|
||||
seed_csv() {
|
||||
local path="$1" deck="$2"
|
||||
if [ ! -e "$path" ]; then
|
||||
cat > "$path" <<EOF
|
||||
#separator:Comma
|
||||
#html:false
|
||||
#deck:$deck
|
||||
#notetype:Basic
|
||||
#tags column:3
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
seed_csv "$ANKI_DIR/dev/cards.csv" "Dev"
|
||||
seed_csv "$ANKI_DIR/french/cards.csv" "French"
|
||||
seed_csv "$ANKI_DIR/protocols/cards.csv" "Protocols"
|
||||
|
||||
for d in exports sources; do
|
||||
[ -e "$ANKI_DIR/$d/.gitkeep" ] || touch "$ANKI_DIR/$d/.gitkeep"
|
||||
done
|
||||
'';
|
||||
|
||||
# --- Ghostty terminal theming (derived from the active theme) ---
|
||||
#
|
||||
# The omni theme modules theme Alacritty (programs.alacritty.settings.
|
||||
# colors) but historically left Ghostty — the default terminal — on its
|
||||
# built-in palette, so `omni-theme <name>` appeared to do nothing in a
|
||||
# Ghostty window. Derive Ghostty's config from the same Alacritty colors
|
||||
# the active theme sets, so the default terminal tracks the theme with
|
||||
# exact parity. Gated on the Alacritty palette being present (themes set
|
||||
# it only when the coding/media feature is on); otherwise Ghostty keeps
|
||||
# its own defaults, matching Alacritty's behaviour.
|
||||
xdg.configFile."ghostty/config" =
|
||||
let
|
||||
ac = config.programs.alacritty.settings.colors or null;
|
||||
hex = s: "#" + lib.removePrefix "#" s;
|
||||
ansi = lib.optionals (ac != null) [
|
||||
ac.normal.black
|
||||
ac.normal.red
|
||||
ac.normal.green
|
||||
ac.normal.yellow
|
||||
ac.normal.blue
|
||||
ac.normal.magenta
|
||||
ac.normal.cyan
|
||||
ac.normal.white
|
||||
ac.bright.black
|
||||
ac.bright.red
|
||||
ac.bright.green
|
||||
ac.bright.yellow
|
||||
ac.bright.blue
|
||||
ac.bright.magenta
|
||||
ac.bright.cyan
|
||||
ac.bright.white
|
||||
];
|
||||
palette = lib.concatStringsSep "\n" (
|
||||
lib.imap0 (i: c: "palette = ${toString i}=${hex c}") ansi
|
||||
);
|
||||
cursor = lib.optionalString (ac != null && ac ? cursor) ''
|
||||
cursor-color = ${hex ac.cursor.cursor}
|
||||
cursor-text = ${hex ac.cursor.text}
|
||||
'';
|
||||
in
|
||||
lib.mkIf (ac != null && ac ? normal && ac ? bright) {
|
||||
# Ghostty writes a default-template config on first launch, so this
|
||||
# path usually pre-exists as a plain file home-manager would refuse to
|
||||
# clobber. We own it declaratively now (like alacritty.toml), so force.
|
||||
force = true;
|
||||
text = ''
|
||||
# Generated by /etc/nixos/home.nix from the active omni theme's
|
||||
# Alacritty palette. Do not edit — switch with `omni-theme <name>`.
|
||||
background = ${hex ac.primary.background}
|
||||
foreground = ${hex ac.primary.foreground}
|
||||
${cursor}${palette}
|
||||
'';
|
||||
};
|
||||
|
||||
# --- Packages gated by omni.features ---
|
||||
|
||||
home.packages =
|
||||
with pkgs;
|
||||
# Core (always installed)
|
||||
[
|
||||
ghostty
|
||||
alacritty
|
||||
starship
|
||||
ranger
|
||||
yazi
|
||||
btop
|
||||
htop
|
||||
|
||||
# CLI essentials
|
||||
ripgrep
|
||||
fd
|
||||
bat
|
||||
eza
|
||||
fzf
|
||||
zoxide
|
||||
jq
|
||||
yq
|
||||
curl
|
||||
wget
|
||||
qrencode
|
||||
openssl
|
||||
imagemagick
|
||||
|
||||
# ZSA Moonlander flashing GUI (needs hardware.keyboard.zsa.enable)
|
||||
keymapp
|
||||
|
||||
# Wayland tools
|
||||
wl-clipboard
|
||||
wlr-randr
|
||||
|
||||
# SD-card / RPi flasher
|
||||
rpi-imager
|
||||
rpiImagerLauncher
|
||||
|
||||
# Screenshot
|
||||
grim
|
||||
slurp
|
||||
swappy
|
||||
|
||||
# Fonts
|
||||
nerd-fonts.jetbrains-mono
|
||||
nerd-fonts.fira-code
|
||||
nerd-fonts.hack
|
||||
noto-fonts
|
||||
noto-fonts-color-emoji
|
||||
liberation_ttf
|
||||
]
|
||||
|
||||
# Development tools
|
||||
++ lib.optionals (featureEnabled "coding") [
|
||||
vscode
|
||||
lazygit
|
||||
gh
|
||||
git-lfs
|
||||
delta
|
||||
meld
|
||||
httpie
|
||||
wf-recorder
|
||||
]
|
||||
|
||||
# Media
|
||||
++ lib.optionals (featureEnabled "media") [
|
||||
mpv
|
||||
imv
|
||||
ffmpeg
|
||||
nvtopPackages.full
|
||||
]
|
||||
|
||||
# Browsers (always include firefox; extras gated on office/communication)
|
||||
++ [ firefox ]
|
||||
++ lib.optionals (featureEnabled "office" || featureEnabled "communication") [
|
||||
chromium
|
||||
brave
|
||||
]
|
||||
|
||||
# Communication
|
||||
++ lib.optionals (featureEnabled "communication") [
|
||||
discord
|
||||
slack
|
||||
telegram-desktop
|
||||
]
|
||||
|
||||
# Productivity
|
||||
++ lib.optionals (featureEnabled "office") [
|
||||
obsidian
|
||||
zathura
|
||||
libreoffice
|
||||
];
|
||||
|
||||
# --- Programs (always enabled) ---
|
||||
|
||||
programs.git = {
|
||||
enable = true;
|
||||
signing.format = "openpgp";
|
||||
settings = {
|
||||
user.name = settings.gitName;
|
||||
user.email = settings.gitEmail;
|
||||
alias = {
|
||||
st = "status";
|
||||
co = "checkout";
|
||||
br = "branch";
|
||||
cm = "commit -m";
|
||||
lg = "log --graph --oneline --decorate";
|
||||
unstage = "reset HEAD --";
|
||||
last = "log -1 HEAD";
|
||||
};
|
||||
init.defaultBranch = "main";
|
||||
pull.rebase = false;
|
||||
push.autoSetupRemote = true;
|
||||
merge.conflictStyle = "diff3";
|
||||
diff.colorMoved = "default";
|
||||
};
|
||||
};
|
||||
|
||||
programs.delta = {
|
||||
enable = true;
|
||||
enableGitIntegration = true;
|
||||
options = {
|
||||
features = "decorations";
|
||||
side-by-side = true;
|
||||
navigate = true;
|
||||
};
|
||||
};
|
||||
|
||||
programs.jujutsu = {
|
||||
enable = true;
|
||||
settings = {
|
||||
user = {
|
||||
name = settings.gitName;
|
||||
email = settings.gitEmail;
|
||||
};
|
||||
ui.default-command = "log";
|
||||
ui.diff-formatter = ":git";
|
||||
};
|
||||
};
|
||||
|
||||
programs.bash = {
|
||||
enable = true;
|
||||
|
||||
shellAliases = {
|
||||
ll = "eza -la";
|
||||
ls = "eza";
|
||||
l = "eza -lah";
|
||||
tree = "eza --tree";
|
||||
".." = "cd ..";
|
||||
"..." = "cd ../..";
|
||||
g = "git";
|
||||
lg = "lazygit";
|
||||
cat = "bat";
|
||||
grep = "rg";
|
||||
find = "fd";
|
||||
rebuild = "sudo nixos-rebuild switch --flake /etc/nixos#omni";
|
||||
update = "nix flake update";
|
||||
clean = "nix-collect-garbage -d";
|
||||
c = "opencode";
|
||||
cx = "claude --dangerously-skip-permissions";
|
||||
qr = "qrencode -t ansiutf8";
|
||||
# Duplex A4 to the Brother HL-L3240CDW — long-edge (portrait) default,
|
||||
# short-edge for landscape. Append the file: `print doc.pdf`.
|
||||
print = "lp -d Brother_HL_L3240CDW_series -o media=A4 -o sides=two-sided-long-edge";
|
||||
print-landscape = "lp -d Brother_HL_L3240CDW_series -o media=A4 -o sides=two-sided-short-edge";
|
||||
};
|
||||
|
||||
initExtra = ''
|
||||
eval "$(starship init bash)"
|
||||
eval "$(zoxide init bash)"
|
||||
source ${pkgs.fzf}/share/fzf/key-bindings.bash
|
||||
source ${pkgs.fzf}/share/fzf/completion.bash
|
||||
if [ -n "$IN_NIX_SHELL" ]; then
|
||||
export PS1="[nix-shell] $PS1"
|
||||
fi
|
||||
|
||||
# Route the terminal command through the same wrapper walker uses.
|
||||
alias rpi-imager=rpi-imager-launcher
|
||||
|
||||
# Passive backstop for the claude-memory weekly snapshot timer —
|
||||
# if drift has gone uncommitted for >7 days, surface it on shell
|
||||
# startup. The systemd.user.timers.claude-memory-snapshot unit
|
||||
# is the primary mechanism; this is a visibility-only fallback.
|
||||
_claude_memory_drift_check() {
|
||||
[ -d "$HOME/.claude/.git" ] || return
|
||||
local last drift
|
||||
last=$(git -C "$HOME/.claude" log -1 --format=%ct 2>/dev/null) || return
|
||||
drift=$(( ($(date +%s) - last) / 86400 ))
|
||||
if [ "$drift" -gt 7 ] && [ -n "$(git -C "$HOME/.claude" status --porcelain 2>/dev/null)" ]; then
|
||||
echo "[claude-memory] $drift days since last snapshot, uncommitted drift in ~/.claude/"
|
||||
fi
|
||||
}
|
||||
_claude_memory_drift_check
|
||||
'';
|
||||
};
|
||||
|
||||
programs.starship = {
|
||||
enable = true;
|
||||
settings = {
|
||||
format = lib.mkDefault ''
|
||||
[╭─](bold green)$username[@](bold yellow)$hostname [in ](bold white)$directory$git_branch$git_status$cmd_duration
|
||||
[╰─](bold green)$character
|
||||
'';
|
||||
character = {
|
||||
success_symbol = lib.mkDefault "[➜](bold green)";
|
||||
error_symbol = lib.mkDefault "[➜](bold red)";
|
||||
};
|
||||
directory = {
|
||||
truncation_length = 3;
|
||||
truncate_to_repo = true;
|
||||
style = lib.mkDefault "bold cyan";
|
||||
};
|
||||
git_branch = {
|
||||
style = lib.mkDefault "bold purple";
|
||||
symbol = " ";
|
||||
};
|
||||
git_status = {
|
||||
style = lib.mkDefault "bold red";
|
||||
ahead = "⇡\${count}";
|
||||
diverged = "⇕⇡\${ahead_count}⇣\${behind_count}";
|
||||
behind = "⇣\${count}";
|
||||
};
|
||||
cmd_duration = {
|
||||
min_time = 500;
|
||||
format = " took [$duration](bold yellow)";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
programs.alacritty = {
|
||||
enable = true;
|
||||
settings = {
|
||||
window = {
|
||||
padding = {
|
||||
x = 10;
|
||||
y = 10;
|
||||
};
|
||||
opacity = 0.95;
|
||||
decorations = "none";
|
||||
};
|
||||
font = {
|
||||
normal = {
|
||||
family = "JetBrainsMono Nerd Font";
|
||||
style = "Regular";
|
||||
};
|
||||
size = 12.0;
|
||||
};
|
||||
cursor = {
|
||||
style = "Block";
|
||||
unfocused_hollow = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Neovim is provided by the lazyvim-nix home-manager module
|
||||
# (wired through `home-manager.sharedModules` in flake.nix). The
|
||||
# previous setup half-installed lazy.nvim and then required a
|
||||
# `lua/config/lazy.lua` file that was never created on a fresh
|
||||
# install — see commit history. lazyvim-nix tracks LazyVim
|
||||
# releases upstream and pins every plugin via the nix store, so
|
||||
# there is no runtime download. extras opt in to LazyVim's
|
||||
# language packs (LSP servers, formatters, treesitter parsers).
|
||||
programs.lazyvim = {
|
||||
enable = true;
|
||||
# Treat nvim/vim/vi as aliases the way the old programs.neovim
|
||||
# block did.
|
||||
extraPackages = with pkgs; [
|
||||
ripgrep # used by telescope live-grep
|
||||
fd # used by telescope file-find
|
||||
lazygit # for the LazyVim lazygit integration
|
||||
nixd # module-aware Nix LSP (alongside nil from extras.lang.nix)
|
||||
];
|
||||
extras.lang.nix.enable = true;
|
||||
extras.lang.julia.enable = true;
|
||||
|
||||
# Disable LazyVim's format-on-save globally. `vim.g.autoformat`
|
||||
# is LazyVim's documented switch; set it false here rather than
|
||||
# toggling per-buffer with <leader>uf each session. Re-enable at
|
||||
# runtime with `:lua vim.g.autoformat = true` if needed.
|
||||
config.options = ''
|
||||
vim.g.autoformat = false
|
||||
'';
|
||||
|
||||
# Plain-text accounting: treesitter grammars for syntax highlighting
|
||||
treesitterParsers = with pkgs.vimPlugins.nvim-treesitter-parsers; [
|
||||
beancount
|
||||
ledger
|
||||
];
|
||||
|
||||
# ftplugin support (completion, alignment, checker integration)
|
||||
plugins.beancount-ledger = ''
|
||||
return {
|
||||
{ "nathangrigg/vim-beancount", ft = "beancount" },
|
||||
{ "ledger/vim-ledger", ft = "ledger" },
|
||||
}
|
||||
'';
|
||||
|
||||
# nixd alongside nil_ls (which extras.lang.nix already wires). Both
|
||||
# attach to .nix buffers — nil is faster for general lookups, nixd
|
||||
# is module-aware and resolves omni.* options + jump-to-definition
|
||||
# into the flake's modules. The options.* exprs anchor nixd's eval
|
||||
# engine at /etc/nixos so it can introspect the live module tree.
|
||||
plugins.nixd = ''
|
||||
return {
|
||||
"neovim/nvim-lspconfig",
|
||||
opts = {
|
||||
servers = {
|
||||
nixd = {
|
||||
settings = {
|
||||
nixd = {
|
||||
nixpkgs = {
|
||||
expr = "import (builtins.getFlake \"/etc/nixos\").inputs.nixpkgs { }",
|
||||
},
|
||||
options = {
|
||||
nixos = {
|
||||
expr = "(builtins.getFlake \"/etc/nixos\").nixosConfigurations.omni.options",
|
||||
},
|
||||
["home-manager"] = {
|
||||
expr = "(builtins.getFlake \"/etc/nixos\").nixosConfigurations.omni.options.home-manager.users.value.padreug",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
'';
|
||||
|
||||
# Seamless C-h/j/k/l navigation between nvim splits and tmux panes.
|
||||
# Pairs with tmuxPlugins.vim-tmux-navigator on the tmux side (already
|
||||
# wired below at programs.tmux.plugins) — both use the same `is_vim`
|
||||
# heuristic, so tmux forwards C-hjkl to nvim when nvim is the focused
|
||||
# pane and nvim hands control back to tmux at split edges.
|
||||
plugins.nvim-tmux-navigation = ''
|
||||
return {
|
||||
{
|
||||
"alexghergh/nvim-tmux-navigation",
|
||||
event = "VeryLazy",
|
||||
config = function()
|
||||
require("nvim-tmux-navigation").setup({
|
||||
disable_when_zoomed = true,
|
||||
keybindings = {
|
||||
left = "<C-h>",
|
||||
down = "<C-j>",
|
||||
up = "<C-k>",
|
||||
right = "<C-l>",
|
||||
last_active = "<C-\\>",
|
||||
next = "<C-Space>",
|
||||
},
|
||||
})
|
||||
end,
|
||||
},
|
||||
}
|
||||
'';
|
||||
};
|
||||
# Make `defaultEditor = true` work without re-enabling
|
||||
# programs.neovim (which would conflict with lazyvim-nix).
|
||||
home.sessionVariables = {
|
||||
EDITOR = "nvim";
|
||||
# pi-coding-agent: the binary is store-pinned, so its self-update
|
||||
# check is noise — omni-rebuild is the only thing that bumps it.
|
||||
PI_SKIP_VERSION_CHECK = "1";
|
||||
# User-installed pi packages can't live in the read-only nix store;
|
||||
# the README flags this var as "useful for Nix/Guix where store
|
||||
# paths tokenize poorly".
|
||||
PI_PACKAGE_DIR = "${config.home.homeDirectory}/.pi/packages";
|
||||
PI_TELEMETRY = "0";
|
||||
};
|
||||
home.shellAliases = {
|
||||
vi = "nvim";
|
||||
vim = "nvim";
|
||||
};
|
||||
|
||||
programs.firefox = {
|
||||
enable = true;
|
||||
# Adopt the new XDG profile path (the 26.05 default) ahead of the
|
||||
# stateVersion bump. The existing ~/.mozilla/firefox must be moved to
|
||||
# $XDG_CONFIG_HOME/mozilla/firefox so the profile carries over.
|
||||
configPath = "${config.xdg.configHome}/mozilla/firefox";
|
||||
profiles.default.settings = {
|
||||
"privacy.donottrackheader.enabled" = true;
|
||||
"privacy.trackingprotection.enabled" = true;
|
||||
"privacy.trackingprotection.socialtracking.enabled" = true;
|
||||
};
|
||||
};
|
||||
|
||||
programs.chromium = {
|
||||
enable = true;
|
||||
extensions = [
|
||||
{ id = "aeblfdkhhhdcdjpifhhbdiojplfjncoa"; } # 1Password
|
||||
{ id = "nopnfneeeclpahhclgfbhkianfmickdj"; } # Claude
|
||||
{ id = "ccpjjenaollgbhnejgfmapdmjmadpgio"; } # Copy URL
|
||||
{ id = "kpgefcfmnafjgpblomihpgcdlhiodkdc"; } # nos2x (Nostr)
|
||||
{ id = "dneaehbmnbhcippjikoajpoabadpodje"; } # Old Reddit Redirect
|
||||
{ id = "kbmfpngjjgdllneeigpgjifpgocmfgmb"; } # Reddit Enhancement Suite
|
||||
{ id = "ddkjiahejlhfcafbddmgiahcphecmpfh"; } # uBlock Origin Lite
|
||||
{ id = "dbepggeogbaibhgnhhndojpepiihcmeb"; } # Vimium
|
||||
];
|
||||
};
|
||||
|
||||
# Override rpi-imager's .desktop so launchers (walker, etc.) go through
|
||||
# the wrapper that preserves the Wayland session. Matches the upstream
|
||||
# filename so this entry replaces the one from the rpi-imager package.
|
||||
xdg.desktopEntries."com.raspberrypi.rpi-imager" = {
|
||||
name = "Raspberry Pi Imager";
|
||||
comment = "Tool for writing images to SD cards for Raspberry Pi";
|
||||
exec = "rpi-imager-launcher %u";
|
||||
icon = "rpi-imager";
|
||||
categories = [ "Utility" ];
|
||||
mimeType = [
|
||||
"x-scheme-handler/rpi-imager"
|
||||
"application/vnd.raspberrypi.imager-manifest+json"
|
||||
];
|
||||
type = "Application";
|
||||
startupNotify = false;
|
||||
};
|
||||
|
||||
# Electron apps default to the basic_text password store when no flag
|
||||
# is passed, which corrupts the existing gnome-libsecret-encrypted DB.
|
||||
# The hyprland autostart already passes --password-store=gnome-libsecret;
|
||||
# these overrides make launches from walker/file managers match.
|
||||
xdg.desktopEntries.signal = {
|
||||
name = "Signal";
|
||||
comment = "Private messaging from your desktop";
|
||||
exec = "signal-desktop --password-store=gnome-libsecret %U";
|
||||
icon = "signal-desktop";
|
||||
categories = [
|
||||
"Network"
|
||||
"InstantMessaging"
|
||||
"Chat"
|
||||
];
|
||||
mimeType = [
|
||||
"x-scheme-handler/sgnl"
|
||||
"x-scheme-handler/signalcaptcha"
|
||||
];
|
||||
terminal = false;
|
||||
type = "Application";
|
||||
settings.StartupWMClass = "signal";
|
||||
};
|
||||
|
||||
xdg.desktopEntries.element-desktop = {
|
||||
name = "Element";
|
||||
genericName = "Matrix Client";
|
||||
comment = "Feature-rich client for Matrix.org";
|
||||
exec = "element-desktop --password-store=gnome-libsecret %u";
|
||||
icon = "element";
|
||||
categories = [
|
||||
"Network"
|
||||
"InstantMessaging"
|
||||
"Chat"
|
||||
];
|
||||
mimeType = [
|
||||
"x-scheme-handler/element"
|
||||
"x-scheme-handler/io.element.desktop"
|
||||
];
|
||||
type = "Application";
|
||||
settings.StartupWMClass = "Element";
|
||||
};
|
||||
|
||||
xdg.desktopEntries.ferdium = {
|
||||
name = "Ferdium";
|
||||
comment = "Desktop app bringing all your messaging services into one installable";
|
||||
exec = "ferdium --password-store=gnome-libsecret %U";
|
||||
icon = "ferdium";
|
||||
categories = [
|
||||
"Network"
|
||||
"InstantMessaging"
|
||||
];
|
||||
mimeType = [ "x-scheme-handler/ferdium" ];
|
||||
terminal = false;
|
||||
type = "Application";
|
||||
settings.StartupWMClass = "Ferdium";
|
||||
};
|
||||
|
||||
# Set chromium as default browser for xdg
|
||||
xdg.mimeApps = {
|
||||
enable = true;
|
||||
defaultApplications = {
|
||||
"text/html" = "chromium-browser.desktop";
|
||||
"x-scheme-handler/http" = "chromium-browser.desktop";
|
||||
"x-scheme-handler/https" = "chromium-browser.desktop";
|
||||
"x-scheme-handler/about" = "chromium-browser.desktop";
|
||||
"x-scheme-handler/unknown" = "chromium-browser.desktop";
|
||||
};
|
||||
};
|
||||
|
||||
# VS Code — uncomment if needed
|
||||
# programs.vscode = lib.mkIf (featureEnabled "coding") {
|
||||
# enable = true;
|
||||
# profiles.default.extensions = with pkgs.vscode-extensions; [
|
||||
# pkief.material-icon-theme
|
||||
# zhuangtongfa.material-theme
|
||||
# rust-lang.rust-analyzer
|
||||
# golang.go
|
||||
# ms-python.python
|
||||
# ms-vscode.cpptools
|
||||
# dbaeumer.vscode-eslint
|
||||
# esbenp.prettier-vscode
|
||||
# bradlc.vscode-tailwindcss
|
||||
# eamodio.gitlens
|
||||
# vscodevim.vim
|
||||
# yzhang.markdown-all-in-one
|
||||
# jnoortheen.nix-ide
|
||||
# ];
|
||||
# profiles.default.userSettings = {
|
||||
# "workbench.colorTheme" = lib.mkDefault "One Dark Pro";
|
||||
# "workbench.iconTheme" = lib.mkDefault "material-icon-theme";
|
||||
# "editor.fontFamily" = lib.mkDefault "'JetBrainsMono Nerd Font', monospace";
|
||||
# "editor.fontSize" = lib.mkDefault 14;
|
||||
# "editor.fontLigatures" = lib.mkDefault true;
|
||||
# "editor.formatOnSave" = lib.mkDefault true;
|
||||
# "editor.minimap.enabled" = lib.mkDefault false;
|
||||
# "editor.rulers" = lib.mkDefault [ 80 120 ];
|
||||
# "terminal.integrated.fontFamily" = lib.mkDefault "'JetBrainsMono Nerd Font'";
|
||||
# "vim.enableNeovim" = lib.mkDefault true;
|
||||
# };
|
||||
# };
|
||||
|
||||
programs.direnv = {
|
||||
enable = true;
|
||||
nix-direnv.enable = true;
|
||||
enableBashIntegration = true;
|
||||
};
|
||||
|
||||
programs.zoxide = {
|
||||
enable = true;
|
||||
enableBashIntegration = true;
|
||||
};
|
||||
|
||||
programs.bat = {
|
||||
enable = true;
|
||||
config = {
|
||||
theme = lib.mkDefault "TwoDark";
|
||||
pager = lib.mkDefault "less -FR";
|
||||
};
|
||||
};
|
||||
|
||||
programs.fzf = {
|
||||
enable = true;
|
||||
enableBashIntegration = true;
|
||||
defaultCommand = lib.mkDefault "fd --type f --hidden --follow --exclude .git";
|
||||
defaultOptions = lib.mkDefault [
|
||||
"--height 40%"
|
||||
"--layout reverse"
|
||||
"--border"
|
||||
"--inline-info"
|
||||
"--color 'fg:#bbccdd,fg+:#ddeeff,bg:#334455,preview-bg:#223344,border:#778899'"
|
||||
];
|
||||
};
|
||||
|
||||
programs.btop = {
|
||||
enable = true;
|
||||
settings = {
|
||||
color_theme = lib.mkDefault "tokyo-night";
|
||||
theme_background = lib.mkDefault false;
|
||||
update_ms = lib.mkDefault 1000;
|
||||
};
|
||||
};
|
||||
|
||||
programs.gpg.enable = true;
|
||||
services.gpg-agent = {
|
||||
enable = true;
|
||||
pinentry.package = pkgs.pinentry-gtk2;
|
||||
};
|
||||
|
||||
programs.tmux = {
|
||||
enable = true;
|
||||
shell = "${pkgs.bash}/bin/bash";
|
||||
terminal = "tmux-256color";
|
||||
prefix = "C-a";
|
||||
mouse = true;
|
||||
keyMode = "vi";
|
||||
baseIndex = 1;
|
||||
escapeTime = 0;
|
||||
historyLimit = 1000000;
|
||||
|
||||
plugins = with pkgs.tmuxPlugins; [
|
||||
yank
|
||||
vim-tmux-navigator
|
||||
tmux-thumbs
|
||||
tmux-fzf
|
||||
fzf-tmux-url
|
||||
|
||||
{
|
||||
plugin = resurrect;
|
||||
extraConfig = ''
|
||||
set -g @resurrect-strategy-nvim 'session'
|
||||
set -g @resurrect-capture-pane-contents 'on'
|
||||
'';
|
||||
}
|
||||
{
|
||||
plugin = continuum;
|
||||
extraConfig = ''
|
||||
set -g @continuum-restore 'on'
|
||||
set -g @continuum-save-interval '15'
|
||||
'';
|
||||
}
|
||||
{
|
||||
plugin = tmux-sessionx;
|
||||
extraConfig = ''
|
||||
set -g @sessionx-bind 'o'
|
||||
set -g @sessionx-bind-zo-new-window 'ctrl-y'
|
||||
set -g @sessionx-auto-accept 'off'
|
||||
set -g @sessionx-window-height '85%'
|
||||
set -g @sessionx-window-width '75%'
|
||||
set -g @sessionx-zoxide-mode 'on'
|
||||
set -g @sessionx-custom-paths-subdirectories 'false'
|
||||
set -g @sessionx-filter-current 'false'
|
||||
'';
|
||||
}
|
||||
{
|
||||
plugin = tmux-floax;
|
||||
extraConfig = ''
|
||||
set -g @floax-width '80%'
|
||||
set -g @floax-height '80%'
|
||||
set -g @floax-border-color 'magenta'
|
||||
set -g @floax-text-color 'blue'
|
||||
set -g @floax-bind 'p'
|
||||
set -g @floax-change-path 'true'
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
extraConfig = ''
|
||||
set -ga terminal-overrides ',*256col*:RGB'
|
||||
set -g detach-on-destroy off
|
||||
set -g renumber-windows on
|
||||
set -g set-clipboard on
|
||||
set -g status-position top
|
||||
|
||||
# Forward modified keys (Shift+Enter, Ctrl+Enter, …) in CSI-u
|
||||
# format. Required for pi-coding-agent (and other TUIs that
|
||||
# distinguish modified Enter); see
|
||||
# https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/tmux.md
|
||||
set -g extended-keys on
|
||||
set -g extended-keys-format csi-u
|
||||
set -g pane-active-border-style 'fg=magenta,bg=default'
|
||||
set -g pane-border-style 'fg=brightblack,bg=default'
|
||||
|
||||
set -g @fzf-url-fzf-options '-p 60%,30% --prompt=" " --border-label=" Open URL "'
|
||||
set -g @fzf-url-history-limit '2000'
|
||||
|
||||
# --- window / session bindings ---
|
||||
bind ^X lock-server
|
||||
bind ^C new-window -c "$HOME"
|
||||
bind ^D detach
|
||||
bind '*' list-clients
|
||||
|
||||
bind H previous-window
|
||||
bind L next-window
|
||||
bind r command-prompt "rename-window %%"
|
||||
bind R source-file ~/.config/tmux/tmux.conf \; display "reloaded"
|
||||
bind ^A last-window
|
||||
bind ^W list-windows
|
||||
bind w list-windows
|
||||
bind z resize-pane -Z
|
||||
bind ^L refresh-client
|
||||
bind | split-window -h -c "#{pane_current_path}"
|
||||
bind s split-window -v -c "#{pane_current_path}"
|
||||
bind v split-window -h -c "#{pane_current_path}"
|
||||
bind '"' choose-window
|
||||
|
||||
# pane resize (repeatable); pane-nav handled by vim-tmux-navigator via C-hjkl
|
||||
bind -r h resize-pane -L 5
|
||||
bind -r j resize-pane -D 5
|
||||
bind -r k resize-pane -U 5
|
||||
bind -r l resize-pane -R 5
|
||||
|
||||
bind : command-prompt
|
||||
bind P set pane-border-status
|
||||
bind x swap-pane -D
|
||||
bind S choose-session
|
||||
bind K send-keys "clear" \; send-keys "Enter"
|
||||
bind-key -T copy-mode-vi v send-keys -X begin-selection
|
||||
|
||||
bind-key g popup -E -w 95% -h 95% -d '#{pane_current_path}' ${pkgs.lazygit}/bin/lazygit
|
||||
'';
|
||||
};
|
||||
|
||||
xdg = {
|
||||
enable = true;
|
||||
userDirs = {
|
||||
enable = true;
|
||||
createDirectories = true;
|
||||
setSessionVariables = true;
|
||||
};
|
||||
};
|
||||
|
||||
gtk = {
|
||||
enable = true;
|
||||
theme = {
|
||||
name = lib.mkDefault "Catppuccin-Mocha-Standard-Blue-dark";
|
||||
package = lib.mkDefault (
|
||||
pkgs.catppuccin-gtk.override {
|
||||
variant = "mocha";
|
||||
}
|
||||
);
|
||||
};
|
||||
gtk4.theme = null;
|
||||
iconTheme = {
|
||||
name = lib.mkDefault "Papirus-Dark";
|
||||
package = lib.mkDefault pkgs.papirus-icon-theme;
|
||||
};
|
||||
};
|
||||
|
||||
qt = {
|
||||
enable = true;
|
||||
platformTheme.name = lib.mkDefault "gtk3";
|
||||
style.name = lib.mkDefault "gtk2";
|
||||
};
|
||||
|
||||
home.pointerCursor = {
|
||||
package = pkgs.catppuccin-cursors.mochaDark;
|
||||
name = "catppuccin-mocha-dark-cursors";
|
||||
size = 24;
|
||||
gtk.enable = true;
|
||||
x11.enable = true;
|
||||
};
|
||||
|
||||
# Weekly refresh + digest of ~/dev/refs/ reference repos.
|
||||
# Manifest at ~/dev/refs/refs.toml; see ~/dev/refs/README.md.
|
||||
systemd.user.services.refs-refresh = {
|
||||
Unit.Description = "Refresh and digest reference repos";
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
ExecStart = [
|
||||
# `-` prefix: a per-repo refresh error must not abort the digests
|
||||
# (a oneshot stops on the first failing ExecStart otherwise).
|
||||
"-%h/dev/refs/bin/refresh"
|
||||
"%h/dev/refs/bin/digest"
|
||||
"%h/dev/refs/bin/upstream-digest"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.user.timers.refs-refresh = {
|
||||
Unit.Description = "Weekly reference-repo refresh";
|
||||
Timer = {
|
||||
OnCalendar = "Mon 09:00";
|
||||
Persistent = true;
|
||||
};
|
||||
Install.WantedBy = [ "timers.target" ];
|
||||
};
|
||||
|
||||
# Weekly snapshot of ~/.claude/ memory drift.
|
||||
# Auto-commits + pushes to padreug/claude-memory if there's any drift since
|
||||
# last run. No-op on a clean tree. Script at ~/.claude/bin/snapshot.
|
||||
systemd.user.services.claude-memory-snapshot = {
|
||||
Unit.Description = "Weekly snapshot of ~/.claude/ memory";
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "%h/.claude/bin/snapshot";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.user.timers.claude-memory-snapshot = {
|
||||
Unit.Description = "Weekly snapshot of ~/.claude/ memory";
|
||||
Timer = {
|
||||
OnCalendar = "Mon 09:30"; # 30 min after refs-refresh
|
||||
Persistent = true;
|
||||
};
|
||||
Install.WantedBy = [ "timers.target" ];
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue