Created
October 13, 2025 12:34
-
-
Save simbo1905/adba75543b8857b0714fa12086f8d3bd to your computer and use it in GitHub Desktop.
iTerm2 terminal color management script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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 | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
i have this as
~/colors.shwhich I load assource ~/colors.sh