Skip to content

Instantly share code, notes, and snippets.

@simbo1905
Created October 13, 2025 12:34
Show Gist options
  • Save simbo1905/adba75543b8857b0714fa12086f8d3bd to your computer and use it in GitHub Desktop.
Save simbo1905/adba75543b8857b0714fa12086f8d3bd to your computer and use it in GitHub Desktop.
iTerm2 terminal color management script
#!/usr/bin/env zsh
# shellcheck shell=bash
# colors - Manage iTerm2 background and foreground colors with state persistence
#
# USAGE:
# colors Load and apply colors from .colors file in current directory.
# If no file exists, show interactive menu to choose from
# predefined colors in ~/.colors_list
#
# colors random Randomly select a background from ~/.colors_list,
# auto-adjust font brightness based on background luminance,
# and save to .colors file
#
# colors new Randomly generate a completely new background color.
# Auto-adjust font brightness based on background luminance.
# Apply immediately but DO NOT save to file.
# Can be called repeatedly (up arrow + enter) to try colors.
# Run 'colors save' to keep it.
#
# colors save Save the currently applied colors to .colors file.
# Use this after 'colors new' when you find a color you like.
#
# colors add Add the current saved color (.colors) to your global list
# (~/.colors_list) for future use in menus.
# Only adds the background color (line 1 of .colors file).
# Use this when you have a local color you want to reuse elsewhere.
#
# colors list Display text list of colors and interactive menu.
# Option to open HTML color palette.
#
# colors font N Set font brightness level (0-5) while keeping current background.
# 0=dark, 5=light.
# Saves both current background and new font to .colors file.
#
# colors restore "BG={r,g,b} FG={r,g,b}"
# Restore a specific color combination from a copy/paste of
# previous output. Saves to .colors file.
#
# STATE FILE:
# $PWD/.colors Stores two lines:
# Line 1: background RGB as {r, g, b}
# Line 2: foreground RGB as {r, g, b}
#
# PREDEFINED COLORS:
# ~/.colors_list Newline-delimited list of background RGB values as {r, g, b}
# Created with default 11 colors if it doesn't exist.
# Can be manually edited to add/remove/reorder colors.
# Use 'colors add' to append current saved color to this list.
#
# INTERACTIVE MENU:
# - Displays numbered list of colors from ~/.colors_list
# - User enters number to select background
# - User enters 0-5 to set font brightness level
# - Saves selection to .colors file and applies immediately
#
# AUTO FONT BRIGHTNESS:
# When using 'random' or 'new' modes, font brightness is automatically set:
# - Dark backgrounds (perceived luminance < 50%) get bright font (level 5)
# - Light backgrounds (perceived luminance >= 50%) get dark font (level 1)
# - Luminance calculated using weighted RGB: 0.299*R + 0.587*G + 0.114*B
#
# RANDOM COLOR GENERATION ('colors new'):
# Generates completely random RGB values (0-65535 per component).
# No intelligence about contrast - just pure randomness.
# Keep hitting up arrow + enter to cycle through options.
#
# TYPICAL WORKFLOW:
# - Try random colors: 'colors new' (repeat until you like one)
# - Save locally: 'colors save'
# - Add to global list: 'colors add' (optional, if you want to reuse it)
# - Review your palette: 'colors list'
# - Each folder can have unique .colors (not added to global list)
# - Global list is for colors you want available everywhere
colors() {
local state_file="$PWD/.colors"
local global_list="$HOME/.colors_list"
local html_file="$PWD/.colors.html"
local mode="${1:-load}"
local subarg="${2:-}"
# Initialize global colors list if it doesn't exist
_colors_init_global_list() {
if [ ! -f "$global_list" ]; then
cat > "$global_list" <<'EOF'
{0, 0, 32768}
{32768, 0, 0}
{0, 16384, 0}
{24576, 4096, 0}
{0, 16384, 16384}
{40960, 0, 0}
{0, 40960, 0}
{0, 0, 40960}
{28672, 12288, 0}
{24576, 0, 24576}
{0, 24576, 24576}
EOF
echo "πŸ“ Created $global_list with default colors"
fi
}
# Convert RGB {r, g, b} to HTML RGB format
_colors_to_rgb() {
local rgb="$1"
# Extract r, g, b from {r, g, b} format - remove braces first, then split by comma
local cleaned
cleaned=$(echo "$rgb" | tr -d '{}')
local r g b
r=$(echo "$cleaned" | awk -F',' '{print $1}' | tr -d ' ')
g=$(echo "$cleaned" | awk -F',' '{print $2}' | tr -d ' ')
b=$(echo "$cleaned" | awk -F',' '{print $3}' | tr -d ' ')
# Convert from 0-65535 to 0-255
r=$((r / 256))
g=$((g / 256))
b=$((b / 256))
echo "rgb($r,$g,$b)"
}
# Calculate perceived luminance (0.0 to 1.0)
_colors_luminance() {
local rgb="$1"
local cleaned
cleaned=$(echo "$rgb" | tr -d '{}')
local r g b
r=$(echo "$cleaned" | awk -F',' '{print $1}' | tr -d ' ')
g=$(echo "$cleaned" | awk -F',' '{print $2}' | tr -d ' ')
b=$(echo "$cleaned" | awk -F',' '{print $3}' | tr -d ' ')
# Weighted luminance formula, normalized to 0-1
awk -v r="$r" -v g="$g" -v b="$b" 'BEGIN {
lum = (0.299 * r + 0.587 * g + 0.114 * b) / 65535
print lum
}'
}
# Get appropriate font color based on background luminance
_colors_auto_font() {
local bg_rgb="$1"
local lum
lum=$(_colors_luminance "$bg_rgb")
# If luminance >= 0.5, use dark font (level 1), else bright font (level 5)
if awk -v l="$lum" 'BEGIN { exit (l >= 0.5) ? 0 : 1 }'; then
echo "{13107, 13107, 13107}" # level 1 (dark)
else
echo "{65535, 65535, 65535}" # level 5 (bright)
fi
}
# Generate HTML color palette
_colors_generate_html() {
cat > "$html_file" << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>Color Palette</title>
<style>
body { font-family: monospace; padding: 20px; }
ol { list-style-position: inside; }
li { margin: 10px 0; }
.swatch {
display: inline-block;
width: 100px;
height: 30px;
border: 1px solid #000;
margin-right: 10px;
vertical-align: middle;
}
</style>
</head>
<body>
<h1>Color Palette</h1>
<ol>
EOF
while IFS= read -r color; do
[ -z "$color" ] && continue
local rgb
rgb=$(_colors_to_rgb "$color")
echo "<li><span class=\"swatch\" style=\"background:${rgb};\"></span> ${rgb} <code>${color}</code></li>" >> "$html_file"
done < "$global_list"
cat >> "$html_file" << 'EOF'
</ol>
</body>
</html>
EOF
echo "βœ… Generated $html_file"
}
# Apply colors to iTerm2
_colors_apply() {
local bg="$1"
local fg="$2"
osascript -e "tell application \"iTerm2\" \
to tell current window \
to set background color of current session to $bg" 2>/dev/null
osascript -e "tell application \"iTerm2\" \
to tell current window \
to set foreground color of current session to $fg" 2>/dev/null
echo "🎨 Applied: BG=$bg FG=$fg"
}
# Save current colors to state file
_colors_save_state() {
local bg="$1"
local fg="$2"
echo "$bg" > "$state_file"
echo "$fg" >> "$state_file"
echo "πŸ’Ύ Saved to $state_file"
}
# Get current background color from state file or from environment
_colors_get_current_bg() {
if [ -n "$_COLORS_TEMP_BG" ]; then
# If we have an unsaved color, use that
echo "$_COLORS_TEMP_BG"
return 0
elif [ -f "$state_file" ]; then
# Otherwise try to read from state file
local bg
bg=$(sed -n '1p' "$state_file")
if [ -n "$bg" ]; then
echo "$bg"
return 0
fi
fi
# If all else fails
echo ""
return 1
}
# Set font brightness directly
_colors_set_font() {
local level="$1"
if ! [[ "$level" =~ ^[0-5]$ ]]; then
echo "❌ Invalid font level. Use 0-5 (0=dark, 5=light)."
return 1
fi
# Get current background color
local current_bg
current_bg=$(_colors_get_current_bg)
if [ -z "$current_bg" ]; then
echo "❌ No background color found. Run 'colors' or 'colors new' first."
return 1
fi
# Calculate font color
local fraction
local value
fraction=$(awk -v l="$level" 'BEGIN { print l / 5 }')
value=$(awk -v f="$fraction" 'BEGIN { printf "%.0f", 65535 * f }')
local new_fg="{${value}, ${value}, ${value}}"
# Apply and save
_colors_apply "$current_bg" "$new_fg"
_colors_save_state "$current_bg" "$new_fg"
# Clear temp variables since we saved
unset _COLORS_TEMP_BG
unset _COLORS_TEMP_FG
}
# Restore specific colors from pasted string
_colors_restore() {
local color_string="$1"
if [ -z "$color_string" ]; then
echo "❌ Missing color string. Format: BG={r,g,b} FG={r,g,b}"
return 1
fi
# Extract BG and FG from the string
local bg fg
# Extract directly without regex - more reliable
bg=$(echo "$color_string" | grep -o 'BG={[0-9]*, [0-9]*, [0-9]*}' | cut -d'=' -f2)
fg=$(echo "$color_string" | grep -o 'FG={[0-9]*, [0-9]*, [0-9]*}' | cut -d'=' -f2)
if [ -z "$bg" ] || [ -z "$fg" ]; then
echo "❌ Invalid format. Use: BG={r,g,b} FG={r,g,b}"
return 1
fi
_colors_apply "$bg" "$fg"
_colors_save_state "$bg" "$fg"
}
# Show text list and interactive menu
_colors_show_list_menu() {
_colors_init_global_list
echo "🎨 Available background colors:"
echo
local i=1
while IFS= read -r color; do
[ -z "$color" ] && continue
printf "%2d) %s\n" "$i" "$color"
((i++))
done < "$global_list"
echo
echo "Press number to set, 'y' to open HTML view, or any other key to exit:"
local choice
read -r choice
# Handle 'y' to open HTML view
if [[ "$choice" == "y" ]]; then
_colors_generate_html
open "$html_file"
echo
echo "Press number to set color or any other key to exit:"
read -r choice
fi
# Process number selection
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then
local selected_bg
selected_bg=$(sed -n "${choice}p" "$global_list")
echo
echo "Select font brightness (0-5):"
local font_level
read -r font_level
if ! [[ "$font_level" =~ ^[0-5]$ ]]; then
echo "❌ Invalid font level"
return 1
fi
# Calculate font color
local fraction
local value
fraction=$(awk -v l="$font_level" 'BEGIN { print l / 5 }')
value=$(awk -v f="$fraction" 'BEGIN { printf "%.0f", 65535 * f }')
local selected_fg="{${value}, ${value}, ${value}}"
_colors_apply "$selected_bg" "$selected_fg"
_colors_save_state "$selected_bg" "$selected_fg"
else
echo "No change."
fi
}
# Handle different modes
case "$mode" in
load)
if [ -f "$state_file" ]; then
local bg
local fg
bg=$(sed -n '1p' "$state_file")
fg=$(sed -n '2p' "$state_file")
if [ -z "$bg" ] || [ -z "$fg" ]; then
echo "❌ State file is corrupted"
return 1
fi
_colors_apply "$bg" "$fg"
else
echo "πŸ“‚ No .colors file found in current directory"
echo
_colors_show_list_menu
fi
;;
random)
_colors_init_global_list
# Count lines in global list
local count
count=$(grep -cv '^[[:space:]]*$' "$global_list")
if [ "$count" -eq 0 ]; then
echo "❌ No colors in $global_list"
return 1
fi
# Pick random line
local rand=$((RANDOM % count + 1))
local bg
local fg
bg=$(sed -n "${rand}p" "$global_list")
fg=$(_colors_auto_font "$bg")
_colors_apply "$bg" "$fg"
_colors_save_state "$bg" "$fg"
;;
new)
# Generate completely random RGB
local r=$((RANDOM % 65536))
local g=$((RANDOM % 65536))
local b=$((RANDOM % 65536))
local bg="{${r}, ${g}, ${b}}"
local fg
fg=$(_colors_auto_font "$bg")
_colors_apply "$bg" "$fg"
# Store in memory for potential save, but don't write to file
export _COLORS_TEMP_BG="$bg"
export _COLORS_TEMP_FG="$fg"
echo "πŸ’‘ Color applied but not saved. Run 'colors save' to keep it."
;;
save)
# Check if we have temp colors from 'colors new'
if [ -n "$_COLORS_TEMP_BG" ] && [ -n "$_COLORS_TEMP_FG" ]; then
_colors_save_state "$_COLORS_TEMP_BG" "$_COLORS_TEMP_FG"
unset _COLORS_TEMP_BG
unset _COLORS_TEMP_FG
else
echo "❌ No unsaved colors to save. Use 'colors new' first."
return 1
fi
;;
add)
if [ ! -f "$state_file" ]; then
echo "❌ No .colors file in current directory"
return 1
fi
local bg
bg=$(sed -n '1p' "$state_file")
if [ -z "$bg" ]; then
echo "❌ State file is empty or corrupted"
return 1
fi
_colors_init_global_list
# Check if color already exists
if grep -Fxq "$bg" "$global_list"; then
echo "ℹ️ Color already exists in global list"
return 0
fi
echo "$bg" >> "$global_list"
echo "βœ… Added $bg to $global_list"
;;
list)
_colors_init_global_list
_colors_show_list_menu
;;
font)
if [ -z "$subarg" ]; then
echo "❌ Missing font level. Usage: colors font [0-5]"
return 1
fi
_colors_set_font "$subarg"
;;
restore)
if [ -z "$subarg" ]; then
echo "❌ Missing color string. Usage: colors restore \"BG={r,g,b} FG={r,g,b}\""
return 1
fi
_colors_restore "$subarg"
;;
*)
echo "❌ Unknown command: $mode"
echo
echo "Usage:"
echo " colors - Load .colors or show menu"
echo " colors random - Random color from list"
echo " colors new - Generate random color (not saved)"
echo " colors save - Save last generated color"
echo " colors add - Add current color to global list"
echo " colors list - Show text list and menu"
echo " colors font N - Set font brightness (0-5), keeps current background"
echo " colors restore \"BG={r,g,b} FG={r,g,b}\" - Restore specific colors"
return 1
;;
esac
}
@simbo1905
Copy link
Author

i have this as ~/colors.sh which I load as source ~/colors.sh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment