Compare commits
14
Commits
5d0a33d09f
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a6830c24e
|
||
|
|
e0912f568c
|
||
|
|
cb01efd267
|
||
|
|
0f7f1dade2
|
||
|
|
bbb468c432
|
||
|
|
24b940810e
|
||
|
|
d4f326bfd9
|
||
|
|
93a4a934eb
|
||
|
|
61300bd9b9
|
||
|
|
38556c8839
|
||
|
|
102fb3c3c4
|
||
|
|
752ee84430
|
||
|
|
fbf68fa4a4
|
||
|
|
b9e8641bbf
|
@@ -101,6 +101,29 @@ this repo pins, not the reverse.
|
|||||||
Your machine list, hostnames, and any private specifics stay in *your* flake — you pull
|
Your machine list, hostnames, and any private specifics stay in *your* flake — you pull
|
||||||
only the generic modules from here.
|
only the generic modules from here.
|
||||||
|
|
||||||
|
## Updating (day-to-day)
|
||||||
|
|
||||||
|
**Every** package version comes from the `nixpkgs` **you** declare — including the packages
|
||||||
|
this repo's own modules install, since they build against your `nixpkgs` via `follows`, not
|
||||||
|
against anything this repo pins. So the whole upgrade happens in your flake; you never touch
|
||||||
|
this repo to move packages, even the ones it declares:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nix flake update # bump every input to newest
|
||||||
|
sudo nixos-rebuild switch --flake .#myhost # build + activate
|
||||||
|
```
|
||||||
|
|
||||||
|
`nix flake update` advances `nixpkgs` to the newest commit on the branch you pinned (e.g.
|
||||||
|
`nixos-unstable` or `nixos-25.11`) — that is what upgrades your packages. `home-manager` and
|
||||||
|
`baseline` **follow** your `nixpkgs`, so they move in lockstep with no version skew. To move
|
||||||
|
one input at a time, name it: `nix flake update nixpkgs` bumps only packages;
|
||||||
|
`nix flake update baseline` pulls the latest **modules** from this repo (new features/fixes)
|
||||||
|
without changing any package version. These are independent knobs — routine upgrades need
|
||||||
|
only the `nixpkgs` bump.
|
||||||
|
|
||||||
|
A **release jump** (e.g. `nixos-25.11` → `nixos-26.05`) is not a plain update: edit the
|
||||||
|
branch in the `nixpkgs` and `home-manager` URLs in your `flake.nix`, then update and rebuild.
|
||||||
|
|
||||||
## Overriding the baseline
|
## Overriding the baseline
|
||||||
|
|
||||||
The public modules are a baseline you extend without forking. Three ways to shape them
|
The public modules are a baseline you extend without forking. Three ways to shape them
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Hunspell dictionaries, joined into one directory so anything needing a
|
||||||
|
# dictionary *path* can be pointed at a single store path.
|
||||||
|
#
|
||||||
|
# Two consumers, two mechanisms:
|
||||||
|
# - LibreOffice needs nothing beyond the package being installed: its wrapper
|
||||||
|
# scans `share/hunspell` in every NIX_PROFILES entry.
|
||||||
|
# - Gecko apps (LibreWolf, Thunderbird) need `spellchecker.dictionary_path`
|
||||||
|
# set explicitly — there is no /usr/share/hunspell on NixOS and wrapFirefox
|
||||||
|
# wires up nothing itself. Point that pref at `local.dictionaries.path`.
|
||||||
|
#
|
||||||
|
# Chromium-based browsers are NOT served by this: they ignore hunspell and
|
||||||
|
# fetch their own dictionaries, selected by enterprise policy.
|
||||||
|
#
|
||||||
|
# Which languages is a personal choice, so the default is empty; a consumer sets
|
||||||
|
# `local.dictionaries.packages = [ pkgs.hunspellDicts.de_DE ];`. en-US ships
|
||||||
|
# inside Firefox/Thunderbird already and does not need listing for those.
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.local.dictionaries;
|
||||||
|
|
||||||
|
joined = pkgs.symlinkJoin {
|
||||||
|
name = "hunspell-dicts";
|
||||||
|
paths = cfg.packages;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.local.dictionaries = {
|
||||||
|
packages = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.package;
|
||||||
|
default = [ ];
|
||||||
|
example = lib.literalExpression "[ pkgs.hunspellDicts.de_DE ]";
|
||||||
|
description = ''
|
||||||
|
Hunspell dictionary packages to install and expose via
|
||||||
|
`local.dictionaries.path`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
path = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
readOnly = true;
|
||||||
|
description = ''
|
||||||
|
Directory holding every `packages` dictionary. Feed this to
|
||||||
|
`spellchecker.dictionary_path` in Gecko apps.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
local.dictionaries.path = "${joined}/share/hunspell";
|
||||||
|
home.packages = lib.optional (cfg.packages != [ ]) joined;
|
||||||
|
};
|
||||||
|
}
|
||||||
+8
-3
@@ -2,7 +2,7 @@
|
|||||||
# signing identity — those are personal (→ inventory). Enabling this alone is
|
# signing identity — those are personal (→ inventory). Enabling this alone is
|
||||||
# harmless: a user with no key just has an idle agent.
|
# harmless: a user with no key just has an idle agent.
|
||||||
|
|
||||||
{ config, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
programs.gpg = {
|
programs.gpg = {
|
||||||
@@ -26,7 +26,12 @@
|
|||||||
# sk-based ssh keys use the normal ssh-agent; don't let gpg-agent hijack
|
# sk-based ssh keys use the normal ssh-agent; don't let gpg-agent hijack
|
||||||
# SSH_AUTH_SOCK.
|
# SSH_AUTH_SOCK.
|
||||||
enableSshSupport = false;
|
enableSshSupport = false;
|
||||||
defaultCacheTtl = 1800; # 30 min
|
# Idle timer, reset on every use. The hard cap runs from the unlock and
|
||||||
maxCacheTtl = 7200; # 2 h
|
# never resets, so a machine in continuous use still re-asks once a workday —
|
||||||
|
# otherwise an unattended unlocked session signs forever.
|
||||||
|
# mkDefault so an inventory can dial these per machine with a plain
|
||||||
|
# `services.gpg-agent.defaultCacheTtl = …;` and no mkForce.
|
||||||
|
defaultCacheTtl = lib.mkDefault 3600; # 1 h idle
|
||||||
|
maxCacheTtl = lib.mkDefault 28800; # 8 h absolute
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-14
@@ -115,14 +115,17 @@ let
|
|||||||
| "keyword monitor \($l.name),preferred,0x0,\($l.scale)"
|
| "keyword monitor \($l.name),preferred,0x0,\($l.scale)"
|
||||||
+ " ; keyword monitor \($r.name),preferred,\($off)x0,\($r.scale)"')
|
+ " ; keyword monitor \($r.name),preferred,\($off)x0,\($r.scale)"')
|
||||||
"$hc" --batch "$cmd"
|
"$hc" --batch "$cmd"
|
||||||
# re-anchor the layer-shell clients onto the new geometry (see comment above)
|
# re-anchor the layer-shell clients onto the new geometry (see comment above).
|
||||||
${pkgs.procps}/bin/pkill -USR2 -x .waybar-wrapped || true
|
# Anchored ERE covers both comms: the wrapper-launched bar (comm
|
||||||
|
# `.waybar-wrapped`) and one the monitor-hotplug handler relaunched via
|
||||||
|
# `hyprctl dispatch exec waybar` (comm `waybar`).
|
||||||
|
${pkgs.procps}/bin/pkill -USR2 -x '\.waybar-wrapped|waybar' || true
|
||||||
bg="$HOME/.local/share/bg"
|
bg="$HOME/.local/share/bg"
|
||||||
[ -e "$bg" ] && ${pkgs.awww}/bin/awww img "$bg" || true
|
[ -e "$bg" ] && ${pkgs.awww}/bin/awww img "$bg" || true
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [ ./gpg.nix ./media.nix ./texlive.nix ./librewolf.nix ./mc.nix ./mime.nix ./hyprland-workspaces.nix ./nvim.nix ];
|
imports = [ ./gpg.nix ./media.nix ./texlive.nix ./librewolf.nix ./mc.nix ./mime.nix ./hyprland-workspaces.nix ./nvim.nix ./dictionaries.nix ];
|
||||||
|
|
||||||
# Put ~/.local/bin (the XDG user-bin dir) on PATH so hand-dropped scripts run
|
# Put ~/.local/bin (the XDG user-bin dir) on PATH so hand-dropped scripts run
|
||||||
# by name without wiring each through nix.
|
# by name without wiring each through nix.
|
||||||
@@ -144,9 +147,8 @@ in
|
|||||||
# -p. Pins throwaway REPLs and new projects to one version across machines
|
# -p. Pins throwaway REPLs and new projects to one version across machines
|
||||||
# (further steered by uv.toml below). Fetched on demand by uv, not Nix.
|
# (further steered by uv.toml below). Fetched on demand by uv, not Nix.
|
||||||
UV_PYTHON = "3.14";
|
UV_PYTHON = "3.14";
|
||||||
# psql / wget / readline configs, XDG-routed (the rc files are declared
|
# wget / readline configs, XDG-routed (the rc files are declared via
|
||||||
# via xdg.configFile below).
|
# xdg.configFile below).
|
||||||
PSQLRC = "${config.xdg.configHome}/psql/rc";
|
|
||||||
WGETRC = "${config.xdg.configHome}/wget/wgetrc";
|
WGETRC = "${config.xdg.configHome}/wget/wgetrc";
|
||||||
INPUTRC = "${config.xdg.configHome}/readline/inputrc";
|
INPUTRC = "${config.xdg.configHome}/readline/inputrc";
|
||||||
# Rust/cargo: keep registry cache, git checkouts and `cargo install` bins
|
# Rust/cargo: keep registry cache, git checkouts and `cargo install` bins
|
||||||
@@ -190,6 +192,9 @@ in
|
|||||||
unzip # extract .zip (tools that shell out to it expect the binary)
|
unzip # extract .zip (tools that shell out to it expect the binary)
|
||||||
jq # JSON on the command line (scripts, ad-hoc inspection)
|
jq # JSON on the command line (scripts, ad-hoc inspection)
|
||||||
pv # pipe progress meter (throughput, ETA on streamed data)
|
pv # pipe progress meter (throughput, ETA on streamed data)
|
||||||
|
git-filter-repo # `git filter-repo`: history rewriting (upstream's replacement
|
||||||
|
# for `git filter-branch`); a standalone script git picks
|
||||||
|
# up as a subcommand, so programs.git needs no config
|
||||||
libreoffice
|
libreoffice
|
||||||
grim # screenshot capture (also used by the Print-key binds)
|
grim # screenshot capture (also used by the Print-key binds)
|
||||||
slurp # interactive region selector for grim
|
slurp # interactive region selector for grim
|
||||||
@@ -218,12 +223,6 @@ in
|
|||||||
python-preference = "managed"
|
python-preference = "managed"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# psql: unlimited, per-database history file (kept out of $HOME).
|
|
||||||
"psql/rc".text = ''
|
|
||||||
\set HISTFILE ${config.xdg.cacheHome}/psql/history-:DBNAME
|
|
||||||
\set HISTSIZE -1
|
|
||||||
'';
|
|
||||||
|
|
||||||
# wget: keep its HSTS db in the cache dir, not ~/.
|
# wget: keep its HSTS db in the cache dir, not ~/.
|
||||||
"wget/wgetrc".text = ''
|
"wget/wgetrc".text = ''
|
||||||
hsts-file=${config.xdg.cacheHome}/wget/hsts
|
hsts-file=${config.xdg.cacheHome}/wget/hsts
|
||||||
@@ -668,8 +667,8 @@ in
|
|||||||
# the ~/.local/share/bg symlink if it exists.
|
# the ~/.local/share/bg symlink if it exists.
|
||||||
"awww-daemon"
|
"awww-daemon"
|
||||||
"sh -c '[ -e \"$HOME/.local/share/bg\" ] && awww img \"$HOME/.local/share/bg\"'"
|
"sh -c '[ -e \"$HOME/.local/share/bg\" ] && awww img \"$HOME/.local/share/bg\"'"
|
||||||
# re-push the wallpaper onto monitors connected after login (media.nix)
|
# re-push wallpaper + relaunch waybar on monitor add/remove (media.nix)
|
||||||
"wallpaper-hotplug"
|
"monitor-hotplug"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+28
-8
@@ -7,7 +7,7 @@
|
|||||||
# awww-daemon and the boot-time restore from that symlink run as Hyprland
|
# awww-daemon and the boot-time restore from that symlink run as Hyprland
|
||||||
# `exec-once` (see home.nix).
|
# `exec-once` (see home.nix).
|
||||||
|
|
||||||
{ pkgs, ... }:
|
{ pkgs, config, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
# setbg: persist the choice as an XDG-data symlink, then set it live. Startup
|
# setbg: persist the choice as an XDG-data symlink, then set it live. Startup
|
||||||
@@ -21,15 +21,35 @@ let
|
|||||||
# awww-daemon paints only the outputs present when `awww img` last ran and does
|
# awww-daemon paints only the outputs present when `awww img` last ran and does
|
||||||
# not repaint a monitor hotplugged afterwards — it comes up on the fill color
|
# not repaint a monitor hotplugged afterwards — it comes up on the fill color
|
||||||
# (black), and a transparent foot on it then shows that black, not the
|
# (black), and a transparent foot on it then shows that black, not the
|
||||||
# wallpaper. Watch Hyprland's event socket and re-push the wallpaper (setbg
|
# wallpaper. Symmetric problem on real DP/HDMI unplug: Hyprland drops the
|
||||||
# with no arg → all current outputs) on each monitor-add. Hyprland emits both
|
# layer-shell surfaces on the surviving monitor and does not recreate them, so
|
||||||
# `monitoradded` and `monitoraddedv2`; match only the former so setbg fires
|
# its waybar and wallpaper vanish. Watch Hyprland's event socket and, on either
|
||||||
# once. Runs as a Hyprland exec-once (see home.nix).
|
# add or remove, re-push the wallpaper (setbg with no arg → all current
|
||||||
wallpaperHotplug = pkgs.writeShellScriptBin "wallpaper-hotplug" ''
|
# outputs) and fully relaunch waybar. A SIGUSR2 reload is not enough — it
|
||||||
|
# rereads config but does not recreate a bar Hyprland already tore down — so
|
||||||
|
# kill and restart, which enumerates the current outputs and draws a bar on
|
||||||
|
# each. The event flaps (several add/remove lines per plug); the loop is
|
||||||
|
# sequential so each pass converges to one waybar. Hyprland emits both
|
||||||
|
# `monitoradded` and `monitoraddedv2`; match only the non-v2 forms so each side
|
||||||
|
# fires once. Runs as a Hyprland exec-once (see home.nix).
|
||||||
|
hyprctl = "${config.wayland.windowManager.hyprland.finalPackage}/bin/hyprctl";
|
||||||
|
monitorHotplug = pkgs.writeShellScriptBin "monitor-hotplug" ''
|
||||||
sock="''${XDG_RUNTIME_DIR}/hypr/''${HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock"
|
sock="''${XDG_RUNTIME_DIR}/hypr/''${HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock"
|
||||||
${pkgs.socat}/bin/socat -u UNIX-CONNECT:"$sock" - | while IFS= read -r line; do
|
${pkgs.socat}/bin/socat -u UNIX-CONNECT:"$sock" - | while IFS= read -r line; do
|
||||||
case "$line" in
|
case "$line" in
|
||||||
"monitoradded>>"*) setbg ;;
|
"monitoradded>>"* | "monitorremoved>>"*)
|
||||||
|
sleep 1 # let Hyprland settle the new geometry
|
||||||
|
setbg # re-push wallpaper to all current outputs
|
||||||
|
# Anchored ERE matching `comm` (max 15 chars): the bar launched via the
|
||||||
|
# home-manager wrapper reports comm `.waybar-wrapped` (arg[0] is still
|
||||||
|
# `waybar`), so a bare `-x waybar` never matches it and leaves a stale bar.
|
||||||
|
wb='\.waybar-wrapped|waybar'
|
||||||
|
${pkgs.procps}/bin/pkill -x "$wb" || true # relaunch waybar fresh (see comment)
|
||||||
|
i=0
|
||||||
|
while [ $i -lt 6 ] && ${pkgs.procps}/bin/pgrep -x "$wb" >/dev/null; do sleep 0.3; i=$((i+1)); done
|
||||||
|
${pkgs.procps}/bin/pkill -9 -x "$wb" || true
|
||||||
|
${hyprctl} dispatch exec waybar
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
'';
|
'';
|
||||||
@@ -66,7 +86,7 @@ in
|
|||||||
|
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
setbg
|
setbg
|
||||||
wallpaperHotplug # re-push wallpaper to a hotplugged monitor (see comment above)
|
monitorHotplug # re-push wallpaper + reload waybar on monitor add/remove (see comment above)
|
||||||
awww # wayland wallpaper daemon (setbg backend)
|
awww # wayland wallpaper daemon (setbg backend)
|
||||||
wl-clipboard # wl-copy, for the yank binds
|
wl-clipboard # wl-copy, for the yank binds
|
||||||
imagemagick # `magick`: rotate/flip binds
|
imagemagick # `magick`: rotate/flip binds
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [ ./shutdown-debug.nix ./printing-scanning.nix ];
|
imports = [ ./shutdown-debug.nix ./printing-scanning.nix ./rebuild-diff.nix ];
|
||||||
|
|
||||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||||
# Deduplicate identical store paths via hardlinks.
|
# Deduplicate identical store paths via hardlinks.
|
||||||
|
|||||||
+80
-9
@@ -1,8 +1,26 @@
|
|||||||
# Neovim, configured declaratively via nixvim. The editor — plugins, LSP,
|
# Neovim, configured declaratively via nixvim. The editor — plugins, LSP,
|
||||||
# completion, keymaps — is defined here and pinned by the flake lock; nothing is
|
# completion, keymaps — is defined here and pinned by the flake lock; nothing is
|
||||||
# downloaded at runtime, unlike an imperative distro (NvChad/LazyVim). The nixvim
|
# downloaded at runtime. The nixvim home-manager module that provides
|
||||||
# home-manager module that provides `programs.nixvim` is imported for consumers by
|
# `programs.nixvim` is imported for consumers by `homeModules.default` in
|
||||||
# `homeModules.default` in flake.nix, so this file only sets options.
|
# flake.nix, so this file only sets options.
|
||||||
|
#
|
||||||
|
# Why this rather than a distro (LazyVim, NvChad, AstroNvim): those drive
|
||||||
|
# lazy.nvim as a package manager, which git-clones plugins into
|
||||||
|
# ~/.local/share/nvim on first launch and updates them in place. The editor's
|
||||||
|
# actual contents would then live outside the flake — unpinned, unreproducible,
|
||||||
|
# and invisible to a rollback.
|
||||||
|
#
|
||||||
|
# That choice has a price, and it is paid in ergonomics. A distro ships hundreds
|
||||||
|
# of defaults other people already sanded smooth; this config does not. Rough
|
||||||
|
# edges surface one at a time, mid-task, and each gets fixed by hand here (see
|
||||||
|
# the `<leader>bd` and `<leader>Q` keymaps for two such fixes). Anyone extending
|
||||||
|
# this should expect that cost rather than be surprised by it.
|
||||||
|
#
|
||||||
|
# The middle path stays open: the objection is to lazy.nvim as a *runtime*
|
||||||
|
# package manager, not to a distro's ergonomics. Individual keymaps, option
|
||||||
|
# sets, and plugin configurations can be lifted from LazyVim & co. into this
|
||||||
|
# file, and nixvim installs any plugin set declaratively. Borrow freely — just
|
||||||
|
# keep the installation declarative.
|
||||||
#
|
#
|
||||||
# Consumer knobs (the option-with-default contract): `local.nvim.enable` to opt
|
# Consumer knobs (the option-with-default contract): `local.nvim.enable` to opt
|
||||||
# out wholesale, `local.nvim.colorscheme` to swap theme in one line, and
|
# out wholesale, `local.nvim.colorscheme` to swap theme in one line, and
|
||||||
@@ -86,8 +104,8 @@ in
|
|||||||
scrolloff = 8; # keep 8 lines of context around the cursor
|
scrolloff = 8; # keep 8 lines of context around the cursor
|
||||||
|
|
||||||
expandtab = true; # spaces, not tabs
|
expandtab = true; # spaces, not tabs
|
||||||
shiftwidth = 2;
|
shiftwidth = 4;
|
||||||
tabstop = 2;
|
tabstop = 4;
|
||||||
smartindent = true;
|
smartindent = true;
|
||||||
|
|
||||||
ignorecase = true; # case-insensitive search…
|
ignorecase = true; # case-insensitive search…
|
||||||
@@ -162,10 +180,31 @@ in
|
|||||||
blink-cmp = {
|
blink-cmp = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings = {
|
settings = {
|
||||||
keymap.preset = "default"; # C-space open, C-y confirm, C-n/C-p move
|
# "enter" preset: <CR> accepts, Up/Down and C-n/C-p move, Tab is
|
||||||
|
# left to snippet jumping. Custom keys below merge over the preset.
|
||||||
|
keymap = {
|
||||||
|
preset = "enter";
|
||||||
|
# Vim's own ins-completion confirm key, kept alongside <CR>.
|
||||||
|
"<C-y>" = [ "select_and_accept" "fallback" ];
|
||||||
|
# Dismiss the menu and undo the auto_insert preview, staying in
|
||||||
|
# insert mode. `cancel` rather than the preset's `hide`, which
|
||||||
|
# would leave the previewed text in the buffer without the
|
||||||
|
# additionalTextEdits (imports) and snippet expansion that a real
|
||||||
|
# accept applies — a degraded accept nobody wants. This also
|
||||||
|
# matches builtin ins-completion, where C-e restores the typed
|
||||||
|
# text. With no menu open both keys fall back to their native
|
||||||
|
# meaning (Esc leaves insert mode).
|
||||||
|
"<C-e>" = [ "cancel" "fallback" ];
|
||||||
|
"<Esc>" = [ "cancel" "fallback" ];
|
||||||
|
};
|
||||||
appearance.nerd_font_variant = "mono";
|
appearance.nerd_font_variant = "mono";
|
||||||
sources.default = [ "lsp" "path" "snippets" "buffer" ];
|
sources.default = [ "lsp" "path" "snippets" "buffer" ];
|
||||||
completion.documentation.auto_show = true;
|
completion = {
|
||||||
|
documentation.auto_show = true;
|
||||||
|
# No item is selected until one is picked with Down/C-n, so <CR>
|
||||||
|
# inserts a newline unless a completion was deliberately chosen.
|
||||||
|
list.selection.preselect = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -227,11 +266,24 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Moodle/Mustache templates. nvim detects `.mustache` as filetype mustache
|
||||||
|
# but ships no syntax file for it, so the buffer renders as plain text.
|
||||||
|
# Tree-sitter is not an option here: the Handlebars grammar (glimmer)
|
||||||
|
# rejects mustache's argument-less sections, `{{{unescaped}}}`, partials
|
||||||
|
# and Moodle's `{{< parent }}` inheritance, and a single error node drops
|
||||||
|
# highlighting for the rest of the file. This plugin carries the syntax,
|
||||||
|
# indent and ftdetect scripts. nixpkgs marks it unfree only because
|
||||||
|
# upstream ships no license metadata; `allowUnfree` in modules/nixos.nix
|
||||||
|
# already covers it.
|
||||||
|
extraPlugins = [ pkgs.vimPlugins.vim-mustache-handlebars ];
|
||||||
|
|
||||||
# Global (non-LSP) keymaps. Deliberately small — a starting point to grow.
|
# Global (non-LSP) keymaps. Deliberately small — a starting point to grow.
|
||||||
keymaps = [
|
keymaps = [
|
||||||
{ mode = "n"; key = "<leader>w"; action = "<cmd>w<cr>"; options.desc = "Save"; }
|
{ mode = "n"; key = "<leader>w"; action = "<cmd>w<cr>"; options.desc = "Save"; }
|
||||||
{ mode = "n"; key = "<leader>q"; action = "<cmd>q<cr>"; options.desc = "Quit window"; }
|
{ mode = "n"; key = "<leader>q"; action = "<cmd>q<cr>"; options.desc = "Quit window"; }
|
||||||
{ mode = "n"; key = "<leader>Q"; action = "<cmd>qa<cr>"; options.desc = "Quit all (exit nvim)"; }
|
# `confirm` prompts Save/Discard/Cancel per modified buffer rather than
|
||||||
|
# aborting with E37, so quitting never dead-ends and never writes silently.
|
||||||
|
{ mode = "n"; key = "<leader>Q"; action = "<cmd>confirm qa<cr>"; options.desc = "Quit all (prompt to save)"; }
|
||||||
# Restore the saved session for the current directory (persistence.nvim).
|
# Restore the saved session for the current directory (persistence.nvim).
|
||||||
{ mode = "n"; key = "<leader>S"; action.__raw = "function() require('persistence').load() end"; options.desc = "Restore session"; }
|
{ mode = "n"; key = "<leader>S"; action.__raw = "function() require('persistence').load() end"; options.desc = "Restore session"; }
|
||||||
{ mode = "n"; key = "<leader>e"; action = "<cmd>Neotree toggle<cr>"; options.desc = "File tree"; }
|
{ mode = "n"; key = "<leader>e"; action = "<cmd>Neotree toggle<cr>"; options.desc = "File tree"; }
|
||||||
@@ -251,7 +303,26 @@ in
|
|||||||
# Buffer navigation (no bufferline: cycle with Shift-h/l, list via <leader>fb)
|
# Buffer navigation (no bufferline: cycle with Shift-h/l, list via <leader>fb)
|
||||||
{ mode = "n"; key = "<S-l>"; action = "<cmd>bnext<cr>"; options.desc = "Next buffer"; }
|
{ mode = "n"; key = "<S-l>"; action = "<cmd>bnext<cr>"; options.desc = "Next buffer"; }
|
||||||
{ mode = "n"; key = "<S-h>"; action = "<cmd>bprevious<cr>"; options.desc = "Prev buffer"; }
|
{ mode = "n"; key = "<S-h>"; action = "<cmd>bprevious<cr>"; options.desc = "Prev buffer"; }
|
||||||
{ mode = "n"; key = "<leader>bd"; action = "<cmd>bdelete<cr>"; options.desc = "Close buffer"; }
|
# Plain `:bdelete` closes the window when no other listed buffer is left
|
||||||
|
# for it to show, which with neo-tree open leaves a full-width tree.
|
||||||
|
# Point the window at another buffer (or a fresh empty one) first, so the
|
||||||
|
# layout survives; `confirm` prompts rather than failing when modified.
|
||||||
|
{
|
||||||
|
mode = "n";
|
||||||
|
key = "<leader>bd";
|
||||||
|
action.__raw = ''
|
||||||
|
function()
|
||||||
|
local cur = vim.api.nvim_get_current_buf()
|
||||||
|
local listed = vim.tbl_filter(
|
||||||
|
function(b) return vim.bo[b].buflisted end,
|
||||||
|
vim.api.nvim_list_bufs()
|
||||||
|
)
|
||||||
|
if #listed > 1 then vim.cmd('bprevious') else vim.cmd('enew') end
|
||||||
|
vim.cmd('confirm bdelete ' .. cur)
|
||||||
|
end
|
||||||
|
'';
|
||||||
|
options.desc = "Close buffer";
|
||||||
|
}
|
||||||
|
|
||||||
# Terminal: toggle a horizontal or vertical split (toggleterm). Same key
|
# Terminal: toggle a horizontal or vertical split (toggleterm). Same key
|
||||||
# hides it again. From inside the terminal, <C-w> drops to normal mode and
|
# hides it again. From inside the terminal, <C-w> drops to normal mode and
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# `nixos-diff`: after a rebuild, report (1) what packages changed and (2) whether the
|
||||||
|
# change needs a reboot, a re-login, or nothing. NixOS keeps two markers —
|
||||||
|
# /run/booted-system (what you booted) and /run/current-system (what the last `switch`
|
||||||
|
# activated). A switch applies userland live, but:
|
||||||
|
# - the kernel, initrd, and kernel modules are frozen until the next BOOT — detected
|
||||||
|
# exactly, by comparing those store paths between the two systems;
|
||||||
|
# - session-mapped components (the Wayland compositor, GL stack, portals) a running
|
||||||
|
# graphical session keeps until you log out and back in — detected HEURISTICALLY,
|
||||||
|
# by matching changed package names against `local.rebuildDiff.reloginPackages`.
|
||||||
|
# Generic and topology-free, so it rides in the baseline; disable with
|
||||||
|
# `local.rebuildDiff.enable = false`.
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.local.rebuildDiff;
|
||||||
|
reloginPattern = lib.concatStringsSep "|" cfg.reloginPackages;
|
||||||
|
|
||||||
|
nixos-diff = pkgs.writeShellApplication {
|
||||||
|
name = "nixos-diff";
|
||||||
|
runtimeInputs = [ pkgs.nvd pkgs.coreutils pkgs.gnugrep ];
|
||||||
|
text = ''
|
||||||
|
booted=/run/booted-system
|
||||||
|
current=/run/current-system
|
||||||
|
|
||||||
|
# Package-level diff of the running system vs the activated one. Force colour
|
||||||
|
# (nvd auto-disables it when captured); only version parts are coloured, so the
|
||||||
|
# package-name grep below is unaffected.
|
||||||
|
diff=$(nvd --color always diff "$booted" "$current")
|
||||||
|
printf '%s\n' "$diff"
|
||||||
|
|
||||||
|
# Reboot (exact): kernel/initrd/modules only take effect on the next boot.
|
||||||
|
reboot=0
|
||||||
|
for c in kernel initrd kernel-modules; do
|
||||||
|
[ "$(readlink -f "$booted/$c" 2>/dev/null)" = "$(readlink -f "$current/$c" 2>/dev/null)" ] \
|
||||||
|
|| reboot=1
|
||||||
|
done
|
||||||
|
|
||||||
|
# Re-login (heuristic): session-mapped components a running Wayland session
|
||||||
|
# keeps until you log out and back in.
|
||||||
|
relogin_hits=""
|
||||||
|
${lib.optionalString (reloginPattern != "") ''
|
||||||
|
relogin_hits=$(printf '%s\n' "$diff" | grep -Ew "${reloginPattern}" || true)
|
||||||
|
''}
|
||||||
|
|
||||||
|
echo
|
||||||
|
if [ "$reboot" -eq 1 ]; then
|
||||||
|
printf '>>> REBOOT needed (kernel/initrd/modules changed).\n'
|
||||||
|
elif [ -n "$relogin_hits" ]; then
|
||||||
|
printf '>>> RE-LOGIN recommended — session components changed:\n'
|
||||||
|
printf '%s\n' "$relogin_hits"
|
||||||
|
else
|
||||||
|
printf '>>> No reboot/re-login needed.\n'
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.local.rebuildDiff = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Install `nixos-diff`: after a `nixos-rebuild switch`, print an `nvd` closure
|
||||||
|
diff of the running vs activated system plus a reboot / re-login / live verdict.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
reloginPackages = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ "mesa" "wayland" "hyprland" "xdg-desktop-portal" ];
|
||||||
|
description = ''
|
||||||
|
Whole-word package-name patterns matched against the diff; a match means a
|
||||||
|
running Wayland session should be restarted (re-login) to pick the change up —
|
||||||
|
the compositor, GL stack, and portals. Extend per host (e.g. a specific GPU
|
||||||
|
driver). Empty disables the re-login heuristic.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
environment.systemPackages = [ nixos-diff pkgs.nvd ];
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user