name: tmux-dracula-cpu-temp description: "Show CPU temperature in the tmux status bar via the Dracula theme, with dynamic color based on the value (cold/normal/warm/hot), in an update-safe way. macOS (Apple Silicon) version using smctemp. Use when working with tmux, dracula/tmux, or the user mentions adding a CPU temp segment to the tmux status line on a Mac."
Show Mac CPU temp in the tmux status bar (Dracula, colored, update-safe)
The dracula/tmux theme ships widgets for cpu-usage and ram-usage
but not CPU temperature. Patching dracula.sh works but TPM
updates (prefix + U) will clobber the edit.
The update-safe pattern: keep dracula stock and append an extra
segment to status-right in your own .tmux.conf, after the
run '~/.tmux/plugins/tpm/tpm' line. run-shell is synchronous, so
by the time tpm returns, dracula has already populated status-right
and a trailing set -ag status-right "..." sticks.
The script emits its own #[fg=...,bg=...] directive so the
background color changes with the temperature.
1. Install smctemp
On Apple Silicon, osx-cpu-temp and istats don't work.
Use smctemp — a lightweight
CLI that reads CPU temperature directly from the SMC:
brew tap narugit/tap
brew install narugit/tap/smctemp
smctemp -c prints just the CPU temp as a number (e.g. 52.3).
Why smctemp instead of macmon?
We originally used macmon, but
it polls Apple's IOReport framework at a 200ms interval
(macmon pipe -s 1 -i 200), which causes ~35% CPU usage — far too
heavy for a status-bar widget that refreshes every 5 seconds.
smctemp -c does a single direct SMC register read and exits
immediately, with negligible CPU overhead.
2. Wrapper script
~/.tmux/scripts/cpu_temp.sh:
#!/usr/bin/env bash
# Print colored CPU temp segment for tmux status bar (macOS, Apple Silicon).
# Uses smctemp (lightweight SMC reader) instead of macmon.
set -eu
NA='#[fg=#282a36,bg=#6272a4] |n/a'
if ! command -v smctemp >/dev/null 2>&1; then
printf '%s' "$NA"; exit 0
fi
raw=$(smctemp -c 2>/dev/null)
if [ -z "${raw:-}" ]; then
printf '%s' "$NA"; exit 0
fi
t=$(printf '%.0f' "$raw")
# Mac CPUs run hotter than SBCs — bands shifted up.
if [ "$t" -lt 60 ]; then bg='#8be9fd'; ico='🥶' # cyan cold
elif [ "$t" -lt 80 ]; then bg='#50fa7b'; ico='😎' # green normal
elif [ "$t" -lt 95 ]; then bg='#ffb86c'; ico='🥵' # orange warm
else bg='#ff5555'; ico='🔥' # red hot
fi
printf '#[fg=#282a36,bg=%s] |%s%s°C' "$bg" "$ico" "$t"
chmod +x ~/.tmux/scripts/cpu_temp.sh
3. Wire it into ~/.tmux.conf
At the very bottom, after the tpm run line:
# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
run '~/.tmux/plugins/tpm/tpm'
# Append CPU temp segment to status-right (after dracula has set it).
# Script emits its own color based on temp value. Survives TPM updates.
set -ag status-right "#(~/.tmux/scripts/cpu_temp.sh)"
Reload:
tmux source-file ~/.tmux.conf
The dracula refresh rate (@dracula-refresh-rate, default 5s) drives
how often the temp updates.
Why not patch dracula.sh?
Editing ~/.tmux/plugins/tmux/scripts/dracula.sh to add a cpu-temp
case works, but TPM's update step runs git pull in the plugin
checkout, so local edits create merge conflicts or get lost. Keeping
the segment in your own .tmux.conf plus a script under
~/.tmux/scripts/ puts the customisation entirely in files TPM never
touches.
Tip: how tmux interprets #[...] from #(...) output
tmux re-evaluates the stdout of #(command) as a format string by
default, so #[fg=...,bg=...] directives emitted by the script are
honoured. That's what lets the script set its own background color
dynamically.
Gotcha: emoji width on narrow terminals (phone SSH)
This one bites hard. On a ~40-column phone SSH session the status line starts wrapping its last 1-2 characters onto a second row, and every 5-second redraw adds another wrapped row until the screen is a stack of half-drawn status bars.
Root cause: tmux and your terminal disagree on how wide an emoji is. tmux measures the status with its own Unicode width table; some emoji it counts as 1 cell while your terminal renders them as 2. tmux thinks the line fits, doesn't truncate, and the terminal then paints it 1 cell too wide -- and because the status row sits at the bottom with autowrap on, it wraps and scroll-accumulates on every refresh.
It is not all emoji -- only the ones with Unicode
Emoji_Presentation=No (legacy "text-presentation" pictographs):
🌡 ☀ ❄ ♨ ✈ ☁ ❤ ✏. tmux follows the spec and
counts these 1 cell; terminals render them as 2. Emoji with
Emoji_Presentation=Yes (🔥 🥵 🥶 😎 💻 🧊 …) are
counted 2 by tmux, matching the terminal -- those are safe.
The thermometer 🌡 (U+1F321) is one of the bad ones, which is why an
earlier version of this skill overflowed. The fix: use an
Emoji_Presentation=Yes glyph instead. The script above picks a
width-2 face per temperature band (🥶/😎/🥵/🔥), so tmux and the
terminal always agree and the status never overflows.
Things that do NOT work
- Variation Selector-16 (U+FE0F): appending
️to force emoji presentation does not change tmux's width count (verified on tmux 3.6b --🌡️still measures width 1). - Trimming spaces: shaving a space compensates for one bad emoji but is fragile -- add another text-presentation emoji and it breaks again. Fix the glyph, not the spacing.
Probe any glyph's tmux width
Stash the string in a user option and pad it with #{p<N>:...}, which
uses tmux's own width math (a bare literal after p: is treated as a
variable name, so the option indirection is required):
tmux set -g @m '🌡'
tmux display -p '[#{p8:#{@m}}]' # width = 8 - (trailing spaces)
tmux set -gu @m
Width 1 = will overflow on a 2-cell-rendering terminal; width 2 = safe.
The general cure (terminal side)
The disagreement is really the terminal violating Unicode's default-presentation rule. Many terminals (Blink, Termius, WezTerm, kitty) expose a Unicode-width / "ambiguous = narrow" setting. Making the terminal spec-compliant renders text-presentation emoji as 1 cell too -- then tmux and terminal agree for every emoji and you can use any glyph, thermometer included.
Related
See linux-tmux-dracula-cpu-temp for the Linux SBC variant (Raspberry
Pi, Orange Pi) which reads /sys/class/thermal/thermal_zone0/temp
instead of using macmon.