Baddcafe - Oh My Zsh Theme
September 5, 2026 · View on GitHub
A custom Oh My Zsh theme that shows live system information alongside a Git-aware prompt. The status line refreshes once a second without blocking the shell: all data collection happens in a detached background process, so the prompt itself does no work.
Features
- System Information:
- CPU usage
- Memory usage
- Battery level
- Local and global IP addresses
- Dynamic Prompt:
- Current time
- Current working directory
- Success (
green ➜) or failure (red ➜) of the last command
- Git Status (powerlevel10k-style):
- Branch name, tag, or short sha for a detached HEAD
- Commits ahead of / behind the upstream branch
- Counts of conflicted, staged, unstaged and untracked files
- Stash count
- The operation in progress: merge, rebase (with
step/total), cherry-pick, revert or bisect - Computed off the prompt path, so a slow
git statusin a large repository never blocks the shell
- Auto-Updating:
- Refreshes system information every second while idle
- Updates seamlessly without disrupting typing
- Zero subprocesses on the prompt path; each metric has its own refresh interval, and network lookups are cached and time-limited
The git segment
main →origin/trunk ⇣2⇡1 *3 ~1 +2 !4 ?5 rebase 1/3
| Symbol | Meaning |
|---|---|
| Branch name. |
# / ➦ | Detached HEAD: the tag it points at, or the short sha. |
→origin/trunk | Upstream branch, shown only when its name differs from yours. |
⇣2 / ⇡1 | Commits behind / ahead of the upstream branch. |
*3 | Stashes. |
~1 | Unmerged (conflicted) files. |
+2 | Staged files. |
!4 | Files changed but not staged. |
?5 | Untracked files. |
rebase 1/3 | Operation in progress: merge, rebase, cherry-pick, revert, bisect. |
A file that is both staged and edited again afterwards is counted in + and
in ! -- that is the distinction those two numbers exist to make.
The segment is drawn as a powerline segment of its own, following the path:
green background when there is nothing to commit, red when there is, dark
text throughout, and the closing sits at the end of it. Like p10k's
rainbow style, the state lives in the background colour and the counts are told
apart by their symbols rather than by colours of their own.
Everything above comes from a single git status --porcelain=v2 --branch,
which requires git 2.11 or newer. It runs in a child process, and the
result is picked up over a pipe as soon as it lands, so the prompt is drawn
immediately and the segment appears a moment later rather than the shell
waiting on git. Set BADDCAFE_GIT_ASYNC=0 if you would rather it be
synchronous.
Preview
![]()
System Requirements
Supported Operating Systems
- macOS: Version 10.12 (Sierra) and later, Intel and Apple Silicon
- Linux: Most modern distributions, including:
- Ubuntu (16.04 and later)
- Debian (9 and later)
- CentOS (7 and later)
- Fedora (latest)
- Arch Linux (latest)
- openSUSE (latest)
Required Tools and Dependencies
- Shell: Zsh 5.3 or newer (the theme is written specifically for Zsh)
- Linux: no external tools are needed for CPU, memory or battery -- these
are read directly from
/procand/sys. Optionally:hostname(local IP address)curl(global IP address)
- macOS:
ps(CPU usage)vm_statandsysctl(memory usage)pmset(battery status)ifconfig(local IP address)curl(global IP address)
- Terminal Support:
- Terminal should support ANSI escape sequences for color coding and background changes.
Optional:
- Git: 2.11 or newer, for the git segment.
- figlet: Draws the ASCII art banner on shell start.
Installation
You can install the theme using the provided installation script.
Prerequisites
- Zsh: Ensure Zsh is your default shell (SHELL`).
- Oh My Zsh: Ensure you have Oh My Zsh installed. You can install it from Oh My Zsh's official site.
- Powerline fonts
- figlet: Used for drawing ascii art on terminal init
Steps
- (Optional)
- Linux:
sudo apt-get install figlet - macOS:
brew install figlet
- Linux:
- Download and run the installation script:
zsh <(curl -fsSL https://github.com/dimgatz98/Baddcafe_zsh_theme/raw/main/theme-installer.sh) - Restart your terminal or reload Zsh (if needed):
source ~/.zshrc
That’s it! You are now ready to use the Baddcafe theme! 🎉
The installer edits ~/.zshrc between two marker comments and prefixes any
ZSH_THEME line it disables with #baddcafe-disabled#. Re-running it is safe,
and uninstall undoes exactly those edits and nothing else. A timestamped copy
of your ~/.zshrc is kept in ~/.baddcafe/ either way.
Updating
zsh <(curl -fsSL https://github.com/dimgatz98/Baddcafe_zsh_theme/raw/main/theme-installer.sh) update
Uninstalling
zsh <(curl -fsSL https://github.com/dimgatz98/Baddcafe_zsh_theme/raw/main/theme-installer.sh) uninstall
Manual Installation
If you prefer to install the theme manually:
- Download the theme file:
curl -fsSL https://raw.githubusercontent.com/dimgatz98/Baddcafe_zsh_theme/main/theme/baddcafe.zsh-theme -o ~/.oh-my-zsh/custom/themes/baddcafe.zsh-theme - Update your
.zshrcfile:- Open the file in an editor:
vim ~/.zshrc # or your favourite editor - Set the
ZSH_THEMEto"baddcafe":ZSH_THEME="baddcafe"
- Open the file in an editor:
- Save the file and reload Zsh:
source ~/.zshrc
Configuration
Set any of these in ~/.zshrc before Oh My Zsh loads the theme:
| Variable | Default | Meaning |
|---|---|---|
BADDCAFE_TICK | 1 | Prompt redraw interval, in seconds. Drives the clock. |
BADDCAFE_POLL | 1 | How often the background collector wakes up. |
BADDCAFE_INTERVAL_CPU | 2 | CPU sampling interval, in seconds. |
BADDCAFE_INTERVAL_MEM | 2 | Memory sampling interval. |
BADDCAFE_INTERVAL_BAT | 30 | Battery sampling interval. |
BADDCAFE_INTERVAL_LOCALIP | 60 | Local IP refresh interval. |
BADDCAFE_INTERVAL_GLOBALIP | 3600 | Global IP refresh interval. |
BADDCAFE_GLOBALIP | 1 | Set to 0 to never contact an external service. |
BADDCAFE_GLOBALIP_URL | ifconfig.me | Service used to resolve the public IP. |
BADDCAFE_GLOBALIP_TIMEOUT | 3 | Hard timeout, in seconds, for that lookup. |
BADDCAFE_BANNER | 1 | Set to 0 to skip the figlet banner. |
BADDCAFE_SET_BACKGROUND | 1 | Set to 0 to leave the terminal background alone. |
BADDCAFE_PROMPT_GAP | 1 | Blank columns between the end of the prompt and what you type. |
BADDCAFE_GIT | 1 | Set to 0 to drop the git segment entirely. |
BADDCAFE_GIT_ASYNC | 1 | Set to 0 to run git status in precmd instead of a child. |
BADDCAFE_GIT_UNTRACKED | (unset) | 0 skips the untracked scan (-uno), 1 forces it on. Unset defers to git's own status.showUntrackedFiles. |
BADDCAFE_GIT_STASH | 1 | Set to 0 to hide the stash count. |
BADDCAFE_GIT_TAG | 1 | Set to 0 to show a bare sha on a detached HEAD instead of looking up a tag. |
BADDCAFE_GIT_BG_CLEAN | 071 | Segment background when there is nothing to commit. |
BADDCAFE_GIT_BG_DIRTY | 167 | Segment background when there is. |
BADDCAFE_GIT_FG | 235 | Text colour inside the segment. |
Every symbol is a variable too: BADDCAFE_GIT_SYM_BRANCH, _DETACHED, _TAG,
_UPSTREAM, _AHEAD, _BEHIND, _STASH, _CONFLICT, _STAGED,
_UNSTAGED, _UNTRACKED. Colours are 256-colour numbers; symbols must stay
one column wide, for the reason given under
Notes on rendering.
Raising the intervals is the first thing to try if you keep many terminal tabs open; each shell runs its own collector.
The git segment runs once per command, not once per redraw, and it no longer
blocks the prompt while it does. In a repository large enough for git status
to still be felt, the untracked-file scan is usually what costs the time; turn
it off for that repository alone with
git config status.showUntrackedFiles no
and leave BADDCAFE_GIT_UNTRACKED unset, which is the default, so that git's
own setting is the one that decides. BADDCAFE_GIT_UNTRACKED=0 does the same
thing for every repository at once.
You can also edit the theme file directly at:
~/.oh-my-zsh/custom/themes/baddcafe.zsh-theme
Feel free to tweak the colors, symbols, or displayed information to suit your preferences.
Notes on rendering
Every emoji in the prompt carries an explicit %2{...%} width hint. Zsh sizes
prompts with wcwidth(), which reports one column for the variation-selector
sequences (U+FE0F) used by the clock and gear emoji, while terminals draw two.
Without the hint zsh miscounts the line, the terminal wraps without zsh knowing,
and every redraw leaves a stray blank line behind -- most visibly on macOS,
whose wcwidth() tables are older. If you replace an emoji, keep the hint and
make the number match the columns your terminal actually draws.
Notes on redrawing
The clock is redrawn from TRAPALRM, once a second. Two rules keep that from
fighting with the line editor, and both were learned by breaking them:
-
Do not redraw while the line editor owns the screen below the prompt -- a completion listing, an incremental search, history navigation. The test has to look at
$LASTWIDGET, not only$WIDGET:$WIDGETis set only while a widget is running, and the alarm nearly always arrives between widgets, where it is empty. As a result the clock pauses while a Tab listing is on screen and resumes at your next keystroke. -
Call the
zlebuiltin fromTRAPALRMitself, never from a helper function it calls. Moving that one line a function deeper leaves the line editor out of step: the next keypress discards what you had typed, redraws the whole five-line prompt, and the completion listing you were looking at is gone -- which looks exactly like "the prompt keeps throwing newlines around when I press Tab".
Contributions
Feel free to fork the repo or suggest improvements by creating a pull request. Contributions to enhance the functionality or add new features are welcome.
License
This theme is provided under the GNU GENERAL PUBLIC LICENSE. See LICENSE for details.