Prettying up my terminal with ZSH and oh-my-posh
After many years of using zsh with powerlevel10k, switching to fish and then falling back to the default terminal, I’ve decided to try something new and more up-to-date… I’ve been using the default terminal for a long time now. It’s simple, it’s easy to use, and it’s what the system comes with. I’ve been wanting to try something new for a while now, but the lack of time kept me from doing so (as always).
Things like the quite restricted default CTRL+R history search, bland colors, ugly prompts etc. motivated me to give it another try.
Setting up ZSH
First, I needed to install ZSH. I’ve been using Ubuntu for my dev machines recently, since I’m required to use it at work and I got frustrated at NixOS too many times (which is a whole other story. Hint: PrismaORM).
sudo apt install zsh
chsh -s $(which zsh) # Change the default shell to zsh
Of course, depending on your distribution, the package manager might be different. On MacOS, zsh should be already installed (but you might want to update it with brew).
Installing oh-my-posh
Next, we need to install oh-my-posh.
curl -s https://ohmyposh.dev/install.sh | bash -s
This should install oh-my-posh into ~/.local/bin/, depending on your environment.
Now, we need to add the following to our .zshrc file:
eval "$(oh-my-posh init zsh)"
Motivating for using oh-my-posh
- Apparently, powerlevel10k is not maintained anymore (in terms of new features) and the support / help requests are not answered.
- Many people are reporting issues with Oh My ZSH and its performance.
- Starship is fast, “Rust-Powered” but does a bit too much “magic” for my taste.
Configuring oh-my-posh
Now, we need to configure oh-my-posh. The default configuration is embedded and we’d need to pass it to the oh-my-posh init command. To do so, we can start with the default configuration and modify it as needed.
oh-my-posh config export --format toml --output ~/.config/oh-my-posh/config.toml
Alternatively to .toml, we can also export the configuration to .yaml if wanted.
To load the configuration, we need to update the init command in our .zshrc file:
eval "$(oh-my-posh init zsh --config ~/.config/oh-my-posh/config.toml)"
The promt structure in oh-my-posh is separated into so called “segments”. Their documentation gives a good overview of the available segments and how to configure them.
Alternaively, they also provide a long list of themes to choose from, including powerlevel10k_lean and pure, which are quite similar to the one I was using back then.
I decided to write my own theme, inspired by the powerlevel10k_lean theme and the video by Dreams of Autonomy
# transient prompt is how are past prompts are displayed
[transient_prompt]
template = '❯ '
foreground = 'magenta'
foreground_templates = [
'{{ if gt .Code 0 }}red{{ end }}',
]
background = 'transparent'
# main prompt, separated into 2 lines with the path and the git status on top
[[blocks]]
type = 'prompt'
alignment = 'left'
[[blocks.segments]]
type = 'path'
template = '{{ .Path }} '
style = 'plain'
foreground = 'blue'
[blocks.segments.properties]
style = 'full'
[[blocks.segments]]
type = 'git'
template = '''{{ .HEAD }}{{ if or (.Working.Changed) (.Staging.Changed) }}*{{
end }} <cyan>{{ if gt .Behind 0}}⇣{{ end }}{{ if gt .Ahead 0 }}⇡{{ end }}</>'''
style = 'plain'
foreground = '#6c6c6c'
[blocks.segments.properties]
branch_icon = ''
commit_icon = '@'
fetch_status = true
# active commandprompt on a new line
[[blocks]]
type = 'prompt'
alignment = 'left'
newline = true
[[blocks.segments]]
type = 'text'
template = '❯'
style = 'plain'
foreground = 'magenta'
foreground_templates = [
'{{ if gt .Code 0 }}red{{ end }}'
]
# right prompt with the execution time of the last command
[[blocks]]
type = 'rprompt'
overflow = 'hidden'
[[blocks.segments]]
type = 'executiontime'
style = 'plain'
foreground = 'yellow'
template = '{{ .FormattedMs }}'
[blocks.segments.properties]
threashold = 2000
As seen by the last segment (executiontime), we can also add other segments to the prompt with different kinds of integrations (including nix btw). Those can be included in the prompt or in Tooltips, which are displayed during typing.
I did try to use some of the cli integrations to display the JavaScript package manager, but they were not working as expected. I might come back to this later as it does not select the type per lock-file.
Browsing the history with fzf
As mentioned earlier, I kinda…dislike the default history search. Yes, it’s there and with CTRL+R I can search through some part of the history, but once you have a few commands that are structured similarly, it becomes quite messy to find what I’m looking for.
Getting the history set up
First we’re making sure that the history is properly set up. Check your .zshrc for the following lines:
# History
export HISTFILE=~/.zsh_history
export HISTSIZE=2000
export SAVEHIST=2000
export HISTDUP=false
export HISTCONTROL=ignoredups
This will save the history to ~/.zsh_history with a maximum of 2000 entries (feel free to add more, for me it’s more than enough).
ZFS will start creating the history file after a first exit, so if you don't see it being used, restart your terminal.
Installing fzf
Next, we need to install fzf. There are multiple ways to install it, I actually wrote a package manager myself…, but if you’re on a debian-based system, the simplest would be the following:
sudo apt install fzf
Once installed, we just need to add fzf to our .zshrc file:
source <(fzf --zsh)
Et voilà, we can now use fzf to search through our history! There is no new command to learn, since it just maps to the default CTRL+R combination.
Prettify our history
It’s a blessing to use, but honestly it’s not very pretty. Thankfully, we can customize the search with some nice colors and themes.
My personal pick is usually catppuccin and thankfully they provide a fzf theme :3
“Installation” is simple, in the themes folder of the repo are some script files, which in my case just set an environment variable. For example catpuccin Macchiato:
export FZF_DEFAULT_OPTS=$FZF_DEFAULT_OPTS" \
--color=bg+:#363A4F,bg:#24273A,spinner:#F4DBD6,hl:#ED8796 \
--color=fg:#CAD3F5,header:#ED8796,info:#C6A0F6,pointer:#F4DBD6 \
--color=marker:#B7BDF8,fg+:#CAD3F5,prompt:#C6A0F6,hl+:#ED8796 \
--color=selected-bg:#494D64 \
--color=border:#6E738D,label:#CAD3F5"
Sadly, I didn’t find a way to define the theme in a config file, I did however a little addition to the theme provided by catppuccin to not completely override the default options.
This way you could potentially chain multiple options together in different locations of your .zshrc file.
Verdict
I’m quite happy with the result. It’s not perfect (yet), but it’s something to improve over time. I spend some time over the past few weeks to work-out a new tool-chain for docker deployments and the storage of my dotfiles after moving away from NixOS (for my dev machines !).
This is already a great start, the configs are stored on my GitHub and I’ll follow-up with some more setups if this was interesting for you. ^^