Skip to content

Instantly share code, notes, and snippets.

@Tamas-Toth-ebola
Last active January 11, 2026 12:37
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

(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:
    • Shows the shell name with full version (e.g. pwsh 7.5.4).
    • Displays user@host context.
    • SSH Aware: Colors dynamically shift to high-contrast Yellow if an active SSH session is detected.
  • 📂 Path:
    • 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 / 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 }

This project was enhanced with the assistance of Google Gemini 3.

# AI assistance | context
GEMINI.md
# Images
*.png
*.jpg
*.jpeg
*.gif
*.bmp
*.tiff
*.webp
# Temporary scripts
pwsh-fix
#----- 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 = "246;118;35" # Orange (Unused - Preservation)
$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_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)
# %k%F{$1}${S_SEP_L}%k%F{$2}${S_GAP_R}
# Reset BG, FG=PrevBG, SEP_L, Reset BG, FG=NextBG, GAP_R
$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) ---
# Logic: Appears at the end of previous output, pointing up
$seg_error = ""
if ($null -ne $lastExit -and $lastExit -ne 0) {
# Structure: CapL + RedBG(Icon + Code + UpArrow) + CapR
$seg_error = (Set-AnsiColor -FG $C_RED) + "$ESC[49m" + $S_CAP_L # NoBG
$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 # NoBG
}
# --- Segment: ENVIRONMENT (Session) ---
# Logic: SSH check (SSH_CLIENT or SSH_TTY env vars)
$isSSH = ($null -ne $env:SSH_CLIENT) -or ($null -ne $env:SSH_TTY)
if ($isSSH) { $e_bg = $C_YEL; $e_fg = $C_TXT } else { $e_bg = $C_BLK; $e_fg = $C_YEL }
$current_shell = "pwsh $($PSVersionTable.PSVersion.ToString())"
$env_text = ""
if ($env:ANDROID_DATA) {
$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 }
$env_text = "$S_ITA $userName" + (Set-AnsiColor -FG $C_GRY -BG $e_bg) + "@" + (Set-AnsiColor -FG $e_fg -BG $e_bg) + "$hostName "
}
# %F{$e_bg}%k${S_CAP_L}%K{$e_bg}%F{$e_fg}${S_ITA} %n%F{$C_GRY}@%F{$e_fg}%m %F{$C_TXT}${S_PIP_F}%F{$e_fg} ${current_shell} ${S_NIT}
$seg_env = (Set-AnsiColor -FG $e_bg) + "$ESC[49m" + $S_CAP_L
$seg_env += (Set-AnsiColor -BG $e_bg -FG $e_fg) + $env_text
# Perfect Cut (Inverse Method): FG=SessionColor, BG=Default, then Invert.
# Result: BG=SessionColor, FG=TerminalDefaultBG (Transparent!)
# FIX: Stop Italic before pipe, Restart after.
$seg_env += (Set-AnsiColor -FG $e_bg) + "$ESC[49m" + $S_REV + "$S_NIT" + "$S_PIP_F" + "$S_NRV"
$seg_env += (Set-AnsiColor -FG $e_fg -BG $e_bg) + $S_ITA + " $current_shell " + $S_NIT
# --- Transition: Environment -> Path ---
$trans_env_path = Prompt-Trans $e_bg $C_BLU
# --- Segment: PATH ---
# Logic: Soft Blue BG / Slate Text with Smart Truncation
# Shorten path: Replace Home with ~ and separators with S_PTH_S
$p = $PWD.Path
if ($p.StartsWith($HOME)) {
$p = "~" + $p.Substring($HOME.Length)
}
# Smart Truncation
$max_path_len = 24
if ($p.Length -gt $max_path_len) {
$p = ".." + $p.Substring($p.Length - $max_path_len)
}
# Replace separators (both \ and / just in case)
$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 = ""
# Better check: is-inside-work-tree
$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) }
# Transition: Path (Blue) -> Git (Purple)
$trans_path_git = Prompt-Trans $C_BLU $C_PUR
# Body: Purple BG, Slate Text
$body_git = (Set-AnsiColor -BG $C_PUR -FG $C_TXT) + " $S_GIT_B $ref "
# Check Clean/Dirty
$status = (git status --porcelain --ignore-submodules 2>$null)
if ($status) {
$body_git += "$S_GIT_DRT "
# Calculate File Counts (Modified, Added, Deleted)
$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 "
}
# Check Upstream Status (Ahead/Behind)
$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 (Purple) -> Transparent
# %k%F{$C_PUR}${S_SEP_L}%f
$exit_git = "$ESC[49m" + (Set-AnsiColor -FG $C_PUR) + $S_SEP_L + (Reset-Color)
$seg_git = "${trans_path_git}${body_git}${exit_git}"
} else {
# No Git? Just exit Path (Soft Blue) -> Transparent
$seg_git = "$ESC[49m" + (Set-AnsiColor -FG $C_BLU) + $S_SEP_L + (Reset-Color)
}
# --- Segment: TIMER (Conditional, Detached) ---
$seg_time = ""
# Get duration of last command from history
$history = Get-History -Count 1 -ErrorAction SilentlyContinue
if ($history -and $null -ne $history.Duration) {
$dur = $history.Duration
if ($dur.TotalSeconds -ge 2) {
# Format: Hours, Minutes, or Seconds depending on duration
$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(".", ",")
# Structure: CapL + YellowBG(Icon + Time) + CapR
$seg_time = (Set-AnsiColor -FG $C_YEL) + "$ESC[49m" + $S_CAP_L # NoBG
$seg_time += (Set-AnsiColor -BG $C_YEL -FG $C_TXT) + " $S_TIM_C $time_str "
$seg_time += "$ESC[49m" + (Set-AnsiColor -FG $C_YEL) + $S_CAP_R # NoBG
}
}
# --- Segment: PROMPT CHAR (Line 2) ---
# Logic: Newline -> Indent -> User/Root char -> Soft Blue Arrow
$isAdmin = $false
if ($IsWindows) {
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
} else {
# On Linux/macOS, check if effective user ID is 0 (root)
$isAdmin = (id -u) -eq 0
}
$sym_user = if ($isAdmin) { "#" } else { "$" }
# Fix: Add 2 spaces indent for visual balance
# Fix: Reset color before newline to prevent background bleed on wrap
$seg_end = (Reset-Color) + "`n " + (Set-AnsiColor -FG $C_BLU) + "$sym_user $S_ARR_R " + (Reset-Color)
# --- Final: PROMPT ASSEMBLY ---
# 1. Handle Initial Newline: Check Cursor Position
# If CursorTop > 0, it means there is content (Welcome message, previous output), so we need a newline.
# If CursorTop == 0, we are at the top (Clear), no newline needed.
$prefix = ""
try {
if ([Console]::CursorTop -gt 0) { $prefix = "`n" }
} catch {
# Fallback if host doesn't support CursorTop
$prefix = "`n"
}
# 2. Restore LASTEXITCODE
$global:LASTEXITCODE = $lastExit
# Logic:
# Top Line: [Error] [Time] (If any exists)
# Main Line: [Env]...
$top_line = ""
if ($seg_error) { $top_line += $seg_error + " " }
if ($seg_time) { $top_line += $seg_time }
# If we have a top line (Error or Time), we print it, then double newline, then main prompt
# Wait: The prefix (conditional newline) should go BEFORE the top line.
if ($top_line) {
# Trim trailing space if only error existed
"$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
# Reset error state from startup to prevent initial error segment
$global:LASTEXITCODE = 0
# Set error text color to match prompt Error Background
$Host.PrivateData.ErrorForegroundColor = 'Red'
if ($PSStyle) {
# Use the defined Red color for error messages
$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='#F67623' # Orange (Unused - Preservation)
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_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:
# - SSH: Yellow BG ($C_YEL), Slate Text ($C_TXT)
# - Local: Black BG ($C_BLK), Yellow Text ($C_YEL)
local e_bg=$([[ -n "$SSH_CLIENT" || -n "$SSH_TTY" ]] && echo "$C_YEL" || echo "$C_BLK")
local e_fg=$([[ -n "$SSH_CLIENT" || -n "$SSH_TTY" ]] && echo "$C_TXT" || echo "$C_YEL")
local current_shell="${SHELL:t} ${ZSH_VERSION}"
# Separator Logic: Inverse Method for perfect transparency match.
# FG=$e_bg (becomes BG), BG=Default (becomes FG/PipeColor).
# FIX: Disable Italic ($S_NIT) before pipe, Re-enable ($S_ITA) after pipe.
local env_text=""
if [[ -n "$ANDROID_DATA" ]]; then
env_text="Termux"
else
env_text="%n%F{$C_GRY}@%F{$e_fg}%m"
fi
local seg_env="%F{$e_bg}%k${S_CAP_L}%K{$e_bg}%F{$e_fg}${S_ITA} ${env_text} ${S_NIT}%F{$e_bg}%k${S_REV}${S_PIP_F}${S_NRV}%K{$e_bg}%F{$e_fg}${S_ITA} ${current_shell} ${S_NIT}"
# --- Transition: Environment -> Path ---
# From Env BG -> Soft Blue
local trans_env_path="$(prompt_trans $e_bg $C_BLU)"
# --- Segment: PATH ---
# Logic: Soft Blue BG / Slate Text with Smart Truncation
local max_path_len=24
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 -> Indent (Space) -> User/Root char -> Soft Blue Arrow
# %(!.#.$) checks effective UID: # for root, $ for user
# Fix: Prepend %k%f to ensure newline doesn't carry background color (prevents bleed)
# Added 2 spaces before %(!.#.$) for visual balance
local seg_end="%k%f"$'\n '"%F{$C_BLU}%(!.#.$) ${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