Terminal auto dark/light mode

Here's how to update fish and neovim color scheme following the system theme when running in iTerm2.

Terminal auto dark/light mode
Photo by Pawel Czerwinski / Unsplash

๐Ÿ“– Context

I want to benefit from light/dark mode auto switch on as many apps as I can, including my terminal emulator (iTerm2), my favorite shell's (fish) and neovim.

Why light mode do you ask? Well, because when I work in a brightly lit room, a dark terminal strains my eyes. That's also why you'll likely find me using light mode on my IDE during the day (yes, my colleagues often complain about that during video conference, no I don't apologize).

๐Ÿง‘๐Ÿปโ€๐Ÿซ How to

๐Ÿ‘€ iTerm2 script

๐Ÿ’ก
iTerm2 is a powerful terminal emulator, and it is a free software under AGPL v1.

The solution I implemented takes advantage of iTerm2's Python API.

Create a new Basic, Long-Running Daemon script with the following content:

#!/usr/bin/env python3.10

import asyncio
import iterm2
import subprocess

async def main(connection):
    async with iterm2.VariableMonitor(
        connection, iterm2.VariableScopes.APP, "effectiveTheme", None
    ) as mon:
        while True:
            # Block until theme changes
            theme = await mon.async_get()

            # Themes have space-delimited attributes,
            # one of which will be light or dark.
            parts = theme.split(" ")
            if "dark" in parts:
                print("changing theme to dark")
                subprocess.run(["fish", "-c", "dark"])
            else:
                print("changing theme to light")
                subprocess.run(["fish", "-c", "light"])

# This instructs the script to run the "main" coroutine
# and to keep running even after it returns.
iterm2.run_forever(main)

The prints are here to help diagnose eventual issues with iTerm2's Script Console (โŒฅโŒ˜J).

This script depends on two functions - light and dark, let's create them before enabling the script.

๐Ÿ’ก
Save it in the AutoLaunch directory, so that iTerm automatically runs the script at startup.

๐ŸŸ Fish color scheme

function light --description 'Set shell theme to light mode, including neovim'
  yes | fish_config theme save "Catppuccin Latte"
  update_nvim_theme light
end

function dark --description 'Set shell theme to dark mode, including neovim'
  yes | fish_config theme save "Catppuccin Mocha"
  update_nvim_theme dark
end

funcsave light
funcsave dark

The light/dark functions set the appropriate fish color theme and defer to a third function to update neovim's color scheme.

๐Ÿ“œ Neovim background color

function update_nvim_theme --description 'Update neovim theme with light/dark mode' --argument theme
  for addr in /var/folders/7r/l55lwv013hl_g8gc971k7kgw0000gn/T/nvim.gaugendre/**/*
    nvim --server $addr --remote-send ":set background=$theme <cr>"
  end
end

funcsave update_nvim_theme

update_nvim_theme lists all running instances of neovim and sends a command to update the background color (light or dark). neovim then reacts accordingly and uses an appropriate color scheme.

๐Ÿ’ก
The folder containing the neovim's file descriptors won't be the same on your machine. You can find out yours using nvim --headless -c "echo stdpath('run')" -c "q"

โœ… Enable iTerm2 script

Finally, enable the script from the menu and enjoy fish following your color scheme.

๐Ÿค— Thank you

The script and functions presented here are a more advanced version of an answer I posted on SuperUser stack exchange recently. I'd like to thank NotTheDr01ds, who posted the first answer to this question. It led me in the right way.

Is it possible to set separate fish color configurations for light and dark system theme?
I use a terminal (Iterm2 Beta) that allows you to set a separate theme for light and dark mode. This is useful for changing the background color of the terminal, and the contrast of the various preโ€ฆ

๐Ÿ”ฎ Future work

I haven't (yet?) solved this for remote connections (ssh with tmux), please let me know if you have ideas!