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 status in 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
SymbolMeaning
Branch name.
# / Detached HEAD: the tag it points at, or the short sha.
→origin/trunkUpstream branch, shown only when its name differs from yours.
⇣2 / ⇡1Commits behind / ahead of the upstream branch.
*3Stashes.
~1Unmerged (conflicted) files.
+2Staged files.
!4Files changed but not staged.
?5Untracked files.
rebase 1/3Operation 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

icon


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 /proc and /sys. Optionally:
    • hostname (local IP address)
    • curl (global IP address)
  • macOS:
    • ps (CPU usage)
    • vm_stat and sysctl (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

  1. Zsh: Ensure Zsh is your default shell (echo`echoSHELL`).
  2. Oh My Zsh: Ensure you have Oh My Zsh installed. You can install it from Oh My Zsh's official site.
  3. Powerline fonts
  4. figlet: Used for drawing ascii art on terminal init

Steps

  1. (Optional)
    • Linux:
      sudo apt-get install figlet
      
    • macOS:
      brew install figlet
      
  2. Download and run the installation script:
    zsh <(curl -fsSL https://github.com/dimgatz98/Baddcafe_zsh_theme/raw/main/theme-installer.sh)
    
  3. 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:

  1. 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
    
  2. Update your .zshrc file:
    • Open the file in an editor:
      vim ~/.zshrc # or your favourite editor
      
    • Set the ZSH_THEME to "baddcafe":
      ZSH_THEME="baddcafe"
      
  3. Save the file and reload Zsh:
    source ~/.zshrc
    

Configuration

Set any of these in ~/.zshrc before Oh My Zsh loads the theme:

VariableDefaultMeaning
BADDCAFE_TICK1Prompt redraw interval, in seconds. Drives the clock.
BADDCAFE_POLL1How often the background collector wakes up.
BADDCAFE_INTERVAL_CPU2CPU sampling interval, in seconds.
BADDCAFE_INTERVAL_MEM2Memory sampling interval.
BADDCAFE_INTERVAL_BAT30Battery sampling interval.
BADDCAFE_INTERVAL_LOCALIP60Local IP refresh interval.
BADDCAFE_INTERVAL_GLOBALIP3600Global IP refresh interval.
BADDCAFE_GLOBALIP1Set to 0 to never contact an external service.
BADDCAFE_GLOBALIP_URLifconfig.meService used to resolve the public IP.
BADDCAFE_GLOBALIP_TIMEOUT3Hard timeout, in seconds, for that lookup.
BADDCAFE_BANNER1Set to 0 to skip the figlet banner.
BADDCAFE_SET_BACKGROUND1Set to 0 to leave the terminal background alone.
BADDCAFE_PROMPT_GAP1Blank columns between the end of the prompt and what you type.
BADDCAFE_GIT1Set to 0 to drop the git segment entirely.
BADDCAFE_GIT_ASYNC1Set 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_STASH1Set to 0 to hide the stash count.
BADDCAFE_GIT_TAG1Set to 0 to show a bare sha on a detached HEAD instead of looking up a tag.
BADDCAFE_GIT_BG_CLEAN071Segment background when there is nothing to commit.
BADDCAFE_GIT_BG_DIRTY167Segment background when there is.
BADDCAFE_GIT_FG235Text 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:

  1. 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: $WIDGET is 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.

  2. Call the zle builtin from TRAPALRM itself, 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.