Overview
In moving to a terminal heavy workflow, there’s a handful of applications that I use that I would need to reconfigure to handle a light theme. My goal is to have the light and dark theme both be easily accessible, and defined in a way that logging into a system would persist the currently selected theme.
Given my current workflow, this meant having to figure out how make a make a light or dark theme work situationally for the following apps:
- Terminator
- bash
- vim
- bat
- fzf
- lsd
- cointop
- venv
Terminator
For terminator, I installed the
terminator-themes
plugin
which made managing two separate themes as easy a default layout for my
dark theme, and a light layout for my light theme.
For the default theme I use a modification of “Cobalt Neon”, and for the light
theme I use “OneHalfLight”. Each of these is setup as a profile in terminator,
and for the light profile, under the command tab, I’ve checked the “Run a
custom command instead of my shell”, with BASHTHEME=light bash -l
being what
is run, to set the BASHTHEME
environment variable before starting our shell.
Then the layouts are as simple as telling the default to launch with a single
“Cobalt Neon” profile child, and the light layout to launch with a single
“OneHalfLight” profile child.
Then to make it so that I could launch directly into a light themed terminator
layout directly from a keyboard shortcut, I copied the default
terminator.desktop
file and modified it so that it reflected we were
launching a light session. This meant changing the Exec=terminator
lines to
Exec=terminator -l light
, the name from Name=Terminator
to Name=Terminator Light
,
and the Name=Open a New Window
to Name=Open a New Light Window
in
the [NewWindow Shortcut Group]
.
The full light theme terminator-light.desktop
file I use is as follows:
[Desktop Entry]
Categories=GNOME;GTK;Utility;TerminalEmulator;System;
Comment=Multiple terminals in one window
Exec=terminator -l light
Icon=utilities-terminal
Keywords=terminal;shell;prompt;command;commandline;
Name=Terminator Light
NoDisplay=false
StartupNotify=true
Terminal=0
TerminalOptions=
TryExec=terminator
Type=Application
X-Ayatana-Desktop-Shortcuts=NewWindow;
X-KDE-SubstituteUID=false
X-KDE-Username=
X-Ubuntu-Gettext-Domain=terminator
[NewWindow Shortcut Group]
Exec=terminator -l light
Name=Open a New Light Window
TargetEnvironment=Unity
Bash, bat, and fzf
Since we’ve setup Terminator to pass along a BASHTHEME=light
whenever launching
our light, let’s handle working with that in our ~/.bashrc
. To keep my ~/.bashrc
portable, and usable on systems even where I haven’t configured this, I’ll handle
any of the actual configuration in a file called ~/.bash_theme
. Only if it exists,
and we’re on a color_256
system will we try to source it. These lines are directly
out of my ~/.bashrc
:
if [[ "$color_256" == "yes" && -f ~/.bash_theme ]]; then
. ~/.bash_theme
elif [ "$color_256" = yes ]; then
PS1=...
Now in the ~/.bash_theme
file, we’re going to handle setting our PS1 to match
our theme, as well as exporting any color configurations that we can set simply
by environment variables. In this case, that means bat, and fzf can be nicely
handled here.
We assume that if there is no $BASHTHEME
environment variable is set, that we
just want the dark theme.
With the dark theme, we want bat to use OneHalfDark
as the theme for syntax
highlighting, and OneHalfLight
for the light theme.
With fzf, we only need to specify that we want the light theme in it’s default
options by tacking on a --color=light
option at the end of our default
options.
In the case of our PS1/bind commands, these both configure my bash status line to look match the lightline plugin for vim, nicely displaying INSERT/NORMAL since I’m using vi mode.
The light-min
and dark-min
themes are specifically there for
Termux , since rendering the full status line takes up
too much space on a phone rotated in portrait.
~/.bash_theme
if [ -z "$BASHTHEME" ]; then
export BASHTHEME="dark"
fi
dark() {
PS1='\[\e[38;5;231m\]\[\e[48;5;244m\] \h | \u \[\e[48;5;240m\] \w \[\e[0m\] '
bind 'set vi-cmd-mode-string "\1\e[01;38;5;232m\2\1\e[48;5;150m\2 NORMAL \1\e[0m\2"'
bind 'set vi-ins-mode-string "\1\e[01;38;5;232m\2\1\e[48;5;111m\2 INSERT \1\e[0m\2"'
export PS1
export BAT_THEME="OneHalfDark"
}
light() {
PS1='\[\e[38;5;231m\]\[\e[48;5;31m\] \h | \u \[\e[48;5;24m\] \w \[\e[0m\] '
bind 'set vi-cmd-mode-string "\1\e[38;5;232m\2\1\e[48;5;231m\2 NORMAL \1\e[0m\2"'
bind 'set vi-ins-mode-string "\1\e[38;5;63m\2\1\e[48;5;231m\2 INSERT \1\e[0m\2"'
export PS1
export BAT_THEME="OneHalfLight"
export FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS} --color=light"
}
dark-min() {
PS1='\[\e[38;5;231m\]\[\e[48;5;240m\] \w \[\e[0m\] '
bind 'set vi-cmd-mode-string "\1\e[01;38;5;232m\2\1\e[48;5;150m\2 N \1\e[0m\2"'
bind 'set vi-ins-mode-string "\1\e[01;38;5;232m\2\1\e[48;5;111m\2 I \1\e[0m\2"'
export PS1
export BAT_THEME="OneHalfDark"
}
light-min() {
PS1='\[\e[38;5;231m\]\[\e[48;5;31m\] \w \[\e[0m\] '
bind 'set vi-cmd-mode-string "\1\e[38;5;232m\2\1\e[48;5;231m\2 N \1\e[0m\2"'
bind 'set vi-ins-mode-string "\1\e[38;5;63m\2\1\e[48;5;231m\2 I \1\e[0m\2"'
export PS1
export BAT_THEME="OneHalfLight"
export FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS} --color=light"
}
case "$BASHTHEME" in
dark)
dark
;;
light)
light
;;
dark-minimal)
dark-min
;;
light-minimal)
light-min
;;
*)
echo "Warning: Unknown theme: ${BASHTHEME}. Using dark."
dark
;;
esac
Vim
In vim, I have two different color scheme plugins installed,
onedark
and
papercolor-theme
. I also have two plugins that depending on the theme, I need to set configurations for, these are lightline and limelight.
I make use of my ~/.vim/after/plugin/
directory to handle the settings for each of them, there I check if the status
of the $BASHTHEME
variable and apply changes accordingly. For example, these are the contents of ~/.vim/after/plugin/papercolor-theme.vim
:
if ($BASHTHEME ==? "light" || $BASHTHEME ==? "light-min")
set background=light
colorscheme PaperColor
endif
I do the same thing in ~/.vim/after/plugin/onedark.vim
, except instead
checking for a “dark” or “dark-min” theme. Then after making some modifications
to the colorscheme to support a transparent background, I set the following:
set background=dark
colorscheme onedark
In ~/.vim/after/plugin/limelight.vim
, I have to check if we’re on a dark
theme, and adjust my conceal color to match the changes I made to the onedark
colorsheme.
In ~/.vim/after/plugin/lightline.vim
, for a dark theme, I use the one
colorscheme and the dark
background, and for the light theme, I use the
PaperColor
theme with the light
background.
lsd
The latest release version of lsd
didn’t support color themes, and the default
values used have enough fields that refuse to show on a white background. Building
the latest version from source with cargo allowed me access to the current theme
configuration that they offer. Instead of changing this conditionally, instead I opted
to change the values to something that would be visible in both light and dark theme.
To set it up so that it would see the themes, I have the following as my ~/.config/lsd/config.yaml
color:
theme: ~/.config/lsd/themes/light.yaml
and the contents of ~/.config/lsd/themes/light.yaml
are:
user: 202
group: 129
permission:
read: dark_green
write: dark_yellow
exec: dark_red
exec-sticky: 5
no-access: 250
date:
hour-old: 40
day-old: 42
older: 36
size:
none: 250
small: 200
medium: 172
large: 196
inode:
valid: 128
invalid: 250
links:
valid: 128
invalid: 250
tree-edge: 250
The main difference here is I’ve changed the bright yellows to colors like orange and purple.
cointop
Cointop is the first instance that I used a hack to handle toggling themes. Cointop
allows me to launch while specifying a colorscheme, so I cheated, and aliased cointop
to a function called _cointop
that checks to see if the current $BASHTHEME
, and if
so launch it with the xray
colorscheme. The following lines are defined in my ~/.bash_aliases
:
function _cointop(){
case $BASHTHEME in
light*)
cointop --colorscheme xray $@
;;
*)
cointop $@
;;
esac
}
# cointop colorscheme
alias cointop='_cointop'
venv
The only reason I have to care about venv, is because my PS1 doesn’t follow a convention
that allows venv to nicely append a prefix to it. To handle this, I’ve modified the default
activate
script that venv
normally installs to the following:
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
deactivate () {
# reset old environment variables
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
PATH="${_OLD_VIRTUAL_PATH:-}"
export PATH
unset _OLD_VIRTUAL_PATH
fi
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r 2> /dev/null
fi
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
PS1="${_OLD_VIRTUAL_PS1:-}"
export PS1
unset _OLD_VIRTUAL_PS1
fi
unset VIRTUAL_ENV
if [ ! "${1:-}" = "nondestructive" ] ; then
# Self destruct!
unset -f deactivate
fi
}
# unset irrelevant variables
deactivate nondestructive
VIRTUAL_ENV="__VENV_DIR__"
export VIRTUAL_ENV
_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
export PATH
# unset PYTHONHOME if set
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
# could use `if (set -u; : $PYTHONHOME) ;` in bash
if [ -n "${PYTHONHOME:-}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
unset PYTHONHOME
fi
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
_OLD_VIRTUAL_PS1="${PS1:-}"
case $BASHTHEME in
light)
PS1='\[\e[38;5;231m\]\[\e[48;5;31m\] \h | \u \[\e[48;5;24m\] __VENV_PROMPT__| \w \[\e[0m\] '
;;
light-min)
PS1='\[\e[38;5;231m\]\[\e[48;5;24m\] __VENV_PROMPT__| \w \[\e[0m\] '
;;
dark-min)
PS1='\[\e[38;5;231m\]\[\e[48;5;240m\] __VENV_PROMPT__| \w \[\e[0m\] '
;;
*)
PS1='\[\e[38;5;231m\]\[\e[48;5;244m\] \h | \u \[\e[48;5;240m\] __VENV_PROMPT__| \w \[\e[0m\] '
esac
export PS1
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r 2> /dev/null
fi
This will manually set the PS1 based on the current $BASHTHEME
, nicely including the prompt
in with the color theme. I then have to run an installer script to make sure that I’ve properly
overwritten the activate script venv normally installs to be the one presented above.
#!/bin/bash
dir="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
if ! [ $(id -u) = 0 ]; then
echo "I am not root!"
exit 1
fi
_pyVersions="3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13"
for _py in $_pyVersions; do
if [ -d "/lib/python${_py}/venv/" ]; then
if ! [ -f "/lib/python${_py}/venv/scripts/common/activate.bash" ]; then
mv "/lib/python${_py}/venv/scripts/common/activate" "/lib/python${_py}/venv/scripts/common/activate.bash"
fi
cp "${dir}/activate" "/lib/python${_py}/venv/scripts/common/activate"
fi
done