Skip to content

Instantly share code, notes, and snippets.

@Tamas-Toth-ebola
Last active February 15, 2026 18:28
Show Gist options
  • Select an option

  • Save Tamas-Toth-ebola/37ee04d9898dc5febf7203afeb61c0b1 to your computer and use it in GitHub Desktop.

Select an option

Save Tamas-Toth-ebola/37ee04d9898dc5febf7203afeb61c0b1 to your computer and use it in GitHub Desktop.
Custom Powerline Prompt

Note

Gemini Logo AI Assisted Project

This codebase demonstrates the power of human-AI (Google - Gemini) pair programming.

See Credits for contribution details.

(Multi-Platform) Custom Powerline Prompt

This Gist contains a synchronized Powerline prompt configuration for both ZSH and PowerShell environments. Bear in mind to use nerd fonts for it, like 'Delugia Complete'.

image

Note: Since Gists are full Git repositories, we can git pull in these directories to keep our prompts synchronized across all our devices.

Prompt Structure

The prompt is designed for high-density information at a glance, following an elegant Environment > Path > Git flow:

  • 💻 Environment:
    • Displays user@host context.
    • Shows the shell name with full version (e.g. pwsh 7.5.4)
    • SSH Aware: Colors dynamically shift if an active SSH session is detected.
  • 📂 Path:
    • Responsive Design: Adapts to screen width.
      • Desktop (≥80 cols): Shows nearly the full path (90% width) for maximum context.
      • Mobile (<80 cols): Aggressively shortens the path (35% width) to prevent wrapping on small screens.
    • Intelligent path shortening (collapses $HOME to ~).
    • Uses a separator between directories for better readability.
  • 🌿 Git Status:
    • Active only inside repositories.
    • Displays branch name and status: Clean or Dirty.
    • Shows / counts for upstream synchronization.
  • ⏱️ Execution Timer:
    • Appears automatically for commands taking longer than 2 seconds.
  • > Prompt:
    • A clean chevron finishes the line, keeping the input area tidy.

Usage

ZSH (Linux / macOS / Termux)

  1. Clone the Gist into eg. ~/Projects/Powerline_Prompt/...
  2. and add the following snippet to the end of your ~/.zshrc file to source the prompt configuration:
# Source the Powerline prompt configuration
PROMPT_CONFIG="$HOME/Projects/Powerline_Prompt/powerline_prompt.zsh"
[[ -f "$PROMPT_CONFIG" ]] && source "$PROMPT_CONFIG"

PowerShell (Windows)

  1. Clone the Gist into eg. $HOME\Projects\Powerline_Prompt\...
  2. and add the following snippet to your PowerShell profile (usually found at $PROFILE) to dot-source the configuration:
# Dot-source the Powerline prompt configuration
$PromptConfig = "$HOME\Projects\Powerline_Prompt\powerline_prompt.ps1"
if (Test-Path $PromptConfig) { . $PromptConfig }

Credits

# AI assistance | context
.gemini/
.geminiignore
# Images
*.png
*.jpg
*.jpeg
*.gif
*.bmp
*.tiff
*.webp
#----- POWERSHELL - CUSTOM PROMPT -----
# Powerline Prompt Configuration
# 0. GLOBAL SETTINGS
# Set UTF-8 encoding for proper PowerLine symbol display
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
# 1. FORMATTING OPTIONS (Pallete: ~ One Dark Pro)
# ANSI Escape Sequences for TrueColor (24-bit RGB) support:
# Structure: $ESC[<Type>;2;<R>;<G>;<B>m
# - $ESC : Escape character (ASCII 27)
# - [ : Control Sequence Introducer
# - Type : 38 for Foreground (Text), 48 for Background
# - ;2; : Specifies RGB mode
# - R;G;B : Red, Green, Blue values (0-255)
# - m : End of sequence
# Example: $ESC[38;2;244;71;71m sets text color to Red
# RGB Values for ANSI Escape Codes
$C_YEL = "255;215;95" # Yellow (Session BG / Timer FG)
$C_ORG = "255;135;0" # Vibrant Orange (Matching YEL intensity)
$C_PUR = "213;95;222" # Purple (Git BG)
$C_BLU = "97;175;239" # Soft Blue (Path BG / Prompt Arrow FG)
$C_RED = "244;71;71" # Pastel Red (Error BG)
$C_BLK = "0;0;0" # Black (Local Session BG)
$C_WHT = "255;255;255" # White (Error Text)
$C_TXT = "28;44;52" # Dark Slate (Text Color for colored blocks)
$C_GRY = "127;132;142" # Grey (Separators)
$ESC = [char]27
$S_ITA = "$ESC[3m" # Italic Start
$S_NIT = "$ESC[23m" # Italic Stop
$S_REV = "$ESC[7m" # Reverse (Invert) Start
$S_NRV = "$ESC[27m" # Reverse (Invert) Stop
# 2. SYMBOL DEFINITIONS
$S_CAP_L = [char]0xE0B6 #  Start Cap (Left rounded)
$S_CAP_R = [char]0xE0B4 #  End Cap (Right rounded)
$S_SEP_L = [char]0xE0B0 #  Segment Separator (Filled arrow)
$S_GAP_R = [char]0xE0D7 #  Gap Separator (Thin right arrow)
$S_ARR_R = [char]0xF054 #  Prompt Arrow (Simple chevron)
$S_ARR_U = [char]0xF062 #  Up Arrow (Error indicator)
$S_PIP_F = [char]0x2503 # ┃ Full Height Heavy Pipe (Separator)
$S_PTH_S = [char]0xF0DA #  Path Separator (Small arrow)
$S_SEP_VR = [char]0x2595 # ▕ Right Light Vertical Bar
$S_SEP_VL = [char]0x258F # ▏ Left Light Vertical Bar
$S_GIT_B = [char]0xE0A0 #  Git Branch Symbol
$S_GIT_A = [char]0x21E1 # ⇡ Git Ahead
$S_GIT_D = [char]0x21E3 # ⇣ Git Behind
$S_TIM_C = [char]0xF017 #  Clock Symbol
$S_ERR_S = [char]0xF00D #  Error Symbol (Cross)
$S_GIT_CLN = [char]::ConvertFromUtf32(0xF0E1E) # 󱓎 Clean State (Custom)
if ($PSVersionTable.PSVersion.Major -lt 6) { $S_GIT_CLN = "?" } # Fallback for older PS
$S_GIT_DRT = $S_ERR_S # Dirty State (Same as Error)
# 3. HELPER FUNCTIONS
function Set-AnsiColor {
param (
[string]$FG, # "R;G;B"
[string]$BG # "R;G;B"
)
$out = ""
if ($FG) { $out += "$ESC[38;2;${FG}m" }
if ($BG) { $out += "$ESC[48;2;${BG}m" }
return $out
}
function Reset-Color {
return "$ESC[0m"
}
# Helper: Prompt Transition (2-Char Solution)
# Usage: prompt_trans [Previous BG] [Next BG]
function Prompt-Trans {
param ($PrevBG, $NextBG)
$out = "$ESC[49m" + (Set-AnsiColor -FG $PrevBG) + $S_SEP_L
$out += "$ESC[49m" + (Set-AnsiColor -FG $NextBG) + $S_GAP_R
return $out
}
# 4. PROMPT FUNCTION
function prompt {
$lastExit = $LASTEXITCODE
# --- Segment: ERROR (Conditional, Detached) ---
$seg_error = ""
if ($null -ne $lastExit -and $lastExit -ne 0) {
$seg_error = (Set-AnsiColor -FG $C_RED) + "$ESC[49m" + $S_CAP_L
$seg_error += (Set-AnsiColor -BG $C_RED -FG $C_TXT) + " $S_ERR_S $lastExit $S_ARR_U "
$seg_error += "$ESC[49m" + (Set-AnsiColor -FG $C_RED) + $S_CAP_R
}
# --- Segment: ENVIRONMENT (Session) ---
# Logic: SSH check
$isSSH = ($null -ne $env:SSH_CLIENT) -or ($null -ne $env:SSH_TTY)
$bg_shell = $C_YEL
if ($isSSH) { $bg_user = $C_ORG } else { $bg_user = $C_YEL }
$fg_user = $C_TXT
$fg_shell = $C_TXT
$current_shell = "pwsh $($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)"
$pure_env_text = ""
if ($env:ANDROID_DATA) {
$pure_env_text = "Termux"
} else {
$userName = $env:USERNAME
if (-not $userName) { $userName = $env:USER }
$hostName = $env:COMPUTERNAME
if (-not $hostName) { $hostName = $env:HOSTNAME }
if (-not $hostName) { $hostName = [System.Environment]::MachineName }
$pure_env_text = "$userName@$hostName"
}
# Content Construction
$env_display = if ($env:ANDROID_DATA) { "Termux" } else {
"$S_ITA$userName" + (Set-AnsiColor -FG $C_GRY -BG $bg_user) + "@" + (Set-AnsiColor -FG $fg_user -BG $bg_user) + "$hostName$S_NIT"
}
$seg_env_part1 = (Set-AnsiColor -FG $bg_user) + "$ESC[49m" + $S_CAP_L + (Set-AnsiColor -BG $bg_user -FG $fg_user) + " $env_display"
$seg_env_sep = (Set-AnsiColor -BG $bg_user -FG $C_TXT) + $S_SEP_VR + (Set-AnsiColor -BG $bg_shell -FG $C_TXT) + $S_SEP_VL
$seg_env_part2 = (Set-AnsiColor -BG $bg_shell -FG $fg_shell) + "$S_ITA$current_shell$S_NIT "
$seg_env = $seg_env_part1 + $seg_env_sep + $seg_env_part2
# --- Second Line Alignment Calculation ---
$n_len = $pure_env_text.Length
# Pad 1: Center HH:MM (length 5) under user@host (offset 2)
$pad1_len = [math]::Floor($n_len / 2)
$pad1 = "".PadLeft($pad1_len)
# Pad 2: Gap between HH:MM and Prompt Char
# Prompt Char at offset 2 + n_len
$pad2_len = (2 + $n_len) - ($pad1_len + 5)
if ($pad2_len -lt 0) { $pad2_len = 0 }
$pad2 = "".PadLeft($pad2_len)
# --- Transition: Environment -> Path ---
$trans_env_path = Prompt-Trans $bg_shell $C_BLU
# --- Segment: PATH ---
$p = $PWD.Path
if ($p.StartsWith($HOME)) { $p = "~" + $p.Substring($HOME.Length) }
$term_width = 80
try { $term_width = $Host.UI.RawUI.WindowSize.Width } catch { }
$max_path_len = 20
if ($term_width -ge 80) {
$max_path_len = [math]::Floor($term_width * 0.90)
} else {
$max_path_len = [math]::Floor($term_width * 0.35)
if ($max_path_len -lt 20) { $max_path_len = 20 }
}
if ($p.Length -gt $max_path_len) { $p = ".." + $p.Substring($p.Length - $max_path_len) }
$p = $p.Replace("\", " $S_PTH_S ").Replace("/", " $S_PTH_S ")
$seg_path = (Set-AnsiColor -BG $C_BLU -FG $C_TXT) + " $p "
# --- Segment: GIT (Conditional) ---
$seg_git = ""
$isGit = git rev-parse --is-inside-work-tree 2>$null
if ($isGit -eq "true") {
$ref = (git symbolic-ref --short HEAD 2>$null)
if (-not $ref) { $ref = (git rev-parse --short HEAD 2>$null) }
$trans_path_git = Prompt-Trans $C_BLU $C_PUR
$body_git = (Set-AnsiColor -BG $C_PUR -FG $C_TXT) + " $S_GIT_B $ref "
$status = (git status --porcelain --ignore-submodules 2>$null)
if ($status) {
$body_git += "$S_GIT_DRT "
$gMod = ($status | Where-Object { $_ -match '^\s*M' } | Measure-Object).Count
$gAdd = ($status | Where-Object { $_ -match '^\?\?' } | Measure-Object).Count
$gDel = ($status | Where-Object { $_ -match '^\s*D' } | Measure-Object).Count
if ($gMod -gt 0) { $body_git += "~$gMod " }
if ($gAdd -gt 0) { $body_git += "+$gAdd " }
if ($gDel -gt 0) { $body_git += "-$gDel " }
} else { $body_git += "$S_GIT_CLN " }
$git_counts = (git rev-list --left-right --count HEAD...@{u} 2>$null)
if ($git_counts) {
$counts = -split $git_counts
$ahead = [int]$counts[0]
$behind = [int]$counts[1]
if ($ahead -gt 0) { $body_git += "$S_GIT_A$ahead " }
if ($behind -gt 0) { $body_git += "$S_GIT_D$behind " }
}
$exit_git = "$ESC[49m" + (Set-AnsiColor -FG $C_PUR) + $S_SEP_L + (Reset-Color)
$seg_git = "${trans_path_git}${body_git}${exit_git}"
} else {
$seg_git = "$ESC[49m" + (Set-AnsiColor -FG $C_BLU) + $S_SEP_L + (Reset-Color)
}
# --- Segment: TIMER (Conditional, Detached) ---
$seg_time = ""
$history = Get-History -Count 1 -ErrorAction SilentlyContinue
if ($history -and $null -ne $history.Duration) {
$dur = $history.Duration
if ($dur.TotalSeconds -ge 2) {
$time_str = if ($dur.TotalHours -ge 1) {
$h = [math]::Floor($dur.TotalHours); "{0}h {1}m" -f $h, $dur.Minutes
} elseif ($dur.TotalMinutes -ge 1) {
"{0:0}m {1:0}s" -f $dur.TotalMinutes, $dur.Seconds
} else {
"{0:N1}s" -f $dur.TotalSeconds
}
$time_str = $time_str.Replace(".", ",")
$seg_time = (Set-AnsiColor -FG $C_YEL) + "$ESC[49m" + $S_CAP_L + (Set-AnsiColor -BG $C_YEL -FG $C_TXT) + " $S_TIM_C $time_str " + "$ESC[49m" + (Set-AnsiColor -FG $C_YEL) + $S_CAP_R
}
}
# --- Segment: PROMPT CHAR (Line 2) ---
$isAdmin = $false
if ($IsWindows) {
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
} else { $isAdmin = (id -u) -eq 0 }
$sym_user = if ($isAdmin) { "#" } else { "$" }
$time_now = Get-Date -Format "HH:mm"
$seg_end = (Reset-Color) + "`n" + $pad1 + (Set-AnsiColor -FG $C_GRY) + $time_now + $pad2 + " " + (Set-AnsiColor -FG $C_BLU) + "$sym_user $S_ARR_R " + (Reset-Color)
# --- Final: PROMPT ASSEMBLY ---
$prefix = ""
try { if ([Console]::CursorTop -gt 0) { $prefix = "`n" } } catch { $prefix = "`n" }
$global:LASTEXITCODE = $lastExit
$top_line = ""
if ($seg_error) { $top_line += $seg_error + " " }
if ($seg_time) { $top_line += $seg_time }
if ($top_line) {
"$prefix$top_line`n`n$seg_env$trans_env_path$seg_path$seg_git$seg_end"
} else {
"$prefix$seg_env$trans_env_path$seg_path$seg_git$seg_end"
}
}
# 5. GLOBAL ERROR CONFIGURATION
$global:LASTEXITCODE = 0
$Host.PrivateData.ErrorForegroundColor = 'Red'
if ($PSStyle) { $PSStyle.Formatting.Error = "$ESC[38;2;${C_RED}m" }
#----- ZSH - CUSTOM PROMPT -----
# Powerline Prompt Configuration
# 1. FORMATTING OPTIONS (Pallete: ~ One Dark Pro)
# Zsh Prompt Expansion for TrueColor (24-bit RGB) support:
# Structure: %F{<Hex>} or %K{<Hex>}
# - %F{...} : Sets Foreground (Text) color
# - %K{...} : Sets Background color
# - Hex : standard RGB Hex string (e.g., '#RRGGBB')
# - %f / %k : Resets foreground / background color to default
# Example: %F{#F44747} sets text color to Red
local C_YEL='#FFD75F' # Yellow (Session BG / Timer FG)
local C_ORG='#FF8700' # Vibrant Orange (Matching YEL intensity)
local C_PUR='#D55FDE' # Purple (Git BG)
local C_BLU='#61AFEF' # Soft Blue (Path BG / Prompt Arrow FG)
local C_RED='#F44747' # Pastel Red (Error BG)
local C_BLK='#000000' # Black (Local Session BG)
local C_WHT='#FFFFFF' # White (Error Text)
local C_TXT='#1C2C34' # Dark Slate (Text Color for colored blocks)
local C_GRY='#7F848E' # Grey (Separators)
local S_ITA=$'%{\e[3m%}' # Italic Start
local S_NIT=$'%{\e[23m%}' # Italic Stop
local S_REV=$'%{\e[7m%}' # Reverse (Invert) Start
local S_NRV=$'%{\e[27m%}' # Reverse (Invert) Stop
# 2. SYMBOL DEFINITIONS
local S_CAP_L=$'\uE0B6' #  Start Cap (Left rounded)
local S_CAP_R=$'\uE0B4' #  End Cap (Right rounded)
local S_SEP_L=$'\uE0B0' #  Segment Separator (Filled arrow)
local S_GAP_R=$'\ue0d7' #  Gap Separator (Thin right arrow)
local S_ARR_R=$'\uf054' #  Prompt Arrow (Simple chevron)
local S_ARR_U=$'\uf062' #  Up Arrow (Error indicator)
local S_PIP_F=$'\u2503' # ┃ Full Height Heavy Pipe (Separator)
local S_PTH_S=$'\uf0da' #  Path Separator (Small arrow)
local S_SEP_VR=$'\u2595' # ▕ Right Light Vertical Bar
local S_SEP_VL=$'\u258F' # ▏ Left Light Vertical Bar
local S_GIT_B=$'\uE0A0' #  Git Branch Symbol
local S_GIT_A=$'\u21E1' # ⇡ Git Ahead
local S_GIT_D=$'\u21E3' # ⇣ Git Behind
local S_TIM_C=$'\uf017' #  Clock Symbol
local S_ERR_S=$'\uf00d' #  Error Symbol (Cross)
local S_GIT_CLN=$'\U000F0E1E' # 󱓎 Clean State (Custom)
local S_GIT_DRT=$S_ERR_S # Dirty State (Same as Error)
# 3. HELPER FUNCTIONS
# Global flag to track first run
typeset -g _PROMPT_FIRST_RUN=1
# Global flag to determine if initial newline is needed based on cursor position
typeset -g _PROMPT_INIT_NEWLINE=0
# Intelligent Cursor Detection at Startup
# Checks if the cursor is currently at Row 1. If not (e.g. Welcome Message), we need a newline.
# Run inside an anonymous function to keep scope clean
() {
# Only run if connected to a terminal
if [[ -t 1 ]]; then
local pos
local row
local col
# Save TTY settings approach caused issues on some systems
# Instead, explicitly toggle the specific settings we need.
# -echo: don't show the response code
# -icanon: raw mode (so we don't need to wait for Enter)
stty -echo -icanon
# Ask terminal for cursor position: ESC [ 6 n
echo -n $'\e[6n' > /dev/tty
# Read response silently, timeout 0.1s, stop at 'R'
# Response format: ESC [ Row ; Col R
read -s -t 0.1 -d R pos < /dev/tty
# Restore TTY settings explicitly
stty echo icanon
# Parse output
# Remove initial ESC [ (which is \e[)
pos="${pos##*$'\e'\[}"
# Split Row;Col
row="${pos%%;*}"
# If valid row and row > 1, prompt is not at top
if [[ -n "$row" && "$row" -gt 1 ]]; then
_PROMPT_INIT_NEWLINE=1
fi
fi
}
# Helper: Prompt Transition (2-Char Solution)
# Usage: prompt_trans [Previous BG] [Next BG]
prompt_trans() {
echo -n "%k%F{$1}${S_SEP_L}%k%F{$2}${S_GAP_R}"
}
# Timer Logic (Pre-exec/cmd hooks)
zmodload zsh/datetime
preexec() { cmd_start=$EPOCHREALTIME; }
precmd() {
local exit_code=$? # Must be the very first line to capture status
# Calculate execution time
local now=$EPOCHREALTIME
local dur=$(( now - ${cmd_start:-$now} ))
unset cmd_start
# Rebuild the prompt dynamically, passing duration and exit code
build_prompt "$dur" "$exit_code"
}
# 4. PROMPT FUNCTION
build_prompt() {
local dur=$1
local exit_code=$2
# --- Segment: ERROR (Conditional, Detached) ---
# Logic: Red BG / Slate Text, pointing up to previous command
local seg_error=""
if [[ $exit_code -ne 0 ]]; then
seg_error="%F{$C_RED}%k${S_CAP_L}%K{$C_RED}%F{$C_TXT} ${S_ERR_S} ${exit_code} ${S_ARR_U} %k%F{$C_RED}${S_CAP_R}%f"
fi
# --- Segment: ENVIRONMENT (Session) ---
# Logic:
# - Local: Yellow BG ($C_YEL) for both parts.
# - SSH: Orange BG ($C_ORG) for User/Host, Yellow BG ($C_YEL) for Shell.
local is_ssh=""
[[ -n "$SSH_CLIENT" || -n "$SSH_TTY" ]] && is_ssh=1
local bg_user
local bg_shell="$C_YEL"
if [[ -n "$is_ssh" ]]; then
bg_user="$C_ORG"
else
bg_user="$C_YEL"
fi
local fg_user="$C_TXT"
local fg_shell="$C_TXT"
local current_shell="${SHELL:t} ${ZSH_VERSION}"
local env_text=""
if [[ -n "$ANDROID_DATA" ]]; then
env_text="Termux"
else
env_text="%n%F{$C_GRY}@%F{$fg_user}%m"
fi
# Construction
# Part 1: Start Cap + User Text
local seg_env_part1="%F{$bg_user}%k${S_CAP_L}%K{$bg_user}%F{$fg_user}${S_ITA} ${env_text}${S_NIT}"
# Part 2: Separator (Dual-bar trick, always)
# This ensures consistent spacing and alignment for both Local and SSH
local seg_env_sep="%K{$bg_user}%F{$C_TXT}${S_SEP_VR}%K{$bg_shell}%F{$C_TXT}${S_SEP_VL}"
# Part 3: Shell Text
local seg_env_part2="%K{$bg_shell}%F{$fg_shell}${S_ITA}${current_shell} ${S_NIT}"
local seg_env="${seg_env_part1}${seg_env_sep}${seg_env_part2}"
# --- Second Line Alignment Calculation ---
local pure_env_text=$([[ -n "$ANDROID_DATA" ]] && echo "Termux" || echo "${(%):-%n@%m}")
local n_len=${#pure_env_text}
# Pad 1: Center HH:MM (length 5) under user@host (starts at offset 2)
# Center of user@host is at: 2 + (n_len / 2)
# Time starts at: Center - 2.5, which is roughly n_len / 2
local pad1_len=$(( n_len / 2 ))
local pad1="${(l:$pad1_len:: :)}"
# Pad 2: Gap between HH:MM and Prompt Char
# Prompt Char should be at offset 2 + n_len (the separator's position)
# Current position after HH:MM: pad1_len + 5
local pad2_len=$(( 2 + n_len - (pad1_len + 5) ))
[[ $pad2_len -lt 0 ]] && pad2_len=0
local pad2="${(l:$pad2_len:: :)}"
# --- Transition: Environment -> Path ---
# From Shell BG (Yellow) -> Soft Blue
local trans_env_path="$(prompt_trans $bg_shell $C_BLU)"
# --- Segment: PATH ---
# Logic: Soft Blue BG / Slate Text with Smart Truncation
# Responsive Layout Strategy:
# - Desktop (>= 80 cols): Relaxed limit (90% width), almost no truncation.
# - Mobile (< 80 cols): Aggressive limit (35% width, min 20 chars) to prevent wrapping.
local term_width=${COLUMNS:-80}
local max_path_len=20
if [[ $term_width -ge 80 ]]; then
max_path_len=$(( term_width * 90 / 100 ))
else
max_path_len=$(( term_width * 35 / 100 ))
[[ $max_path_len -lt 20 ]] && max_path_len=20
fi
local full_path="${(D)PWD}"
local styled_path=""
if [[ ${#full_path} -gt $max_path_len ]]; then
# Keep the last $max_path_len characters and add prefix
local trunc_path="..${full_path: -$max_path_len}"
styled_path="${trunc_path//\// ${S_PTH_S} }"
else
styled_path="${full_path//\// ${S_PTH_S} }"
fi
local seg_path="%K{$C_BLU}%F{$C_TXT} ${styled_path} "
# --- Segment: GIT (Conditional) ---
local seg_git=""
# Check if in Git repo
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
local ref=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)
# Transition: Path (Blue) -> Git (Purple)
local trans_path_git="$(prompt_trans $C_BLU $C_PUR)"
# Body: Purple BG, Slate Text
local body_git="%K{$C_PUR}%F{$C_TXT} ${S_GIT_B} ${ref} "
# Check Clean/Dirty
local status_text=$(git status --porcelain --ignore-submodules 2>/dev/null)
if [[ -n "$status_text" ]]; then
body_git+="${S_GIT_DRT} "
# Count modified, added, deleted files
local gMod=0
local gAdd=0
local gDel=0
# Split status_text by newlines into an array
local -a status_lines
status_lines=("${(@f)status_text}")
for line in $status_lines; do
# Check for Modified (M at start or space-M)
if [[ "$line" == M* || "$line" == " M"* ]]; then ((gMod++)); fi
# Check for Added (??)
if [[ "$line" == \?\?* ]]; then ((gAdd++)); fi
# Check for Deleted (D at start or space-D)
if [[ "$line" == D* || "$line" == " D"* ]]; then ((gDel++)); fi
done
[[ $gMod -gt 0 ]] && body_git+="~${gMod} "
[[ $gAdd -gt 0 ]] && body_git+="+${gAdd} "
[[ $gDel -gt 0 ]] && body_git+="-${gDel} "
else
body_git+="${S_GIT_CLN} "
fi
# Check Upstream Status (Ahead/Behind)
local git_counts=$(git rev-list --left-right --count HEAD...@{u} 2>/dev/null)
if [[ -n "$git_counts" ]]; then
local -a counts
counts=(${=git_counts})
local ahead=${counts[1]}
local behind=${counts[2]}
[[ $ahead -gt 0 ]] && body_git+="${S_GIT_A}${ahead} "
[[ $behind -gt 0 ]] && body_git+="${S_GIT_D}${behind} "
fi
# Exit: Git (Purple) -> Transparent
local exit_git="%k%F{$C_PUR}${S_SEP_L}%f"
seg_git="${trans_path_git}${body_git}${exit_git}"
else
# No Git? Just exit Path (Soft Blue) -> Transparent
seg_git="%k%F{$C_BLU}${S_SEP_L}%f"
fi
# --- Segment: TIMER (Conditional, Detached) ---
# Logic: Yellow BG / Slate Text ($C_TXT) - Capsule style
local seg_time=""
if (( dur >= 2 )); then
local time_str
if (( dur >= 3600 )); then
local h=$(( dur / 3600 ))
local rem=$(( dur % 3600 ))
local m=$(( rem / 60 ))
time_str=$(printf "%dh %dm" $h $m)
elif (( dur >= 60 )); then
time_str=$(printf "%dm %.1fs" $(( dur / 60 )) $(( dur % 60 )))
else
time_str=$(printf "%.1fs" $dur)
fi
time_str="${time_str/./,}"
seg_time="%F{$C_YEL}%k${S_CAP_L}%K{$C_YEL}%F{$C_TXT} ${S_TIM_C} ${time_str} %k%F{$C_YEL}${S_CAP_R}%f"
fi
# --- Segment: PROMPT CHAR (Line 2) ---
# Logic: Newline -> Pad1 -> Time -> Pad2 -> User/Root char -> Soft Blue Arrow
local time_now="%D{%H:%M}"
local prompt_char="%(!.#.$)"
local seg_end="%k%f"$'\n'"${pad1}%F{$C_GRY}${time_now}${pad2} %F{$C_BLU}${prompt_char} ${S_ARR_R} %f"
# --- Final: PROMPT ASSEMBLY ---
# Handle Initial Newline logic using global flags
local prefix=$'\n'
if [[ -n "$_PROMPT_FIRST_RUN" ]]; then
# If it's the first run, check if we detected a "dirty" screen (Welcome msg)
if [[ "$_PROMPT_INIT_NEWLINE" -eq 1 ]]; then
prefix=$'\n'
else
prefix=""
fi
unset _PROMPT_FIRST_RUN
fi
local top_line=""
[[ -n "$seg_error" ]] && top_line+="${seg_error} "
[[ -n "$seg_time" ]] && top_line+="${seg_time}"
# Trim trailing space if only error existed
top_line="${top_line% }"
if [[ -n "$top_line" ]]; then
# TopLine + Newline + MainPrompt
# Note: top_line already contains colors, but we need separation
PROMPT="${prefix}${top_line}"$'\n\n'"${seg_env}${trans_env_path}${seg_path}${seg_git}${seg_end}"
else
PROMPT="${prefix}${seg_env}${trans_env_path}${seg_path}${seg_git}${seg_end}"
fi
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment