Compare commits

...
Author SHA1 Message Date
Jonny Barnes
6b3ab420ad
Follow light/dark appearance in the tmux status bar
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) <noreply@anthropic.com>
2026-08-09 12:38:16 +01:00
Jonny Barnes
59d5622442
Remove dark-mode-notify, document the native approach
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) <noreply@anthropic.com>
2026-08-09 11:37:24 +01:00
6 changed files with 135 additions and 84 deletions

122
README.md
View file

@ -4,65 +4,95 @@ Heres my dotfiles, inspired by people like Mathias. See his dotfiles at
[`https://github.com/mathias/dotfiles`](https://github.com/mathias/dotfiles). [`https://github.com/mathias/dotfiles`](https://github.com/mathias/dotfiles).
The idea Im currently going down is to create a symlink from `$HOME` to this The idea Im currently going down is to create a symlink from `$HOME` to this
directory. There is one exception to this, the `.gitconfig` file. I dont want actual commiter details committed into this repo. So my git credentials are directory. There is one exception to this, the `.gitconfig` file. I dont want
stored in a `.extra` file which gets sourced. This then calls the relavent git actual commiter details committed into this repo, and they differ per machine
command, which causes git to edit `$HOME/.gitconfig`. If that file was a symlink anyway — this Mac signs with my work address, the iMac with my personal one.
to this repo, then the repo would see the file as edited and the repo would then
be in a dirty state, permanently. 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 ## Usage
First clone the repo. First clone the repo.
As mentioned above my git credentials are stored in an untracked file: Run `./bootstrap.sh`, this will create all the necessary symlinks, then source
`$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
`.zshrc`. `.zshrc`.
> [!WARNING] > [!WARNING]
> This is a **destructive** process, so backup your dotfiles first. > 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: [gpg "ssh"]
program = /Applications/1Password.app/Contents/MacOS/op-ssh-sign
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>ke.bou.dark-mode-notify</string>
<key>KeepAlive</key>
<true/>
<key>StandardErrorPath</key>
<string>/Users/jonny/.local/var/log/dark-mode-notify-stderr.log</string>
<key>StandardOutPath</key>
<string>/Users/jonny/.local/var/log/dark-mode-notify-stdout.log</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/dark-mode-notify</string>
<string>/Users/jonny/git/dotfiles/zsh/dark-mode-notify.zsh</string>
</array>
</dict>
</plist>
``` ```
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 terminals 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.
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:
- `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)
> 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. Dont bring it back.

View file

@ -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/.gitignore || ln -f -s $BASEDIR/gitignore $HOME/.gitignore
test -L $HOME/.hushlogin || ln -f -s $BASEDIR/hushlogin $HOME/.hushlogin 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.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 -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/.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 test -L $HOME/.zsh || ln -f -s $BASEDIR/zsh $HOME/.zsh

23
tmux
View file

@ -33,9 +33,6 @@ set -g default-terminal "$TERM"
# Allow OSC 8 links # Allow OSC 8 links
set -ga terminal-features "*:hyperlinks" set -ga terminal-features "*:hyperlinks"
# Tweak status bar styles
set -g status-style bg=default,fg='#fdfdd9'
# Set status bar length # Set status bar length
set -g status-left-length 40 set -g status-left-length 40
set -g status-right-length 60 set -g status-right-length 60
@ -43,14 +40,16 @@ set -g status-right-length 60
# Center the window list # Center the window list
set -g status-justify centre set -g status-justify centre
# Left side: session name with blue accent # Status bar colours live in ~/.tmux-light.conf and ~/.tmux-dark.conf. tmux
set -g status-left "#[fg=#1a2938,bg=#8fb4cd,bold] #S #[fg=#8fb4cd,bg=default]\ue0b0" # 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 # Those hooks only fire on a change, so also pick the right palette whenever a
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 " # 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) # Default before any client has attached and reported a theme.
setw -g window-status-format "#[fg=#2f4656,bg=default] #I #W " source-file ~/.tmux-dark.conf
# Active window (cyan highlight, no powerline)
setw -g window-status-current-format "#[fg=#1a2938,bg=#6dafb5,bold] #I #W "

21
tmux-dark.conf Normal file
View file

@ -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 "

22
tmux-light.conf Normal file
View file

@ -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 "

View file

@ -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