Compare commits

..

4 Commits

Author SHA1 Message Date
Emma Thorpe 1c15c55605 feat(fonts): JetBrains Mono Nerd Font on every host
The tmux statusline draws powerline/Nerd glyphs that default fonts lack,
so they render as blank/"?". tmux runs on every host (not just the Sway
ones), so install the font in the shared common-nixos module rather than
swaywm -- a future console-only or non-Sway host gets it too. The Mac
installs it via the Darwin config (/Library/Fonts). foot names it as its
main font (home/sway.nix).

On macOS, iTerm2's font is still a GUI setting: Settings -> Profiles ->
Text -> Font -> "JetBrainsMono Nerd Font".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:10:41 +01:00
Emma Thorpe a0dcb258c9 fix(tmux): use tmux-256color (not tmux-direct); add NO_TMUX hatch
tmux-direct as default-terminal desyncs zsh's line redraw on some
terminals (iTerm2: duplicated characters on Tab, stray newlines). Switch
to the standard tmux-256color and advertise truecolor per outer terminal
via terminal-features (add xterm-256color:RGB alongside the foot ones).

Also add a NO_TMUX escape hatch to the auto-start guard, so
`NO_TMUX=1 <terminal>` opens a bare shell.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:04:45 +01:00
Emma Thorpe 19792c9390 fix(nh): drop the automatic GC timer; keep nh for rebuilds
The scheduled `nh clean` only reclaimed disk and risked reaping store
paths the current generation still references (notably on nix-darwin).
Keep `programs.nh` (nicer rebuilds + $NH_FLAKE) but remove clean.enable;
GC manually (`nh clean all` / `nix-collect-garbage -d`) when nothing
important is running. The resetZcompdump activation stays as a safety net
for stale completion dumps across rebuilds/manual GC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 13:51:05 +01:00
Emma Thorpe ac1c04d157 docs: document the interactive shell environment
Add lyrathorpe/home/README.md covering the zsh / CLI tools / tmux / git /
ssh features and nice-to-haves configured across shell.nix and git.nix
(history, fzf/zoxide/direnv/eza/bat, nix-index, nh, tmux plugins +
auto-start, git aliases/settings/signing, ssh agent + Gitea host, the
zcompdump/GC maintenance behaviours, and per-host differences). Link it
from the top-level README alongside the keybindings reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 13:25:25 +01:00
6 changed files with 164 additions and 14 deletions
+5 -3
View File
@@ -28,10 +28,12 @@ sudo nixos-rebuild switch --flake .#<configuration>
darwin-rebuild switch --flake .#lyrathorpe-mac darwin-rebuild switch --flake .#lyrathorpe-mac
``` ```
## Keybindings ## Shell environment & keybindings
All Sway / tmux / foot / zsh keyboard shortcuts are documented in - Interactive shell features (zsh, tmux, git, ssh, CLI tools, auto-tmux):
[`lyrathorpe/home/KEYBINDINGS.md`](./lyrathorpe/home/KEYBINDINGS.md). [`lyrathorpe/home/README.md`](./lyrathorpe/home/README.md).
- All Sway / tmux / foot / zsh keyboard shortcuts:
[`lyrathorpe/home/KEYBINDINGS.md`](./lyrathorpe/home/KEYBINDINGS.md).
## Login / greeter ## Login / greeter
+125
View File
@@ -0,0 +1,125 @@
# Interactive shell environment
Everything the shell, terminal multiplexer, git and ssh do beyond their defaults,
and where each is defined. All of it is managed declaratively through
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) |
| 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
[`../../system/modules/work/default.nix`](../../system/modules/work/default.nix)
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** |
| 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 |
**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 <fragment>` 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; weekly user-GC timer (`--keep 5 --keep-since 3d`) |
## tmux
**Auto-start:** opening any interactive terminal — foot, iTerm2, the WSL shell, the
Linux console — drops you straight into a tmux session named `main` (attach if it
exists, else create). Panes run a plain non-login zsh. It deliberately does **not**
fire for SSH sessions, VS Code's integrated terminal, already-inside-tmux, or
non-interactive shells.
| 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) |
| 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`
(sessions auto-save and restore across reboots).
## git
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 <sub>` (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. |
## 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 |
| 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.
## Maintenance behaviours
- **zcompdump reset** — `~/.zcompdump*` is removed on every activation, so a stale
dump (pointing at `/nix/store` paths a rebuild or the weekly GC removed) can't
break completion with `_git: function definition file not found`.
- **Weekly GC** — `nh clean` runs weekly as a user timer, keeping the last 5
generations and anything newer than 3 days.
## 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 |
+20 -11
View File
@@ -66,9 +66,11 @@
# not SSH -> don't force inbound SSH logins into a server tmux # not SSH -> don't force inbound SSH logins into a server tmux
# not VS Code -> its integrated terminal manages itself # not VS Code -> its integrated terminal manages itself
# tmux on PATH -> a failed exec would otherwise kill the login shell # tmux on PATH -> a failed exec would otherwise kill the login shell
# $NO_TMUX unset -> escape hatch: `NO_TMUX=1 <term>` opens a bare shell
(lib.mkOrder 200 '' (lib.mkOrder 200 ''
if [[ $- == *i* ]] \ if [[ $- == *i* ]] \
&& [[ -z "$TMUX" ]] \ && [[ -z "$TMUX" ]] \
&& [[ -z "$NO_TMUX" ]] \
&& [[ -z "$SSH_CONNECTION" && -z "$SSH_TTY" ]] \ && [[ -z "$SSH_CONNECTION" && -z "$SSH_TTY" ]] \
&& [[ "$TERM_PROGRAM" != "vscode" ]] \ && [[ "$TERM_PROGRAM" != "vscode" ]] \
&& command -v tmux >/dev/null 2>&1; then && command -v tmux >/dev/null 2>&1; then
@@ -126,21 +128,24 @@
programs.nix-index.enable = true; programs.nix-index.enable = true;
programs.nix-index-database.comma.enable = true; programs.nix-index-database.comma.enable = true;
# Nicer nixos-rebuild/home-manager (diffs) + a weekly user-GC timer. # Nicer nixos-rebuild/home-manager (diffs) + $NH_FLAKE. No automatic clean:
# the scheduled GC's only benefit is reclaiming disk, but it can reap store
# paths the current generation still references (notably on nix-darwin, where
# it broke completion by removing an in-use oh-my-zsh). GC manually instead:
# `nh clean all` / `nix-collect-garbage -d` when nothing important is running.
programs.nh = { programs.nh = {
enable = true; enable = true;
flake = "$HOME/code/nixfiles"; flake = "$HOME/code/nixfiles";
clean = {
enable = true;
dates = "weekly";
extraArgs = "--keep 5 --keep-since 3d";
};
}; };
programs.tmux = { programs.tmux = {
enable = true; enable = true;
reverseSplit = true; reverseSplit = true;
terminal = "tmux-direct"; # tmux-256color (not tmux-direct): the standard inside-tmux terminfo.
# tmux-direct's capabilities desync zsh's line redraw on some terminals
# (e.g. iTerm2 -> duplicated chars on Tab, stray newlines). Truecolor is
# advertised per outer terminal via the RGB terminal-features below.
terminal = "tmux-256color";
newSession = true; newSession = true;
keyMode = "vi"; keyMode = "vi";
historyLimit = 500000; historyLimit = 500000;
@@ -188,6 +193,10 @@
bind -n M-Up select-pane -U bind -n M-Up select-pane -U
bind -n M-Down select-pane -D bind -n M-Down select-pane -D
# Truecolor for the outer terminals (foot reports xterm-ish too; iTerm2 is
# xterm-256color). Without this, with tmux-256color as default-terminal,
# 24-bit colour would be quantised to 256.
set -as terminal-features ",xterm-256color:RGB"
# Tell tmux which capabilities the foot terminal supports, so truecolor, # Tell tmux which capabilities the foot terminal supports, so truecolor,
# synchronised output, the system clipboard (OSC 52), window titles and # synchronised output, the system clipboard (OSC 52), window titles and
# cursor styling all pass through. # cursor styling all pass through.
@@ -260,10 +269,10 @@
services.ssh-agent.enable = lib.mkIf pkgs.stdenv.hostPlatform.isLinux true; services.ssh-agent.enable = lib.mkIf pkgs.stdenv.hostPlatform.isLinux true;
# Drop the zsh completion dump on every activation. A stale ~/.zcompdump # Drop the zsh completion dump on every activation. A stale ~/.zcompdump
# caches /nix/store paths to completion functions; once a rebuild or GC (the # caches /nix/store paths to completion functions; once a rebuild or a manual
# weekly nh clean) removes them, compinit fails with "_git: function # GC removes them, compinit fails with "_git: function definition file not
# definition file not found" for every completion. Deleting it forces a fresh # found" for every completion. Deleting it forces a fresh rebuild from the
# rebuild from the current fpath on the next shell. # current fpath on the next shell.
home.activation.resetZcompdump = lib.hm.dag.entryAfter [ "writeBoundary" ] '' home.activation.resetZcompdump = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
$DRY_RUN_CMD rm -f "$HOME"/.zcompdump* "''${XDG_CACHE_HOME:-$HOME/.cache}"/zsh/.zcompdump* 2>/dev/null || true $DRY_RUN_CMD rm -f "$HOME"/.zcompdump* "''${XDG_CACHE_HOME:-$HOME/.cache}"/zsh/.zcompdump* 2>/dev/null || true
''; '';
+3
View File
@@ -237,6 +237,9 @@ in
# text). Only colors-dark is needed; we never set initial-color-theme=light. # text). Only colors-dark is needed; we never set initial-color-theme=light.
settings = { settings = {
main = { main = {
# Nerd Font: monospace plus the powerline/Nerd glyphs the tmux
# statusline uses (otherwise they render as blank/"?").
font = "JetBrainsMono Nerd Font:size=11";
# Advertise as xterm-256color so remote hosts without foot's terminfo # Advertise as xterm-256color so remote hosts without foot's terminfo
# still behave (tmux re-adds foot's RGB/sync/etc. features -- see # still behave (tmux re-adds foot's RGB/sync/etc. features -- see
# shell.nix). The [main] section is the unheadered top of foot.ini. # shell.nix). The [main] section is the unheadered top of foot.ini.
+5
View File
@@ -6,6 +6,11 @@
{ {
programs.zsh.enable = true; programs.zsh.enable = true;
# Install the Nerd Font into /Library/Fonts so iTerm2 can use it (set it in
# iTerm2 -> Settings -> Profiles -> Text -> Font: "JetBrainsMono Nerd Font").
# Provides the powerline/Nerd glyphs the tmux statusline draws.
fonts.packages = [ pkgs.nerd-fonts.jetbrains-mono ];
# CLI tooling sourced from nixpkgs instead of Homebrew formulae. Pure library # CLI tooling sourced from nixpkgs instead of Homebrew formulae. Pure library
# dependencies are omitted; nix pulls them into closures automatically. # dependencies are omitted; nix pulls them into closures automatically.
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
+6
View File
@@ -12,4 +12,10 @@
git git
fastfetch fastfetch
]; ];
# Terminal font with powerline/Nerd glyphs. Installed on every host because
# the tmux statusline (which uses these glyphs) runs everywhere, not just on
# the Sway/graphical hosts. foot names it explicitly (home/sway.nix); the Mac
# installs it via the Darwin config.
fonts.packages = [ pkgs.nerd-fonts.jetbrains-mono ];
} }