From 59d56224424dd6fc390f7c8093377482ea35a33e Mon Sep 17 00:00:00 2001 From: Jonny Barnes Date: Sun, 9 Aug 2026 11:37:24 +0100 Subject: [PATCH 1/2] Remove dark-mode-notify, document the native approach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The launchd agent ran `pkill -usr1 zsh` on every appearance change, which made every shell re-source .zshrc. That could never have achieved its goal: the tmux status bar belongs to the tmux server and colours to a running nvim, so re-sourcing a shell rc cannot retheme either. The one variable it set, MACOS_APPEARANCE, was read by nothing. Its only real effect was firing on each unlock — 5253 times on this machine, 3437 on the iMac — which is what drove the ~/.gitconfig.lock contention fixed in 583bbaa. The agent and plist are removed from both machines; this drops the script. ghostty, bat, delta and nvim all handle appearance natively, and tmux 3.6+ detects it over OSC 2031 (#{client_theme} already reports correctly). Wiring the tmux client-light-theme/client-dark-theme hooks is left as the next step. Also refreshes the stale parts of the README: the intro still explained ~/.gitconfig as a copy because .extra edited it, and the .extra example still showed the `git config --global` calls that caused the lock errors, plus the misspelled GIT_COMMITER_* vars. Following it would have reinstated the bug. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 110 +++++++++++++++++++++++---------------- zsh/dark-mode-notify.zsh | 23 -------- 2 files changed, 64 insertions(+), 69 deletions(-) delete mode 100755 zsh/dark-mode-notify.zsh diff --git a/README.md b/README.md index 5adbb7c..4b2f8f0 100644 --- a/README.md +++ b/README.md @@ -4,65 +4,83 @@ Here’s my dotfiles, inspired by people like Mathias. See his dotfiles at [`https://github.com/mathias/dotfiles`](https://github.com/mathias/dotfiles). The idea I’m currently going down is to create a symlink from `$HOME` to this -directory. There is one exception to this, the `.gitconfig` file. I don’t want actual commiter details committed into this repo. So my git credentials are -stored in a `.extra` file which gets sourced. This then calls the relavent git -command, which causes git to edit `$HOME/.gitconfig`. If that file was a symlink -to this repo, then the repo would see the file as edited and the repo would then -be in a dirty state, permanently. +directory. There is one exception to this, the `.gitconfig` file. I don’t want +actual commiter details committed into this repo, and they differ per machine +anyway — this Mac signs with my work address, the iMac with my personal one. + +So identity and signing live in an untracked `$HOME/.gitconfig.local`, which the +tracked `gitconfig` pulls in with an `[include]` as its **last** directive. Last +matters: git applies config in file order, so anything after the include would +override it. + +`$HOME/.gitconfig` is copied rather than symlinked, so that an ad-hoc +`git config --global` writes into `$HOME` instead of dirtying this repo. The +trade-off is that `git pull` alone does not update it — re-run `./bootstrap.sh`, +or `cp gitconfig ~/.gitconfig`, after changing the tracked copy. ## Usage First clone the repo. -As mentioned above my git credentials are stored in an untracked file: -`$HOME/.extra`. This must be manually created, mine looks something like: - -``` -# Git credentials -GIT_AUTHOR_NAME="Jonny Barnes" -GIT_COMMITER_NAME="$GIT_AUTHOR_NAME" -git config --global user.name "$GIT_AUTHOR_NAME" -GIT_AUTHOR_EMAIL="jonny@jonnybarnes.uk" -GIT_COMMITER_EMAIL="$GIT_AUTHOR_EMAIL" -git config --global user.email "$GIT_AUTHOR_EMAIL" -``` - -Run `./boostrap.sh`, this will create all the necessary symlinks, then source +Run `./bootstrap.sh`, this will create all the necessary symlinks, then source `.zshrc`. > [!WARNING] > This is a **destructive** process, so backup your dotfiles first. -## Auto switch between light and dark mode +As mentioned above, git identity lives in an untracked `$HOME/.gitconfig.local`. +`bootstrap.sh` seeds it from `gitconfig.local.template` if it does not already +exist, and never overwrites an existing one. Fill it in: -To switch between dark and light mode automatically, we use a helper lib. +``` +[user] + name = Jonny Barnes + email = jonny@jonnybarnes.uk + signingkey = ssh-ed25519 AAAA... -Clone it from `https://github.com/bouk/dark-mode-notify` then run `make` and `make install`. +[commit] + gpgsign = true -Then make a script to run is automatically, put the plist where your system can find it: `~/Library/LaunchAgents/ke.bou.dark-mode-notify.plist` with the following content: - -```xml - - - - - Label - ke.bou.dark-mode-notify - KeepAlive - - StandardErrorPath - /Users/jonny/.local/var/log/dark-mode-notify-stderr.log - StandardOutPath - /Users/jonny/.local/var/log/dark-mode-notify-stdout.log - ProgramArguments - - /usr/local/bin/dark-mode-notify - /Users/jonny/git/dotfiles/zsh/dark-mode-notify.zsh - - - +[gpg "ssh"] + program = /Applications/1Password.app/Contents/MacOS/op-ssh-sign ``` -Make the log file and script file point to the right location. For clarity `dark-mode-notify.zsh` is in this repo. +Do not skip this. Git will not prompt you — with no identity it quietly derives +one from your username and hostname, warns once, and signs nothing. + +`$HOME/.extra` is a separate untracked file for other machine-local environment +variables, sourced from `.zshrc`. Keep git out of it: + +> [!IMPORTANT] +> Never put `git config --global` in `.extra`. It is re-sourced on every +> `SIGUSR1`, so with several tmux panes the concurrent writes race on +> `~/.gitconfig.lock` and spew `error: could not lock config file`. Put git +> settings in `.gitconfig.local` instead. + +## Light and dark mode + +Most of this is now handled natively and needs no configuration: + +- **ghostty** follows the system appearance itself via + `theme = light:tangere-light.conf,dark:tangere-dark.conf`. +- **tmux** 3.6+ learns the terminal’s theme over OSC 2031 and exposes it as + `#{client_theme}`. +- **bat** picks a theme per invocation from `BAT_THEME_LIGHT` / `BAT_THEME_DARK`. +- **delta** and **nvim** detect the terminal background themselves. + +Still outstanding: the tmux status bar is hardcoded to dark-tuned colours, so it +is unreadable in light mode. tmux has `client-light-theme` and +`client-dark-theme` hooks for exactly this — not yet wired up. + +Both machines currently run pinned Dark, so the delta and nvim behaviour above is +documented-but-untested here. Worth re-checking when Auto goes back on. + +> [!NOTE] +> This previously used [`dark-mode-notify`](https://github.com/bouk/dark-mode-notify) +> as a `launchd` agent that ran `pkill -usr1 zsh` on every appearance change. +> That has been removed. It could never have worked: re-sourcing `.zshrc` runs +> inside each *shell*, but the status bar belongs to the tmux *server* and +> colours to a running nvim, so it could not retheme either. The one variable it +> set was read by nothing. Meanwhile it fired on every unlock, re-running +> `.zshrc` in every pane. Don’t bring it back. diff --git a/zsh/dark-mode-notify.zsh b/zsh/dark-mode-notify.zsh deleted file mode 100755 index fa52ade..0000000 --- a/zsh/dark-mode-notify.zsh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env zsh - -function switch-dark-mode() -{ - local darkMode=true; - - if [[ $(defaults read -g AppleInterfaceStyle 2> /dev/null) != 'Dark' ]]; then - darkMode=false - fi - - if [[ $darkMode == true ]]; then - echo "Switched to dark mode" - export MACOS_APPEARANCE="dark" - else - echo "Switched to light mode" - export MACOS_APPEARANCE="light" - fi - - # Reload zshrc - pkill -usr1 zsh -} - -switch-dark-mode From 6b3ab420ad6e4ffbd12ae8f6c0b4d95488d941c9 Mon Sep 17 00:00:00 2001 From: Jonny Barnes Date: Sun, 9 Aug 2026 12:38:16 +0100 Subject: [PATCH 2/2] Follow light/dark appearance in the tmux status bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The status bar was five hardcoded tangere-dark colours, so in light mode the pale #fdfdd9 text landed on a white background and became unreadable. That was the real reason Auto appearance "never worked with the terminal setup" — ghostty was switching correctly all along. Split the colours into tmux-light.conf and tmux-dark.conf. The dark file is extracted byte-exact from the previous config; the light file is the same layout with each colour swapped for the tangere-light palette entry at the same index, so roles are preserved (text on an accent uses the theme background, which inverts from #1a2938 to #fdfdfa). tmux 3.6+ learns the terminal's theme over OSC 2031, so this keys off the appearance change itself rather than a schedule, and behaves identically whether that came from Auto at sunrise/sunset or a manual toggle. Three hooks: the two theme hooks react to a change, and client-attached covers a client attaching mid-way, since those two only fire on a change. Sourcing is idempotent so the overlap is harmless. This replaces the dark-mode-notify launchd agent removed in 59d5622, which could not have worked: it re-sourced .zshrc inside each shell, but the status bar belongs to the tmux server. Verified by round trip: flipping to Light switched #{client_theme} and applied the light palette, and returning to Dark restored it. A running nvim does not follow a live change (docs: the TUI sets 'background' on startup); noted in the README. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 22 +++++++++++++++++----- bootstrap.sh | 2 ++ tmux | 23 +++++++++++------------ tmux-dark.conf | 21 +++++++++++++++++++++ tmux-light.conf | 22 ++++++++++++++++++++++ 5 files changed, 73 insertions(+), 17 deletions(-) create mode 100644 tmux-dark.conf create mode 100644 tmux-light.conf diff --git a/README.md b/README.md index 4b2f8f0..ff64126 100644 --- a/README.md +++ b/README.md @@ -68,12 +68,24 @@ Most of this is now handled natively and needs no configuration: - **bat** picks a theme per invocation from `BAT_THEME_LIGHT` / `BAT_THEME_DARK`. - **delta** and **nvim** detect the terminal background themselves. -Still outstanding: the tmux status bar is hardcoded to dark-tuned colours, so it -is unreadable in light mode. tmux has `client-light-theme` and -`client-dark-theme` hooks for exactly this — not yet wired up. +The tmux status bar needs help, because its colours are set explicitly. They live +in `tmux-light.conf` and `tmux-dark.conf` — the same layout, with each colour +taken from the matching tangere palette index — and `tmux` sources one of them +from three hooks: -Both machines currently run pinned Dark, so the delta and nvim behaviour above is -documented-but-untested here. Worth re-checking when Auto goes back on. +- `client-light-theme` / `client-dark-theme` react to a change, +- `client-attached` picks the right one for a client that attaches mid-way, since + the two above only fire on a change. + +Because this keys off the terminal's reported theme rather than a schedule, it +behaves identically whether the change came from Auto at sunrise/sunset or from +toggling Light/Dark by hand — nothing in the chain knows why it changed. + +> [!NOTE] +> A running nvim will not follow a live change: the docs are explicit that the +> TUI sets `background` *on startup* if it can detect it. New instances are +> fine; existing ones need `:set background=light` or a restart. delta is +> per-invocation so should be fine, but that is untested here. > [!NOTE] > This previously used [`dark-mode-notify`](https://github.com/bouk/dark-mode-notify) diff --git a/bootstrap.sh b/bootstrap.sh index a2cf98f..dcc8bad 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -8,6 +8,8 @@ echo "Sym-linking the various config files" test -L $HOME/.gitignore || ln -f -s $BASEDIR/gitignore $HOME/.gitignore test -L $HOME/.hushlogin || ln -f -s $BASEDIR/hushlogin $HOME/.hushlogin test -L $HOME/.tmux.conf || ln -f -s $BASEDIR/tmux $HOME/.tmux.conf +test -L $HOME/.tmux-light.conf || ln -f -s $BASEDIR/tmux-light.conf $HOME/.tmux-light.conf +test -L $HOME/.tmux-dark.conf || ln -f -s $BASEDIR/tmux-dark.conf $HOME/.tmux-dark.conf test -d $HOME/.config/sheldon || mkdir $HOME/.config/sheldon test -L $HOME/.config/sheldon/plugins.toml || ln -f -s $BASEDIR/sheldon.toml $HOME/.config/sheldon/plugins.toml test -L $HOME/.zsh || ln -f -s $BASEDIR/zsh $HOME/.zsh diff --git a/tmux b/tmux index 90e8c15..49182f0 100644 --- a/tmux +++ b/tmux @@ -33,9 +33,6 @@ set -g default-terminal "$TERM" # Allow OSC 8 links set -ga terminal-features "*:hyperlinks" -# Tweak status bar styles -set -g status-style bg=default,fg='#fdfdd9' - # Set status bar length set -g status-left-length 40 set -g status-right-length 60 @@ -43,14 +40,16 @@ set -g status-right-length 60 # Center the window list set -g status-justify centre -# Left side: session name with blue accent -set -g status-left "#[fg=#1a2938,bg=#8fb4cd,bold] #S #[fg=#8fb4cd,bg=default]\ue0b0" +# Status bar colours live in ~/.tmux-light.conf and ~/.tmux-dark.conf. tmux +# learns the terminal's theme over OSC 2031, so this reacts to the appearance +# change itself, whether that came from Auto at sunrise/sunset or a manual +# toggle, since nothing here knows why it changed. +set-hook -g client-light-theme 'source-file ~/.tmux-light.conf' +set-hook -g client-dark-theme 'source-file ~/.tmux-dark.conf' -# Right side: multi-segment with transitions -set -g status-right "#[fg=#6dafb5,bg=default]\ue0b2#[fg=#1a2938,bg=#6dafb5] %H:%M #[fg=#bb98d9,bg=#6dafb5]\ue0b2#[fg=#1a2938,bg=#bb98d9] %d-%b " +# Those hooks only fire on a change, so also pick the right palette whenever a +# client attaches. Sourcing is idempotent, so the overlap is harmless. +set-hook -g client-attached 'if -F "#{==:#{client_theme},light}" "source-file ~/.tmux-light.conf" "source-file ~/.tmux-dark.conf"' -# Inactive windows (no powerline symbols) -setw -g window-status-format "#[fg=#2f4656,bg=default] #I #W " - -# Active window (cyan highlight, no powerline) -setw -g window-status-current-format "#[fg=#1a2938,bg=#6dafb5,bold] #I #W " +# Default before any client has attached and reported a theme. +source-file ~/.tmux-dark.conf diff --git a/tmux-dark.conf b/tmux-dark.conf new file mode 100644 index 0000000..c6cc4c5 --- /dev/null +++ b/tmux-dark.conf @@ -0,0 +1,21 @@ +# tangere-dark status bar, sourced by the client-dark-theme hook in ~/.tmux.conf +# +# Colours are tangere-dark palette entries so the bar matches the ghostty theme. +# Text sitting on an accent uses the theme background (#1a2938). +# #fdfdd9 foreground #1a2938 background #2f4656 dim (15) +# #8fb4cd blue (4) #6dafb5 cyan (2) #bb98d9 magenta (5) + +# Tweak status bar styles +set -g status-style bg=default,fg='#fdfdd9' + +# Left side: session name with blue accent +set -g status-left "#[fg=#1a2938,bg=#8fb4cd,bold] #S #[fg=#8fb4cd,bg=default]\ue0b0" + +# Right side: multi-segment with transitions +set -g status-right "#[fg=#6dafb5,bg=default]\ue0b2#[fg=#1a2938,bg=#6dafb5] %H:%M #[fg=#bb98d9,bg=#6dafb5]\ue0b2#[fg=#1a2938,bg=#bb98d9] %d-%b " + +# Inactive windows (no powerline symbols) +setw -g window-status-format "#[fg=#2f4656,bg=default] #I #W " + +# Active window (cyan highlight, no powerline) +setw -g window-status-current-format "#[fg=#1a2938,bg=#6dafb5,bold] #I #W " diff --git a/tmux-light.conf b/tmux-light.conf new file mode 100644 index 0000000..0b49b8d --- /dev/null +++ b/tmux-light.conf @@ -0,0 +1,22 @@ +# tangere-light status bar, sourced by the client-light-theme hook in ~/.tmux.conf +# +# Same layout as tmux-dark.conf, with each colour swapped for the tangere-light +# palette entry at the same index. Roles are preserved: text on an accent uses +# the theme background, which is now light rather than dark. +# #000000 foreground #fdfdfa background #d1d1d1 dim (15) +# #223355 blue (4) #004c73 cyan (2) #663d52 magenta (5) + +# Tweak status bar styles +set -g status-style bg=default,fg='#000000' + +# Left side: session name with blue accent +set -g status-left "#[fg=#fdfdfa,bg=#223355,bold] #S #[fg=#223355,bg=default]\ue0b0" + +# Right side: multi-segment with transitions +set -g status-right "#[fg=#004c73,bg=default]\ue0b2#[fg=#fdfdfa,bg=#004c73] %H:%M #[fg=#663d52,bg=#004c73]\ue0b2#[fg=#fdfdfa,bg=#663d52] %d-%b " + +# Inactive windows (no powerline symbols) +setw -g window-status-format "#[fg=#d1d1d1,bg=default] #I #W " + +# Active window (cyan highlight, no powerline) +setw -g window-status-current-format "#[fg=#fdfdfa,bg=#004c73,bold] #I #W "