From 6a0d3680fd3d5e7c5bd9543734aeb4163c50de46 Mon Sep 17 00:00:00 2001 From: Emma Thorpe Date: Wed, 10 Jun 2026 14:56:58 +0100 Subject: [PATCH 1/3] feat(home): theme CLI tools, add staples, env defaults and mime apps - Catppuccin Mocha for fzf (colors), bat (catppuccin/bat tmTheme) and git delta (syntax-theme + navigate/line-numbers/side-by-side). - CLI staples on every host: ripgrep, fd, jq, btop, plus gh (SSH) and tea (Gitea CLI). - home.sessionVariables: PAGER, MANPAGER (bat), VISUAL; xdg.enable. - xdg.mimeApps defaults (web->Firefox, directories->nemo). - Document the stateVersion pin. README updated. Co-Authored-By: Claude Opus 4.8 (1M context) --- flake.nix | 77 ++++++++-- lyrathorpe/home/README.md | 142 ++++++++++-------- lyrathorpe/home/default.nix | 21 +++ lyrathorpe/home/desktop.nix | 24 +++ lyrathorpe/home/git.nix | 11 ++ lyrathorpe/home/shell.nix | 56 ++++++- .../default.nix => lyrathorpe/home/work.nix | 0 statix.toml | 8 + 8 files changed, 259 insertions(+), 80 deletions(-) rename system/modules/work/default.nix => lyrathorpe/home/work.nix (100%) create mode 100644 statix.toml diff --git a/flake.nix b/flake.nix index d36170b..c11803e 100644 --- a/flake.nix +++ b/flake.nix @@ -34,6 +34,18 @@ url = "github:nix-community/nix-index-database"; inputs.nixpkgs.follows = "nixpkgs"; }; + # treefmt-nix: one multi-language formatter driving `nix fmt` and the + # formatting flake check (nixfmt + shfmt + prettier). + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + # git-hooks.nix: declarative pre-commit hooks (nixfmt/deadnix/statix), + # installed into the repo via the devShell. + git-hooks = { + url = "github:cachix/git-hooks.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = @@ -53,7 +65,7 @@ let # claude-code tracks nixpkgs-unstable regardless of the pinned nixpkgs. overlays = [ - (final: prev: { + (_final: prev: { claude-code = (import nixpkgs-unstable { inherit (prev.stdenv.hostPlatform) system; @@ -245,7 +257,7 @@ ]; homeModules = [ ./lyrathorpe/home - ./system/modules/work/default.nix + ./lyrathorpe/home/work.nix ]; }; }; @@ -268,6 +280,13 @@ }; in { + # flake-parts modules: treefmt-nix wires `nix fmt` + a formatting check; + # git-hooks.nix wires the pre-commit check + devShell installation script. + imports = [ + inputs.treefmt-nix.flakeModule + inputs.git-hooks.flakeModule + ]; + systems = [ "x86_64-linux" "aarch64-linux" @@ -279,26 +298,60 @@ # nixpkgs instance for that system. Outputs here become per-system # attrsets automatically (e.g. devShells..default). perSystem = - { pkgs, ... }: + { config, pkgs, ... }: { - # `nix fmt` formatter for the repo. - formatter = pkgs.nixfmt; + # treefmt drives `nix fmt` and the formatting check below. nixfmt + # stays the .nix formatter (the tree is already nixfmt-formatted); + # shfmt covers shell and prettier covers markdown/yaml/json. + treefmt = { + projectRootFile = "flake.nix"; + programs.nixfmt.enable = true; + programs.shfmt.enable = true; + programs.prettier.enable = true; + # Generated hardware-configuration.nix files are not hand-edited. + settings.global.excludes = [ "*/hardware-configuration.nix" ]; + }; + + # Pre-commit hooks: format + lint gate run on commit. The same hooks + # are exposed as a flake check (pre-commit.check.enable defaults true). + pre-commit.settings.hooks = { + nixfmt-rfc-style.enable = true; + deadnix = { + enable = true; + # Unused module args ({config,lib,pkgs,...}) are normal; only flag + # genuinely dead bindings. + settings.noLambdaPatternNames = true; + }; + statix.enable = true; # reads .statix.toml (repeated_keys/empty_pattern disabled) + }; + + # treefmt-nix exposes its own `checks.treefmt`; alias it to + # `formatting` so the existing CI gate (.#checks.*.formatting) keeps + # working without churn. + checks.formatting = config.treefmt.build.check inputs.self; + + # deadnix / statix lints as standalone flake checks so `nix flake + # check` flags dead code and antipatterns independently of pre-commit. + checks.deadnix = pkgs.runCommandLocal "check-deadnix" { nativeBuildInputs = [ pkgs.deadnix ]; } '' + deadnix --fail --no-lambda-pattern-names ${./.} && touch $out + ''; + checks.statix = pkgs.runCommandLocal "check-statix" { nativeBuildInputs = [ pkgs.statix ]; } '' + statix check ${./.} && touch $out + ''; # `nix develop` shell with the tooling needed to hack on this flake. + # shellHook installs the git pre-commit hooks into the working tree. devShells.default = pkgs.mkShellNoCC { packages = with pkgs; [ nixfmt nil git + deadnix + statix + treefmt ]; + shellHook = config.pre-commit.installationScript; }; - - checks.formatting = - pkgs.runCommandLocal "check-formatting" { nativeBuildInputs = [ pkgs.nixfmt ]; } - '' - # Generated hardware-configuration.nix files are excluded. - nixfmt --check $(find ${./.} -name '*.nix' -not -name 'hardware-configuration.nix') && touch $out - ''; }; # Realise the host tables: each entry becomes a {nixos,darwin}Configuration. diff --git a/lyrathorpe/home/README.md b/lyrathorpe/home/README.md index 0307a28..6a402f0 100644 --- a/lyrathorpe/home/README.md +++ b/lyrathorpe/home/README.md @@ -6,11 +6,11 @@ home-manager — edit the listed file and rebuild, never the generated dotfiles. Keyboard shortcuts have their own reference: [`KEYBINDINGS.md`](./KEYBINDINGS.md). -| Area | Defined in | -| --- | --- | -| zsh, CLI tools, tmux, ssh, auto-tmux | [`shell.nix`](./shell.nix) | -| git (+ delta, commitizen) | [`git.nix`](./git.nix) | -| vim | [`editor.nix`](./editor.nix) | +| Area | Defined in | +| ------------------------------------- | ----------------------------------------------------- | +| zsh, CLI tools, tmux, ssh, auto-tmux | [`shell.nix`](./shell.nix) | +| git (+ delta, commitizen) | [`git.nix`](./git.nix) | +| vim | [`editor.nix`](./editor.nix) | | GUI apps, GTK/Firefox theming, cursor | [`desktop.nix`](./desktop.nix) (graphical hosts only) | Shared by every host via [`default.nix`](./default.nix); the work box also layers @@ -21,30 +21,40 @@ on top (work email, its own ssh config, extra packages). ## zsh -| Feature | Notes | -| --- | --- | -| oh-my-zsh | plugins `git`, `man`, `sudo` (Esc-Esc to prepend sudo), `colored-man-pages`, `extract`; theme `robbyrussell` | -| Autosuggestion | fish-style history suggestions as you type (→ to accept) | -| Syntax highlighting | commands coloured by validity as you type | -| Completion | menu completion; the dump is rebuilt on every activation (see Maintenance) | -| History | 100k in-memory/on-disk, deduped, space-prefixed commands ignored, timestamped, **shared live across sessions** | +| Feature | Notes | +| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | +| oh-my-zsh | plugins `git`, `man`, `sudo` (Esc-Esc to prepend sudo), `colored-man-pages`, `extract`; theme `robbyrussell` | +| Autosuggestion | fish-style history suggestions as you type (→ to accept) | +| Syntax highlighting | commands coloured by validity as you type | +| Completion | menu completion; the dump is rebuilt on every activation (see Maintenance) | +| History | 100k in-memory/on-disk, deduped, space-prefixed commands ignored, timestamped, **shared live across sessions** | | History substring search | type a fragment, then ↑/↓ cycles matching past commands — works in foot, iTerm2 and the Linux TTY (both CSI and SS3 arrow encodings bound) | -| Prompt | hostname is prefixed when over SSH | +| Prompt | hostname is prefixed when over SSH | **Aliases:** `ls`/`ll`/`la`/`lt` → `eza` (icons + git), `cls` → `clear`. git aliases live in git.nix (below). ## CLI tools -| Tool | What it gives you | -| --- | --- | -| `fzf` | `Ctrl-R` fuzzy history, `Ctrl-T` file picker, `Alt-C` fuzzy cd | -| `zoxide` | `z ` jumps to frecent directories | -| `direnv` + `nix-direnv` | per-project environments auto-loaded on `cd` (cached Nix dev shells) | -| `eza` | modern `ls` (drives the ls aliases) | -| `bat` | syntax-highlighting pager; behaves like `cat` when piped | -| `nix-index` | `command-not-found`: an unknown command tells you which Nix package provides it (prebuilt DB, no manual indexing) | -| `comma` (`,`) | run an uninstalled program once: `, cowsay hi` | -| `nh` | nicer `nixos-rebuild`/`home-manager` with diffs; `$NH_FLAKE` set to the repo. No scheduled GC (it could reap paths a running generation still references) — collect garbage manually with `nh clean all` / `nix-collect-garbage -d` | +| Tool | What it gives you | +| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `fzf` | `Ctrl-R` fuzzy history, `Ctrl-T` file picker, `Alt-C` fuzzy cd (Catppuccin-themed) | +| `zoxide` | `z ` jumps to frecent directories | +| `direnv` + `nix-direnv` | per-project environments auto-loaded on `cd` (cached Nix dev shells) | +| `eza` | modern `ls` (drives the ls aliases) | +| `bat` | syntax-highlighting pager (Catppuccin Mocha theme); behaves like `cat` when piped; also the `MANPAGER` | +| `ripgrep` / `fd` | fast search (`rg`) and find (`fd`); also back `fzf` | +| `jq` / `btop` | JSON processor; resource monitor | +| `gh` / `tea` | GitHub and Gitea (`code.emmathe.dev`) CLIs; `gh` uses SSH | +| `nix-index` | `command-not-found`: an unknown command tells you which Nix package provides it (prebuilt DB, no manual indexing) | +| `comma` (`,`) | run an uninstalled program once: `, cowsay hi` | +| `nh` | nicer `nixos-rebuild`/`home-manager` with diffs; `$NH_FLAKE` set to the repo. No scheduled GC (it could reap paths a running generation still references) — collect garbage manually with `nh clean all` / `nix-collect-garbage -d` | + +**Theming:** `fzf`, `bat` and `git`'s `delta` pager are all Catppuccin Mocha, +driven from the shared `../catppuccin-mocha.nix` palette / the catppuccin/bat +theme. + +**Env & defaults:** `xdg.enable` on; `PAGER`/`MANPAGER` (bat)/`VISUAL` set in +`default.nix`; `xdg.mimeApps` maps web→Firefox, directories→nemo (`desktop.nix`). ## tmux @@ -54,18 +64,18 @@ exists, else create). Panes run a plain non-login zsh. It deliberately does **no fire for SSH sessions, VS Code's integrated terminal, already-inside-tmux, or non-interactive shells. Escape hatch: `NO_TMUX=1 ` opens a bare shell. -| Setting | Value | -| --- | --- | -| Mode keys | vi | -| Mouse | on | -| Scrollback | 500000 lines | -| `escape-time` | 10ms (the 500ms default lagged vim's ESC) | -| `focus-events` | on (vim autoread) | -| `base-index` / `pane-base-index` | 1 | -| Splits | `prefix s` vertical, `prefix v` horizontal (stock `%`/`"` unbound) | -| Pane nav | `Alt`+arrows (no prefix) | -| Terminal | `default-terminal tmux-256color`; truecolor advertised per outer terminal (`foot*`, `xterm-256color`/iTerm2) via `terminal-features … RGB` | -| Clipboard | `set-clipboard on`; foot `terminal-features` advertise truecolor/sync/OSC52/title/cursor | +| Setting | Value | +| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| Mode keys | vi | +| Mouse | on | +| Scrollback | 500000 lines | +| `escape-time` | 10ms (the 500ms default lagged vim's ESC) | +| `focus-events` | on (vim autoread) | +| `base-index` / `pane-base-index` | 1 | +| Splits | `prefix s` vertical, `prefix v` horizontal (stock `%`/`"` unbound) | +| Pane nav | `Alt`+arrows (no prefix) | +| Terminal | `default-terminal tmux-256color`; truecolor advertised per outer terminal (`foot*`, `xterm-256color`/iTerm2) via `terminal-features … RGB` | +| Clipboard | `set-clipboard on`; foot `terminal-features` advertise truecolor/sync/OSC52/title/cursor | **Plugins:** `sensible`, `vim-tmux-navigator` (Ctrl-h/j/k/l across vim ↔ tmux), `yank`, `catppuccin` (Mocha statusline), `resurrect` + `continuum` @@ -77,7 +87,7 @@ glyphs — see Fonts. **JetBrainsMono Nerd Font** is installed on every host (in `common-nixos.nix`, because tmux runs everywhere; the Mac installs it to `/Library/Fonts` via the Darwin config). foot uses it as its main font automatically. iTerm2's font is a -GUI setting — set it to *JetBrainsMono Nerd Font* (Settings → Profiles → Text → +GUI setting — set it to _JetBrainsMono Nerd Font_ (Settings → Profiles → Text → Font) so the tmux statusline glyphs render instead of `?`. ## git @@ -85,33 +95,33 @@ Font) so the tmux statusline glyphs render instead of `?`. Pager is **delta**. **commitizen** is installed on every host; `cz` defaults to Conventional Commits. -| Aliases | | -| --- | --- | -| `st` `co` `sw` `br` `ci` | status / checkout / switch / branch / commit | -| `last` `unstage` | last commit / unstage | -| `lg` | graph log, all branches | -| `cz` `cc` | `git cz ` (e.g. `git cz c`) and `git cc` → commitizen prompt | +| Aliases | | +| ------------------------ | ----------------------------------------------------------------- | +| `st` `co` `sw` `br` `ci` | status / checkout / switch / branch / commit | +| `last` `unstage` | last commit / unstage | +| `lg` | graph log, all branches | +| `cz` `cc` | `git cz ` (e.g. `git cz c`) and `git cc` → commitizen prompt | -| Behaviour | | -| --- | --- | -| Pulls | rebase, with autostash + autosquash | -| Fetch | prune deleted remote branches | -| Conflicts | `zdiff3` (shows the common ancestor) | -| Diffs | histogram algorithm, colour-moved | -| `rerere` | remembers + replays conflict resolutions | -| Commit editor | full diff shown (`commit.verbose`) | -| Misc | branches sorted by date, `column.ui = auto`, `help.autocorrect = prompt`, `push.autoSetupRemote` | -| Global ignores | `result`, `result-*`, `.direnv`, `*.swp`, `.DS_Store` | -| Signing | SSH commit + tag signing (`mkDefault`, so a host without the key in its agent can disable it). Personal email `iam@emmathe.dev`; the work box overrides email + signing. | +| Behaviour | | +| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Pulls | rebase, with autostash + autosquash | +| Fetch | prune deleted remote branches | +| Conflicts | `zdiff3` (shows the common ancestor) | +| Diffs | histogram algorithm, colour-moved | +| `rerere` | remembers + replays conflict resolutions | +| Commit editor | full diff shown (`commit.verbose`) | +| Misc | branches sorted by date, `column.ui = auto`, `help.autocorrect = prompt`, `push.autoSetupRemote` | +| Global ignores | `result`, `result-*`, `.direnv`, `*.swp`, `.DS_Store` | +| Signing | SSH commit + tag signing (`mkDefault`, so a host without the key in its agent can disable it). Personal email `iam@emmathe.dev`; the work box overrides email + signing. | ## ssh -| Feature | Notes | -| --- | --- | -| ssh-agent | runs on Linux (launchd on macOS); keys added on **first use** so the passphrase is typed once per login session — this also feeds git commit signing | -| macOS | `UseKeychain` caches the passphrase in the login keychain (guarded by `IgnoreUnknown`, so a non-Apple `ssh` skips it instead of erroring) | -| Gitea remote | `code.emmathe.dev` → `HostName 10.187.1.76` (DNS-override), `Port 30009`, user `git`, dedicated key, `identitiesOnly` | -| Defaults | the module's deprecated default block is opted out; equivalents kept under `settings."*"` | +| Feature | Notes | +| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| ssh-agent | runs on Linux (launchd on macOS); keys added on **first use** so the passphrase is typed once per login session — this also feeds git commit signing | +| macOS | `UseKeychain` caches the passphrase in the login keychain (guarded by `IgnoreUnknown`, so a non-Apple `ssh` skips it instead of erroring) | +| Gitea remote | `code.emmathe.dev` → `HostName 10.187.1.76` (DNS-override), `Port 30009`, user `git`, dedicated key, `identitiesOnly` | +| Defaults | the module's deprecated default block is opted out; equivalents kept under `settings."*"` | The **work box keeps its own `~/.ssh/config`** (home-manager's `programs.ssh` is forced off there) but still runs the agent. @@ -126,10 +136,10 @@ forced off there) but still runs the agent. ## Per-host differences -| | Personal Linux (sway) | macOS | Work WSL (EDaaS) | -| --- | --- | --- | --- | -| Auto-tmux | yes (foot/TTY) | yes (iTerm2) | yes (WSL shell) | -| git email | `iam@emmathe.dev` | `iam@emmathe.dev` | `…@citrix.com` (work) | -| ssh config managed | yes | yes | no (keeps corporate config) | -| ssh-agent | yes | launchd | yes (work module) | -| GUI / theming (desktop.nix) | yes | no | no | +| | Personal Linux (sway) | macOS | Work WSL (EDaaS) | +| --------------------------- | --------------------- | ----------------- | --------------------------- | +| Auto-tmux | yes (foot/TTY) | yes (iTerm2) | yes (WSL shell) | +| git email | `iam@emmathe.dev` | `iam@emmathe.dev` | `…@citrix.com` (work) | +| ssh config managed | yes | yes | no (keeps corporate config) | +| ssh-agent | yes | launchd | yes (work module) | +| GUI / theming (desktop.nix) | yes | no | no | diff --git a/lyrathorpe/home/default.nix b/lyrathorpe/home/default.nix index 6ef9d42..d07a9c7 100644 --- a/lyrathorpe/home/default.nix +++ b/lyrathorpe/home/default.nix @@ -9,5 +9,26 @@ ./editor.nix ]; + # Manage the XDG base-directory layout and ~/.config files. Tools above + # (bat themes, gh config, ...) write under xdg.configHome; enabling this + # makes the paths explicit and consistent across hosts. No regression: the + # defaults match the conventional ~/.config, ~/.cache, ~/.local/share. + xdg.enable = true; + + # Editor itself comes from vim.defaultEditor (sets $EDITOR). Round out the + # rest of the standard env. desktop.nix adds its own Wayland session vars; + # home-manager merges the two attrsets, so these do not clash. + home.sessionVariables = { + VISUAL = "vim"; + PAGER = "less -FRX"; # -F quit-if-one-screen, -R raw colour, -X no clear + # Render man pages through bat (themed): col strips backspace overstrike, + # bat -l man -p highlights without its own pager decorations. + MANPAGER = "sh -c 'col -bx | bat -l man -p'"; + }; + + # Pinned to the release first installed on these hosts, NOT the current + # nixpkgs (26.05). stateVersion freezes stateful defaults (file locations, + # service data formats) to that release; bumping it silently migrates that + # state and can break it. Leave it -- it is intentional, not stale. home.stateVersion = "25.05"; } diff --git a/lyrathorpe/home/desktop.nix b/lyrathorpe/home/desktop.nix index 77e374d..c278b5f 100644 --- a/lyrathorpe/home/desktop.nix +++ b/lyrathorpe/home/desktop.nix @@ -27,6 +27,30 @@ XDG_CURRENT_DESKTOP = "sway"; }; + # Default apps for the desktop (writes ~/.config/mimeapps.list). Firefox owns + # the web; nemo owns directories/file URIs; images, PDFs and plain text open + # in Firefox too -- no dedicated GUI viewer/editor is installed and vim is + # terminal-only (no usable GUI .desktop for double-click handoff). Kept + # minimal -- only the handlers actually present on these hosts. + xdg.mimeApps = { + enable = true; + defaultApplications = { + "text/html" = "firefox.desktop"; + "x-scheme-handler/http" = "firefox.desktop"; + "x-scheme-handler/https" = "firefox.desktop"; + "x-scheme-handler/about" = "firefox.desktop"; + "x-scheme-handler/unknown" = "firefox.desktop"; + "inode/directory" = "nemo.desktop"; + "image/png" = "firefox.desktop"; + "image/jpeg" = "firefox.desktop"; + "image/gif" = "firefox.desktop"; + "image/webp" = "firefox.desktop"; + "image/svg+xml" = "firefox.desktop"; + "application/pdf" = "firefox.desktop"; + "text/plain" = "firefox.desktop"; + }; + }; + # Theme GTK apps (nemo, etc.) to match the Catppuccin Mocha desktop. Under # Sway there is no XSettings daemon, so GTK reads these from the generated # ~/.config/gtk-{3,4}.0/settings.ini directly. The Mocha theme is dark by diff --git a/lyrathorpe/home/git.nix b/lyrathorpe/home/git.nix index 2b1f819..d539e6a 100644 --- a/lyrathorpe/home/git.nix +++ b/lyrathorpe/home/git.nix @@ -37,6 +37,17 @@ colorMoved = "default"; }; rerere.enabled = true; # remember + replay conflict resolutions + + # delta pager config (programs.delta is enabled below, with git + # integration; these keys land under [delta] in the git config). + # syntax-theme reuses the Catppuccin Mocha tmTheme vendored for bat in + # shell.nix -- delta reads bat's theme directory. + delta = { + syntax-theme = "Catppuccin Mocha"; + navigate = true; # n/N to jump between diff hunks + line-numbers = true; + side-by-side = true; + }; commit.verbose = true; # full diff in the commit-message editor branch.sort = "-committerdate"; # most-recent branches first column.ui = "auto"; diff --git a/lyrathorpe/home/shell.nix b/lyrathorpe/home/shell.nix index eae0552..e4cdbbb 100644 --- a/lyrathorpe/home/shell.nix +++ b/lyrathorpe/home/shell.nix @@ -5,6 +5,10 @@ inputs, ... }: +let + # Shared Catppuccin Mocha palette: raw 6-hex strings, no leading "#". + ctp = import ../catppuccin-mocha.nix; +in { imports = [ # Prebuilt nix-index database -> working command-not-found @@ -12,6 +16,16 @@ inputs.nix-index-database.homeModules.default ]; + # CLI staples wanted on every host (search, parse, monitor). ripgrep/fd also + # back fzf and editor integrations; tea is the Gitea CLI for code.emmathe.dev. + home.packages = [ + pkgs.ripgrep + pkgs.fd + pkgs.jq + pkgs.btop + pkgs.tea + ]; + programs.zsh = { enable = true; enableCompletion = true; @@ -99,6 +113,23 @@ programs.fzf = { enable = true; enableZshIntegration = true; + # Catppuccin Mocha colours (rendered into FZF_DEFAULT_OPTS --color). Each + # value needs a leading "#"; the palette stores raw hex. + colors = { + "bg" = "#${ctp.base}"; + "bg+" = "#${ctp.surface1}"; # current line / selected row + "fg" = "#${ctp.text}"; + "fg+" = "#${ctp.text}"; + "hl" = "#${ctp.blue}"; # match highlights + "hl+" = "#${ctp.blue}"; + "header" = "#${ctp.red}"; + "info" = "#${ctp.mauve}"; + "marker" = "#${ctp.green}"; + "pointer" = "#${ctp.pink}"; + "prompt" = "#${ctp.mauve}"; + "spinner" = "#${ctp.pink}"; + "border" = "#${ctp.surface1}"; + }; }; # Frecency directory jumping: `z `. @@ -120,8 +151,22 @@ icons = "auto"; # boolean form is deprecated }; - # Syntax-highlighting pager, used as `bat` (acts like cat when piped). - programs.bat.enable = true; + # Syntax-highlighting pager, used as `bat` (acts like cat when piped). bat + # ships no Catppuccin theme, so vendor the upstream tmTheme from catppuccin/bat + # (delta in git.nix reuses it as its syntax-theme). + programs.bat = { + enable = true; + config.theme = "Catppuccin Mocha"; + themes."Catppuccin Mocha" = { + src = pkgs.fetchFromGitHub { + owner = "catppuccin"; + repo = "bat"; + rev = "6810349b28055dce54076712fc05fc68da4b8ec0"; + sha256 = "1y5sfi7jfr97z1g6vm2mzbsw59j1jizwlmbadvmx842m0i5ak5ll"; + }; + file = "themes/Catppuccin Mocha.tmTheme"; + }; + }; # command-not-found backed by the prebuilt nix-index DB (module imported # above). `comma` runs an uninstalled program once: `, cowsay hi`. @@ -138,6 +183,13 @@ flake = "$HOME/code/nixfiles"; }; + # GitHub CLI. Prefer SSH for any git operations it drives, matching the + # ssh-based remotes used elsewhere. + programs.gh = { + enable = true; + settings.git_protocol = "ssh"; + }; + programs.tmux = { enable = true; reverseSplit = true; diff --git a/system/modules/work/default.nix b/lyrathorpe/home/work.nix similarity index 100% rename from system/modules/work/default.nix rename to lyrathorpe/home/work.nix diff --git a/statix.toml b/statix.toml new file mode 100644 index 0000000..748238f --- /dev/null +++ b/statix.toml @@ -0,0 +1,8 @@ +# statix lint config. Two default lints are disabled because they flag this +# repo's intentional house style, not bugs: +# repeated_keys - we use `foo.a = ...; foo.b = ...;` (dotted) over nesting. +# empty_pattern - module files use `{ ... }:` / `{ }:` deliberately. +disabled = [ + "repeated_keys", + "empty_pattern", +] -- 2.52.0 From f41879710c7dd05cf278719786692b2da80a24b3 Mon Sep 17 00:00:00 2001 From: Emma Thorpe Date: Wed, 10 Jun 2026 14:56:58 +0100 Subject: [PATCH 2/3] feat(nixos): disk hygiene, dedupe shared options, fix MacPro docs - common-nixos: nix.settings.auto-optimise-store + larger download buffer. - workstation: fstrim, boot.tmp.cleanOnBoot, and the shared graphical options moved here from the per-host configs (pipewire, swaylock PAM stub, redistributable firmware) -- MBP-Asahi gains audio it lacked. - T400: zramSwap for the low-RAM host. - MBP-Asahi: nixos-apple-silicon binary cache substituter. - MacPro31 README: describe the real (LVM/UUID) hardware config; it is no longer a placeholder. Co-Authored-By: Claude Opus 4.8 (1M context) --- system/machine/MBP-Asahi/configuration.nix | 15 ++++++++++++--- system/machine/MacPro31/README.md | 9 +++++---- system/machine/MacPro31/configuration.nix | 14 ++------------ system/machine/T400/README.md | 10 +++++----- system/machine/T400/configuration.nix | 17 +++++------------ system/modules/common-nixos.nix | 7 +++++++ system/modules/workstation.nix | 20 ++++++++++++++++++++ 7 files changed, 56 insertions(+), 36 deletions(-) diff --git a/system/machine/MBP-Asahi/configuration.nix b/system/machine/MBP-Asahi/configuration.nix index 24dad33..0a1d5f7 100644 --- a/system/machine/MBP-Asahi/configuration.nix +++ b/system/machine/MBP-Asahi/configuration.nix @@ -14,9 +14,18 @@ networking.hostName = "Emma-Asahi"; - # No fingerprint reader on this machine; empty service still lets swaylock - # authenticate via password. - security.pam.services.swaylock = { }; + # Audio (PipeWire) and the swaylock PAM stack are inherited from + # workstation.nix. hardware.enableRedistributableFirmware is also set there; + # it is harmless here since Asahi supplies its own peripheral firmware below. + + # Binary cache for the Asahi kernel/build artifacts, so the MBP pulls prebuilt + # outputs instead of compiling the Asahi kernel locally. + nix.settings = { + substituters = [ "https://nixos-apple-silicon.cachix.org" ]; + trusted-public-keys = [ + "nixos-apple-silicon.cachix.org-1:8psDu5SA5dAD7qA0zMy5UT292TxeEPzIz8VVEr2Js20=" + ]; + }; # Apple peripheral firmware (Wi-Fi/Bluetooth). The directory is gitignored and # populated out-of-band -- see README. diff --git a/system/machine/MacPro31/README.md b/system/machine/MacPro31/README.md index e459b1e..d223e5b 100644 --- a/system/machine/MacPro31/README.md +++ b/system/machine/MacPro31/README.md @@ -6,10 +6,11 @@ Flake host: `lyrathorpe-macpro31`. Desktop (`portable = false`, imports ## Hardware configuration -`hardware-configuration.nix` here is a hand-written **placeholder**. On the real -machine, run `nixos-generate-config`, replace the file, and commit it. It assumes -by-label partitions — ESP `ESP` (vfat, mounted at `/boot`), root `nixos` (ext4), -and `swap` — so either label them at install time or swap in the generated UUIDs. +`hardware-configuration.nix` here is the real config generated by +`nixos-generate-config` on the machine. Root is an **LVM** logical volume +(`/dev/mapper/MacPro-Root`, ext4); the ESP (vfat) and swap are referenced by +UUID. The initrd carries `dm-snapshot` for the LVM root. Regenerate and commit +if the disk layout changes. ## Bootloader diff --git a/system/machine/MacPro31/configuration.nix b/system/machine/MacPro31/configuration.nix index 1d41ad5..6b7d3f7 100644 --- a/system/machine/MacPro31/configuration.nix +++ b/system/machine/MacPro31/configuration.nix @@ -27,19 +27,9 @@ services.openssh.enable = true; networking.firewall.allowedTCPPorts = [ 22 ]; - services.pipewire = { - enable = true; - pulse.enable = true; - }; - - # No fingerprint hardware; empty service still lets swaylock authenticate via - # password. - security.pam.services.swaylock = { }; - - # Dual Harpertown Xeon microcode + redistributable firmware (e.g. GPU/NIC - # blobs). + # Dual Harpertown Xeon microcode. Redistributable firmware (GPU/NIC blobs) is + # enabled in workstation.nix. hardware.cpu.intel.updateMicrocode = true; - hardware.enableRedistributableFirmware = true; # GPU note: the stock card varies between units -- ATI Radeon HD 2600 XT or # NVIDIA GeForce 8800 GT. Sway needs a working KMS/modesetting driver; do NOT diff --git a/system/machine/T400/README.md b/system/machine/T400/README.md index 6f2d1d7..3480f30 100644 --- a/system/machine/T400/README.md +++ b/system/machine/T400/README.md @@ -15,11 +15,11 @@ install time or swap in the generated UUIDs. `configuration.nix` imports exactly one boot module. Default is `boot-bios.nix`; switch by commenting it out and uncommenting the relevant alternative. -| Firmware | Module | Notes | -| --- | --- | --- | -| Stock Lenovo BIOS, or coreboot + **SeaBIOS** payload | `boot-bios.nix` | GRUB on the MBR. Set `device` to the real install disk (`/dev/sda` by default). MBR/legacy layout. | -| coreboot + **GRUB** payload | `boot-coreboot-grub.nix` | GRUB is config-only (`device = "nodev"`); NixOS does **not** write to a disk. Your coreboot `grub.cfg` (in the flash chip) must `search` for and `configfile` the on-disk `/boot/grub/grub.cfg`, or chainload the disk's GRUB. | -| coreboot + **Tianocore/edk2 (UEFI)** payload | `boot-coreboot-uefi.nix` | systemd-boot. `canTouchEfiVariables = true` (coreboot honours NVRAM writes). The module **declares its own ESP** (`/boot` vfat, label `ESP`) — when you regenerate `hardware-configuration.nix`, do **not** let it also define `/boot`. Create + label an `ESP` vfat partition (GPT). | +| Firmware | Module | Notes | +| ---------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Stock Lenovo BIOS, or coreboot + **SeaBIOS** payload | `boot-bios.nix` | GRUB on the MBR. Set `device` to the real install disk (`/dev/sda` by default). MBR/legacy layout. | +| coreboot + **GRUB** payload | `boot-coreboot-grub.nix` | GRUB is config-only (`device = "nodev"`); NixOS does **not** write to a disk. Your coreboot `grub.cfg` (in the flash chip) must `search` for and `configfile` the on-disk `/boot/grub/grub.cfg`, or chainload the disk's GRUB. | +| coreboot + **Tianocore/edk2 (UEFI)** payload | `boot-coreboot-uefi.nix` | systemd-boot. `canTouchEfiVariables = true` (coreboot honours NVRAM writes). The module **declares its own ESP** (`/boot` vfat, label `ESP`) — when you regenerate `hardware-configuration.nix`, do **not** let it also define `/boot`. Create + label an `ESP` vfat partition (GPT). | ## Graphics diff --git a/system/machine/T400/configuration.nix b/system/machine/T400/configuration.nix index 2ed5d13..361130c 100644 --- a/system/machine/T400/configuration.nix +++ b/system/machine/T400/configuration.nix @@ -18,25 +18,18 @@ console.font = "Lat2-Terminus16"; - services.pipewire = { - enable = true; - pulse.enable = true; - }; + # Low-RAM host (4 GiB max): a compressed RAM swap reduces disk paging. + zramSwap.enable = true; # This host accepts SSH, so open 22 (the firewall itself is enabled in # laptop.nix with a default-deny policy). services.openssh.enable = true; networking.firewall.allowedTCPPorts = [ 22 ]; - # The T400's fingerprint reader differs/may be absent; empty service still - # lets swaylock authenticate via password. - security.pam.services.swaylock = { }; - - # Intel Core 2 (Penryn) microcode + redistributable firmware. The latter also - # supplies the iwlwifi blobs (Intel WiFi Link 5100/5300) and the radeon - # firmware needed by the discrete GPU below. + # Intel Core 2 (Penryn) microcode. Redistributable firmware (enabled in + # workstation.nix) supplies the iwlwifi blobs (Intel WiFi Link 5100/5300) and + # the radeon firmware needed by the discrete GPU below. hardware.cpu.intel.updateMicrocode = true; - hardware.enableRedistributableFirmware = true; # This T400 has the optional discrete GPU fitted: an ATI Mobility Radeon HD # 3470 (RV620), driven by the open `radeon` KMS driver. Load it in the initrd diff --git a/system/modules/common-nixos.nix b/system/modules/common-nixos.nix index 476aa1e..fe87953 100644 --- a/system/modules/common-nixos.nix +++ b/system/modules/common-nixos.nix @@ -6,6 +6,13 @@ time.timeZone = "Europe/London"; i18n.defaultLocale = "en_GB.UTF-8"; + # Store hygiene. auto-optimise-store hard-links identical files in the store + # after each build (cheap dedupe; NOT a garbage collector -- there is + # deliberately no automatic GC timer). The larger download buffer avoids + # "buffer full" stalls when fetching big NARs over a fast link. + nix.settings.auto-optimise-store = true; + nix.settings.download-buffer-size = 134217728; # 128 MiB + # Minimal system-level CLI available before the home-manager profile loads # (e.g. early boot / rescue). User-level tooling lives in home-manager. environment.systemPackages = with pkgs; [ diff --git a/system/modules/workstation.nix b/system/modules/workstation.nix index 96da265..a8e52b1 100644 --- a/system/modules/workstation.nix +++ b/system/modules/workstation.nix @@ -14,4 +14,24 @@ # Default-deny inbound. Hosts that run a listening service open their own # ports next to where the service is enabled (e.g. sshd -> 22 on X1). networking.firewall.enable = true; + + # Disk hygiene for the physical hosts. fstrim reclaims unused SSD blocks on a + # weekly timer; cleanOnBoot wipes /tmp at every boot. + services.fstrim.enable = true; + boot.tmp.cleanOnBoot = true; + + # Audio. PipeWire with the PulseAudio shim covers every graphical host; no + # per-machine audio config is needed. + services.pipewire = { + enable = true; + pulse.enable = true; + }; + + # swaylock PAM stack. None of these machines has working fingerprint auth, so + # an empty service is enough -- swaylock falls back to password. + security.pam.services.swaylock = { }; + + # Redistributable firmware (GPU/Wi-Fi/NIC blobs) for the x86 hosts. Harmless + # on the Asahi MBP, which supplies its own peripheral firmware out-of-band. + hardware.enableRedistributableFirmware = true; } -- 2.52.0 From 63ca39253781842903a10874af4d51b69930a250 Mon Sep 17 00:00:00 2001 From: Emma Thorpe Date: Wed, 10 Jun 2026 14:57:21 +0100 Subject: [PATCH 3/3] chore(flake): treefmt + deadnix/statix + pre-commit; relocate work module - treefmt-nix drives `nix fmt` and the formatting check (nixfmt/shfmt/ prettier; generated files and flake.lock excluded). Replaces the bespoke find-based check. - deadnix and statix as flake checks and pre-commit hooks; deadnix ignores module-arg patterns, statix.toml disables the two house-style lints (repeated_keys, empty_pattern). Fixed the one real deadnix hit (unused overlay arg) and statix hit (use inherit for claude-code). - git-hooks.nix installs the pre-commit gate via the devShell. - .editorconfig for the base style. - Move system/modules/work/default.nix -> lyrathorpe/home/work.nix (it is a home-manager module). README gains a Development section; docs reformatted by the new formatter. Co-Authored-By: Claude Opus 4.8 (1M context) --- .editorconfig | 19 ++++ .renovaterc.json | 6 +- README.md | 39 +++++--- flake.lock | 89 +++++++++++++++++- flake.nix | 34 ++++--- lyrathorpe/home/KEYBINDINGS.md | 164 ++++++++++++++++----------------- lyrathorpe/home/work.nix | 2 + 7 files changed, 238 insertions(+), 115 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6b19e96 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{nix,yaml,yml,json,md,sh,toml}] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true + +# Markdown uses trailing whitespace for hard line breaks. +[*.md] +trim_trailing_whitespace = false diff --git a/.renovaterc.json b/.renovaterc.json index 5024f5f..5a18122 100644 --- a/.renovaterc.json +++ b/.renovaterc.json @@ -1,10 +1,6 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended", - ":dependencyDashboard", - ":semanticCommits" - ], + "extends": ["config:recommended", ":dependencyDashboard", ":semanticCommits"], "nix": { "enabled": true }, diff --git a/README.md b/README.md index 5616204..7ef1ec5 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,13 @@ single flake. Defined in the host table in [`flake.nix`](./flake.nix): -| Configuration | System | Machine | -| ------------------- | --------------- | ---------------------------------------- | -| `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) | -| `lyrathorpe-t400` | `x86_64-linux` | ThinkPad T400 — [install notes](./system/machine/T400/README.md) | +| Configuration | System | Machine | +| --------------------- | ---------------- | --------------------------------------------------------------------------- | +| `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) | +| `lyrathorpe-t400` | `x86_64-linux` | ThinkPad T400 — [install notes](./system/machine/T400/README.md) | | `lyrathorpe-macpro31` | `x86_64-linux` | Mac Pro 3,1, desktop — [install notes](./system/machine/MacPro31/README.md) | -| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) | -| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) | +| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) | +| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) | Shared layers: `lyrathorpe/home` (home-manager: shell, git, editor), `system/modules/common-nixos.nix` (all NixOS hosts), and @@ -48,14 +48,29 @@ WSL work box) keep plain TTY login. The target account needs a password ## MacBook (Asahi) firmware The MBP host references `system/modules/firmware/` for Apple peripheral -firmware (Wi-Fi/Bluetooth). Those blobs are **not** redistributable, so the -directory is gitignored and a clean checkout will not build `lyrathorpe-mbp` -until it is populated out-of-band. +firmware (Wi-Fi/Bluetooth). These blobs are **committed** (tracked) even though +`.gitignore` lists the directory: the flake is `git+file`, so it only sees +tracked files — untracking them breaks `lyrathorpe-mbp` evaluation (and the CI +host-eval) because the config can't find the firmware. They are not +redistributable; the repo is private. -Copy the firmware extracted during the Asahi install (from -`/etc/nixos/firmware` on the freshly-installed machine, or re-extract per the +To refresh them, copy the firmware extracted during the Asahi install (from +`/etc/nixos/firmware`, or re-extract per the [Asahi NixOS docs](https://github.com/tpwrules/nixos-apple-silicon)) into -`system/modules/firmware/` before rebuilding that host. +`system/modules/firmware/` and commit with `git add -f`. + +## Development + +A dev shell and a formatting/lint gate are wired through the flake: + +- `nix develop` — shell with `deadnix`, `statix`, `treefmt`, and the git + `pre-commit` hooks (installed automatically on first entry). +- `nix fmt` — formats the tree via `treefmt` (nixfmt + shfmt + prettier; + generated files and `flake.lock` are excluded). +- `nix flake check` — runs formatting, `deadnix`, `statix`, the pre-commit + hooks, and evaluates every host. `.editorconfig` carries the base style; + `statix.toml` disables the two house-style lints (`repeated_keys`, + `empty_pattern`). ## CI diff --git a/flake.lock b/flake.lock index 822ec07..73f2769 100644 --- a/flake.lock +++ b/flake.lock @@ -40,6 +40,22 @@ } }, "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { "locked": { "lastModified": 1761640442, "narHash": "sha256-AtrEP6Jmdvrqiv4x2xa5mrtaIp3OEe8uBYCDZDS+hu8=", @@ -54,7 +70,7 @@ "type": "github" } }, - "flake-compat_2": { + "flake-compat_3": { "flake": false, "locked": { "lastModified": 1767039857, @@ -90,6 +106,49 @@ "type": "github" } }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778507602, + "narHash": "sha256-kTwur1wV+01SdqskVMSo6JMEpg71ps3HpbFY2GsflKs=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "61ab0e80d9c7ab14c256b5b453d8b3fb0189ba0a", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -172,7 +231,7 @@ }, "nixos-apple-silicon": { "inputs": { - "flake-compat": "flake-compat", + "flake-compat": "flake-compat_2", "nixpkgs": [ "nixpkgs" ] @@ -193,7 +252,7 @@ }, "nixos-wsl": { "inputs": { - "flake-compat": "flake-compat_2", + "flake-compat": "flake-compat_3", "nixpkgs": [ "nixpkgs" ] @@ -248,6 +307,7 @@ "inputs": { "firefox-addons": "firefox-addons", "flake-parts": "flake-parts", + "git-hooks": "git-hooks", "home-manager": "home-manager", "nix-darwin": "nix-darwin", "nix-homebrew": "nix-homebrew", @@ -255,7 +315,28 @@ "nixos-apple-silicon": "nixos-apple-silicon", "nixos-wsl": "nixos-wsl", "nixpkgs": "nixpkgs", - "nixpkgs-unstable": "nixpkgs-unstable" + "nixpkgs-unstable": "nixpkgs-unstable", + "treefmt-nix": "treefmt-nix" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1780220602, + "narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "db947814a175b7ca6ded66e21383d938df01c227", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index c11803e..ed4a090 100644 --- a/flake.nix +++ b/flake.nix @@ -66,11 +66,13 @@ # claude-code tracks nixpkgs-unstable regardless of the pinned nixpkgs. overlays = [ (_final: prev: { - claude-code = + inherit (import nixpkgs-unstable { inherit (prev.stdenv.hostPlatform) system; config.allowUnfree = true; - }).claude-code; + }) + claude-code + ; }) ]; @@ -309,20 +311,28 @@ programs.shfmt.enable = true; programs.prettier.enable = true; # Generated hardware-configuration.nix files are not hand-edited. - settings.global.excludes = [ "*/hardware-configuration.nix" ]; + settings.global.excludes = [ + "*/hardware-configuration.nix" # generated by nixos-generate-config + "flake.lock" # generated by `nix flake lock` + ]; }; # Pre-commit hooks: format + lint gate run on commit. The same hooks # are exposed as a flake check (pre-commit.check.enable defaults true). - pre-commit.settings.hooks = { - nixfmt-rfc-style.enable = true; - deadnix = { - enable = true; - # Unused module args ({config,lib,pkgs,...}) are normal; only flag - # genuinely dead bindings. - settings.noLambdaPatternNames = true; + pre-commit.settings = { + # Generated by nixos-generate-config; don't lint/reformat (treefmt + # excludes them too). + excludes = [ "hardware-configuration\\.nix$" ]; + hooks = { + nixfmt-rfc-style.enable = true; + deadnix = { + enable = true; + # Unused module args ({config,lib,pkgs,...}) are normal; only + # flag genuinely dead bindings. + settings.noLambdaPatternNames = true; + }; + statix.enable = true; # reads statix.toml (repeated_keys/empty_pattern disabled) }; - statix.enable = true; # reads .statix.toml (repeated_keys/empty_pattern disabled) }; # treefmt-nix exposes its own `checks.treefmt`; alias it to @@ -336,7 +346,7 @@ deadnix --fail --no-lambda-pattern-names ${./.} && touch $out ''; checks.statix = pkgs.runCommandLocal "check-statix" { nativeBuildInputs = [ pkgs.statix ]; } '' - statix check ${./.} && touch $out + statix check -c ${./.} ${./.} && touch $out ''; # `nix develop` shell with the tooling needed to hack on this flake. diff --git a/lyrathorpe/home/KEYBINDINGS.md b/lyrathorpe/home/KEYBINDINGS.md index 2382edc..b768d01 100644 --- a/lyrathorpe/home/KEYBINDINGS.md +++ b/lyrathorpe/home/KEYBINDINGS.md @@ -4,12 +4,12 @@ Every keyboard shortcut configured across this desktop, and where it is defined. Everything here is managed declaratively through Nix — edit the listed file and rebuild, never the generated dotfiles. -| Area | Defined in | -| --- | --- | +| Area | Defined in | +| ----------------- | --------------------------------------------------------------------------------------------------------------------- | | Sway (compositor) | [`sway.nix`](./sway.nix) `config.keybindings` + `config.modes`, plus the home-manager Sway module's built-in defaults | -| tmux | [`shell.nix`](./shell.nix) `programs.tmux` | -| zsh line editor | [`shell.nix`](./shell.nix) `programs.zsh.historySubstringSearch` | -| foot (terminal) | foot package defaults — only colours are themed (in `sway.nix`) | +| tmux | [`shell.nix`](./shell.nix) `programs.tmux` | +| zsh line editor | [`shell.nix`](./shell.nix) `programs.zsh.historySubstringSearch` | +| foot (terminal) | foot package defaults — only colours are themed (in `sway.nix`) | **Conventions** @@ -26,49 +26,49 @@ rebuild, never the generated dotfiles. ### Applications & session -| Shortcut | Action | -| --- | --- | -| `Super`+`Return` | Open a terminal (foot) | -| `Super`+`Space` | App launcher (sway-launcher-desktop in a floating foot) | -| `Super`+`d` | App launcher (same as above; module default) | -| `Super`+`e` | File manager (nemo) | -| `Super`+`c` | Clipboard history picker (clipman → fuzzel) | -| `Super`+`l` | Lock screen (swaylock) | -| `Super`+`Shift`+`q` | Close the focused window | -| `Super`+`Shift`+`c` | Reload the Sway config | -| `Super`+`Shift`+`e` | Exit Sway (asks for confirmation) | +| Shortcut | Action | +| ------------------- | ------------------------------------------------------- | +| `Super`+`Return` | Open a terminal (foot) | +| `Super`+`Space` | App launcher (sway-launcher-desktop in a floating foot) | +| `Super`+`d` | App launcher (same as above; module default) | +| `Super`+`e` | File manager (nemo) | +| `Super`+`c` | Clipboard history picker (clipman → fuzzel) | +| `Super`+`l` | Lock screen (swaylock) | +| `Super`+`Shift`+`q` | Close the focused window | +| `Super`+`Shift`+`c` | Reload the Sway config | +| `Super`+`Shift`+`e` | Exit Sway (asks for confirmation) | ### Focus -| Shortcut | Action | -| --- | --- | -| `Super`+`←`/`↓`/`↑`/`→` | Move focus by direction | -| `Super`+`h`/`j`/`k` | Move focus left / down / up (vim-style) | -| `Super`+`a` | Focus the parent container | -| `Super`+`Alt`+`Space` | Toggle focus between tiling and floating | +| Shortcut | Action | +| ----------------------- | ---------------------------------------- | +| `Super`+`←`/`↓`/`↑`/`→` | Move focus by direction | +| `Super`+`h`/`j`/`k` | Move focus left / down / up (vim-style) | +| `Super`+`a` | Focus the parent container | +| `Super`+`Alt`+`Space` | Toggle focus between tiling and floating | > Note: vim focus-right would be `Super`+`l`, but that is bound to **lock** here; > use `Super`+`→`. ### Moving windows -| Shortcut | Action | -| --- | --- | -| `Super`+`Shift`+`←`/`↓`/`↑`/`→` | Move the window by direction | +| Shortcut | Action | +| ------------------------------- | ---------------------------------------- | +| `Super`+`Shift`+`←`/`↓`/`↑`/`→` | Move the window by direction | | `Super`+`Shift`+`h`/`j`/`k`/`l` | Move the window left / down / up / right | -| `Super`+`Shift`+`Space` | Toggle the window floating | +| `Super`+`Shift`+`Space` | Toggle the window floating | Mouse (with `Super` held): left-drag moves a window, right-drag resizes it. ### Layout -| Shortcut | Action | -| --- | --- | -| `Super`+`b` | Split horizontally | -| `Super`+`v` | Split vertically | -| `Super`+`s` | Stacking layout | -| `Super`+`w` | Tabbed layout | -| `Super`+`f` | Toggle fullscreen | +| Shortcut | Action | +| ----------- | -------------------------------------------------------------------------------------- | +| `Super`+`b` | Split horizontally | +| `Super`+`v` | Split vertically | +| `Super`+`s` | Stacking layout | +| `Super`+`w` | Tabbed layout | +| `Super`+`f` | Toggle fullscreen | | `Super`+`y` | **Layout submenu**: `s` stacking · `w` tabbed · `e` toggle split · `Return`/`Esc` exit | > The layout submenu's `e` (toggle split) is the home for that action since @@ -76,49 +76,49 @@ Mouse (with `Super` held): left-drag moves a window, right-drag resizes it. ### Workspaces -| Shortcut | Action | -| --- | --- | -| `Super`+`1`…`0` | Switch to workspace 1…10 | +| Shortcut | Action | +| ----------------------- | --------------------------------- | +| `Super`+`1`…`0` | Switch to workspace 1…10 | | `Super`+`Shift`+`1`…`0` | Move the window to workspace 1…10 | -| `Super`+`z` | Previous workspace | -| `Super`+`x` | Next workspace | +| `Super`+`z` | Previous workspace | +| `Super`+`x` | Next workspace | ### Scratchpad -| Shortcut | Action | -| --- | --- | +| Shortcut | Action | +| ------------------- | --------------------------------- | | `Super`+`Shift`+`-` | Move the window to the scratchpad | -| `Super`+`-` | Show / cycle the scratchpad | +| `Super`+`-` | Show / cycle the scratchpad | ### Modes (submenus) -| Shortcut | Action | -| --- | --- | -| `Super`+`r` | **Resize mode**: arrow keys resize; `Return`/`Esc` exit | -| `Super`+`y` | **Layout mode** (see Layout above) | +| Shortcut | Action | +| ------------------- | ------------------------------------------------------------------------------------------------------------ | +| `Super`+`r` | **Resize mode**: arrow keys resize; `Return`/`Esc` exit | +| `Super`+`y` | **Layout mode** (see Layout above) | | `Super`+`Shift`+`x` | **Power menu**: `l` lock · `e` log out · `s` sleep · `r` reboot · `Shift`+`s` shutdown · `Return`/`Esc` exit | ### Screenshots -| Shortcut | Action | -| --- | --- | -| `Print` | Select a region → swappy (annotate/save) | -| `Shift`+`Print` | Focused window → swappy | +| Shortcut | Action | +| --------------- | ---------------------------------------- | +| `Print` | Select a region → swappy (annotate/save) | +| `Shift`+`Print` | Focused window → swappy | ### Audio & media -| Shortcut | Action | -| --- | --- | -| `XF86AudioRaiseVolume` / `XF86AudioLowerVolume` | Volume ±5% (wpctl) | -| `XF86AudioMute` | Toggle output mute | -| `XF86AudioMicMute` | Toggle microphone mute | -| `XF86AudioPlay` | Play/pause (playerctl) | -| `XF86AudioNext` / `XF86AudioPrev` | Next / previous track | +| Shortcut | Action | +| ----------------------------------------------- | ---------------------- | +| `XF86AudioRaiseVolume` / `XF86AudioLowerVolume` | Volume ±5% (wpctl) | +| `XF86AudioMute` | Toggle output mute | +| `XF86AudioMicMute` | Toggle microphone mute | +| `XF86AudioPlay` | Play/pause (playerctl) | +| `XF86AudioNext` / `XF86AudioPrev` | Next / previous track | ### Brightness — laptops only -| Shortcut | Action | -| --- | --- | +| Shortcut | Action | +| ----------------------------------------------- | ----------------------------- | | `XF86MonBrightnessUp` / `XF86MonBrightnessDown` | Backlight ±5% (brightnessctl) | Present only on portable hosts (T400, MBP); desktops have no internal backlight. @@ -129,19 +129,19 @@ Present only on portable hosts (T400, MBP); desktops have no internal backlight. Prefix is **`Ctrl`+`b`** (default). Copy mode uses **vi** keys. -| Shortcut | Action | -| --- | --- | -| `Ctrl`+`b` then `v` | Split into left/right panes | -| `Ctrl`+`b` then `s` | Split into top/bottom panes | -| `Ctrl`+`h`/`j`/`k`/`l` | Move between panes — and into/out of vim splits — seamlessly (vim-tmux-navigator, no prefix) | -| `Alt`+`←`/`→`/`↑`/`↓` | Switch pane by direction (no prefix needed) | -| `Ctrl`+`b` then `[` | Enter copy mode (then vi motions; `Space`/`Enter` to select/copy) | -| `Ctrl`+`b` then `z` | Zoom / unzoom the focused pane | -| `Ctrl`+`b` then `c` | New window | -| `Ctrl`+`b` then `n` / `p` | Next / previous window | -| `Ctrl`+`b` then `d` | Detach | -| `Ctrl`+`b` then `Ctrl`+`s` / `Ctrl`+`r` | Save / restore the session (resurrect; continuum also auto-saves and restores on start) | -| Mouse | Enabled — click to focus, drag borders, scroll, select | +| Shortcut | Action | +| --------------------------------------- | -------------------------------------------------------------------------------------------- | +| `Ctrl`+`b` then `v` | Split into left/right panes | +| `Ctrl`+`b` then `s` | Split into top/bottom panes | +| `Ctrl`+`h`/`j`/`k`/`l` | Move between panes — and into/out of vim splits — seamlessly (vim-tmux-navigator, no prefix) | +| `Alt`+`←`/`→`/`↑`/`↓` | Switch pane by direction (no prefix needed) | +| `Ctrl`+`b` then `[` | Enter copy mode (then vi motions; `Space`/`Enter` to select/copy) | +| `Ctrl`+`b` then `z` | Zoom / unzoom the focused pane | +| `Ctrl`+`b` then `c` | New window | +| `Ctrl`+`b` then `n` / `p` | Next / previous window | +| `Ctrl`+`b` then `d` | Detach | +| `Ctrl`+`b` then `Ctrl`+`s` / `Ctrl`+`r` | Save / restore the session (resurrect; continuum also auto-saves and restores on start) | +| Mouse | Enabled — click to focus, drag borders, scroll, select | > The stock split keys `%` and `"` are unbound; use `v` / `s` above. `Ctrl`+`b` > then `s` is therefore a split, not the session tree. @@ -155,22 +155,22 @@ Prefix is **`Ctrl`+`b`** (default). Copy mode uses **vi** keys. Only colours are themed; these are foot's default key bindings. -| Shortcut | Action | -| --- | --- | -| `Ctrl`+`Shift`+`c` / `Ctrl`+`Shift`+`v` | Copy / paste (clipboard) | -| `Shift`+`Insert` | Paste primary selection | -| `Ctrl`+`Shift`+`r` | Search scrollback | -| `Ctrl`+`+` / `Ctrl`+`-` / `Ctrl`+`0` | Font larger / smaller / reset | -| `Ctrl`+`Shift`+`u` | URL mode (jump to/open links) | -| `Ctrl`+`Shift`+`n` | Spawn a new terminal | -| `Shift`+`PageUp` / `Shift`+`PageDown` | Scroll back / forward | +| Shortcut | Action | +| --------------------------------------- | ----------------------------- | +| `Ctrl`+`Shift`+`c` / `Ctrl`+`Shift`+`v` | Copy / paste (clipboard) | +| `Shift`+`Insert` | Paste primary selection | +| `Ctrl`+`Shift`+`r` | Search scrollback | +| `Ctrl`+`+` / `Ctrl`+`-` / `Ctrl`+`0` | Font larger / smaller / reset | +| `Ctrl`+`Shift`+`u` | URL mode (jump to/open links) | +| `Ctrl`+`Shift`+`n` | Spawn a new terminal | +| `Shift`+`PageUp` / `Shift`+`PageDown` | Scroll back / forward | --- ## zsh -| Shortcut | Action | -| --- | --- | +| Shortcut | Action | +| --------- | -------------------------------------------------------------------------------------------------- | | `↑` / `↓` | History **substring** search — type a fragment first, then the arrows cycle matching past commands | Bound for both CSI and SS3 cursor sequences, so it works in foot, iTerm2 and diff --git a/lyrathorpe/home/work.nix b/lyrathorpe/home/work.nix index 854ee9c..5314e51 100644 --- a/lyrathorpe/home/work.nix +++ b/lyrathorpe/home/work.nix @@ -1,3 +1,5 @@ +# Home-manager module for the work (EDaaS/WSL) profile: corporate git signing, +# work toolchain packages and tmux tweaks. Imported only by the work host. { pkgs, lib, ... }: { -- 2.52.0