Dynamic ZSH Dragon Prompt

How to motivate yourself by pretending a fire-breathing dragon is chasing you through time.

I wrote this little prompt for Zsh & Oh My Zsh. It displays a series of clockface emojis, and replaces each one with a fire emoji as the day wears on.

Dragon of Time

When the hours run out, the dragon sleeps. Respite!

The Code

If you'd like to use this prompt, or repurpose the code for your own dynamic prompt, here's the entire .zsh-theme file. Read about Oh My Zsh Customization to see how to use your own custom themes.


# time-dragon.zsh-theme
THISFILE=${BASH_SOURCE:-$0}

DRAGON="🐉"
DRAGON_SLEEP="🐲💤"
BED="🛏 "
FIRE='🔥'
CLOCKFACES=( 🕛 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 )

HOUR=`date +%H` # current hour
STARTHOUR=08 # first clock to show
ENDHOUR=19 # last clock to show

PROMPT_TOP=''
PROMPT_BOT=''

# If we're outside the bounds of the start/end hours, show the sleeping dragon
if [[ $HOUR -gt $ENDHOUR || $HOUR -lt $STARTHOUR ]]; then
    PROMPT_TOP=$DRAGON_SLEEP
    PROMPT_BOT=$BED
else
# otherwise, show the burned and remaining clocks
    for i in `seq $STARTHOUR $ENDHOUR`; do
        if [[ $i -lt $HOUR  ]]; then
            PROMPT_TOP="$PROMPT_TOP$FIRE"
        else
            PROMPT_TOP="$PROMPT_TOP$CLOCKFACES[$i + 1]"
        fi
    done
    PROMPT_BOT=$DRAGON
fi

# Assemble the full prompt and assign it to the ZSH PROMPT var
PROMPT=$'\n$PROMPT_TOP $FG[032]%~%{$reset_color%}\n$PROMPT_BOT '

# Refresh the prompt every minute
periodic() {
    source $THISFILE
}
PERIOD=60

Explanation

I'll walk you through the code so you know what each part does.


# time-dragon.zsh-theme
THISFILE=${BASH_SOURCE:-$0}

DRAGON="🐉"
DRAGON_SLEEP="🐲💤"
BED="🛏 "
FIRE='🔥'
CLOCKFACES=( 🕛 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 )

HOUR=`date +%H` # current hour
STARTHOUR=08 # first clock to show
ENDHOUR=19 # last clock to show

In the first part (above), we're declaring some variables we'll use, such as the emoji characters, the current hour, and the range of hours to display. If you work different hours, you can change STARTHOUR and ENDHOUR to suit your day. See the caveat at the bottom of this article if your day crosses the midnight barrier!


# If we're outside the bounds of the start/end hours, show the sleeping dragon
if [[ $HOUR -gt $ENDHOUR || $HOUR -lt $STARTHOUR ]]; then
    PROMPT_TOP=$DRAGON_SLEEP
    PROMPT_BOT=$BED
else
# otherwise, show the burned and remaining clocks
    for i in `seq $STARTHOUR $ENDHOUR`; do
        if [[ $i -lt $HOUR  ]]; then
            PROMPT_TOP="$PROMPT_TOP$FIRE"
        else
            PROMPT_TOP="$PROMPT_TOP$CLOCKFACES[$i + 1]"
        fi
    done
    PROMPT_BOT=$DRAGON
fi

Here is where the comparison of hours is done. As noted below, this script uses simple comparisons which assume that you begin sometime in the morning, and end before midnight. If the current hour is outside those boundaries, none of the clocks are printed, and the sleeping dragon is shown instead.

Otherwise, the else clause will loop through every hour from STARTHOUR to ENDHOUR in sequence, print a fire emoji if the hour has passed, or the appropriate clock emoji from the CLOCKFACES array if the hour hasn't ended yet.


# Assemble the full prompt and assign it to the ZSH PROMPT var
PROMPT=$'\n$PROMPT_TOP $FG[032]%~%{$reset_color%}\n$PROMPT_BOT '

In this line, we're creating the whole prompt by setting the Zsh variable PROMPT. The string is prefixed with a dollar sign $'' so that escape characters like the newline \n are interpreted correctly.

It starts with a newline to put a bit of space between the new prompt and the last command's output. Next, it prints $PROMPT_TOP which contains either the fire & clocks, or the sleeping dragon.

$FG[032] is a Zsh built-in which applies a color code (this one is blue) to whatever text that follows. Next, %~ prints the current path, and %{$reset_color%} removes the blue color we had just applied.

Finally, it prints one more newline and the $PROMPT_BOT variable, which has been set to either the dragon or bed emoji.


# Refresh the prompt every minute
periodic() {
    source $THISFILE
}
PERIOD=60

To make the prompt dynamically update, we tell Zsh to source this theme file every 60 seconds using the built-in periodic() function and PERIOD variable. Since we can't control when the prompt is first rendered, we can't rely on simply changing this once an hour. It will update only when a new prompt is rendered, and only if it's been more than 60 seconds since the last one. Sourcing the file is fast, so there's no detectable performance hit.

And that's it. I hope you've found this fun or useful. You can use this prompt as-is, substitute your own design, or use the idea as inspiration for your own custom, dynamic Zshell prompt!

Oh, and one more thing...

CAVEAT

This code uses a start and end hour comparison that assumes you are starting sometime in the day, and ending before midnight. If your working hours cross the midnight boundary, then you won't be able to use a simple numerical comparison to determine which clocks to print. In that case, you have two problems:

  1. You're working too late. Go to bed earlier.
  2. You'll have to write your own code to account for the boundary.

Good luck, and may you slay every dragon!