#!/usr/bin/env bash ############################################################################# # CSS Color Palette Generator (Pure Bash) # # Generate comprehensive color palettes with tints, shades, and tones # No Node.js required - pure bash implementation # # Usage: # css_color_palette.sh [OPTIONS] # # Arguments: # COLOR Base hex color (e.g., #3498db, 3498db) # # Options: # -p, --palette TYPE Palette type: monochromatic, analogous, complementary, # split-complementary, triadic, tetradic (default: monochromatic) # -o, --output FILE Output file (default: ./colors.yaml) # -m, --mode MODE Color mode: light, dark (default: light) # -s, --style STYLE Generate style variations: shades, tints, tones, all # -n, --name NAME Color palette name (default: auto-generated) # --scales N Number of scale steps (default: 11) # -i, --interactive Interactive mode # -v, --verbose Verbose output with color preview # -h, --help Show this help message # ############################################################################# set -euo pipefail # ============================================================================ # Color Definitions # ============================================================================ RED="" GREEN="" YELLOW="" BLUE="" MAGENTA="" CYAN="" BOLD="" DIM="" RESET="" COLORS=0 if [[ -t 1 ]] && command -v tput >/dev/null 2>&1; then COLORS=$(tput colors 2>/dev/null || echo 0) if [[ ${COLORS:-0} -ge 8 ]]; then RED=$(tput setaf 1 2>/dev/null || echo "") GREEN=$(tput setaf 2 2>/dev/null || echo "") YELLOW=$(tput setaf 3 2>/dev/null || echo "") BLUE=$(tput setaf 4 2>/dev/null || echo "") MAGENTA=$(tput setaf 5 2>/dev/null || echo "") CYAN=$(tput setaf 6 2>/dev/null || echo "") BOLD=$(tput bold 2>/dev/null || echo "") DIM=$(tput dim 2>/dev/null || echo "") RESET=$(tput sgr0 2>/dev/null || echo "") fi fi # ============================================================================ # Global Variables # ============================================================================ BASE_COLOR="" PALETTE_TYPE="monochromatic" OUTPUT_FILE="./colors.yaml" COLOR_MODE="light" STYLE_TYPE="all" PALETTE_NAME="" SCALE_STEPS=11 INTERACTIVE=false VERBOSE=false # Associative arrays for storing palette data declare -A PALETTE_DATA declare -a COLOR_GROUPS # ============================================================================ # Helper Functions # ============================================================================ print_usage() { cat << EOF ${BOLD}CSS Color Palette Generator (Pure Bash)${RESET} Generate comprehensive color palettes without Node.js dependencies. ${BOLD}USAGE:${RESET} $(basename "$0") COLOR [OPTIONS] ${BOLD}ARGUMENTS:${RESET} COLOR Base hex color (e.g., #3498db, 3498db) ${BOLD}OPTIONS:${RESET} -p, --palette TYPE Palette type: monochromatic, analogous, complementary, split-complementary, triadic, tetradic -o, --output FILE Output file (default: ./colors.yaml) -m, --mode MODE Color mode: light, dark (default: light) -s, --style STYLE Style: shades, tints, tones, all (default: all) -n, --name NAME Palette name (default: auto-generated) --scales N Number of scale steps (default: 11) -i, --interactive Interactive mode -v, --verbose Verbose output with color preview -h, --help Show this help message ${BOLD}DEPENDENCIES:${RESET} bc For floating-point arithmetic ${BOLD}EXAMPLES:${RESET} $(basename "$0") "#3498db" $(basename "$0") "#3498db" -p triadic -o palette.json $(basename "$0") "ff5733" -p analogous -m dark EOF } error() { echo "${RED}${BOLD}Error:${RESET} $1" >&2 exit 1 } info() { echo "${BLUE}${BOLD}==>${RESET} $1" } success() { echo "${GREEN}${BOLD}[OK]${RESET} $1" } warning() { echo "${YELLOW}${BOLD}[WARN]${RESET} $1" } # Check for bc dependency check_dependencies() { if ! command -v bc >/dev/null 2>&1; then error "bc is required but not found. Please install bc (apt-get install bc)" fi } # ============================================================================ # Math Utilities # ============================================================================ # Floating point comparison bc_calc() { local expr="$1" # Remove any leading/trailing whitespace expr=$(echo "$expr" | tr -d ' ') # Check if expression is empty if [[ -z "$expr" ]]; then echo "0" return fi echo "scale=6; $expr" | bc -l 2>/dev/null || echo "0" } # Boolean bc comparison (returns 0 for true, 1 for false) bc_compare() { local result=$(echo "$1" | bc -l 2>/dev/null || echo "0") [[ "$result" == "1" ]] } min() { local a=$1 b=$2 if bc_compare "$a < $b"; then echo "$a" else echo "$b" fi } max() { local a=$1 b=$2 if bc_compare "$a > $b"; then echo "$a" else echo "$b" fi } clamp() { local val=$1 min_val=$2 max_val=$3 val=$(max "$val" "$min_val") val=$(min "$val" "$max_val") echo "$val" } round() { LC_NUMERIC=C printf "%.0f" "$1" } # ============================================================================ # Color Validation and Normalization # ============================================================================ validate_hex() { local hex="$1" hex="${hex#\#}" if [[ "$hex" =~ ^[0-9A-Fa-f]{3}$ ]] || [[ "$hex" =~ ^[0-9A-Fa-f]{6}$ ]]; then return 0 fi return 1 } normalize_hex() { local hex="$1" hex="${hex#\#}" # Expand shorthand if [[ ${#hex} -eq 3 ]]; then hex="${hex:0:1}${hex:0:1}${hex:1:1}${hex:1:1}${hex:2:1}${hex:2:1}" fi echo "#${hex^^}" } # ============================================================================ # Color Conversion Functions # ============================================================================ # Convert hex to RGB (returns "r g b") hex_to_rgb() { local hex="$1" hex="${hex#\#}" if [[ ${#hex} -eq 3 ]]; then hex="${hex:0:1}${hex:0:1}${hex:1:1}${hex:1:1}${hex:2:1}${hex:2:1}" fi local r=$((16#${hex:0:2})) local g=$((16#${hex:2:2})) local b=$((16#${hex:4:2})) echo "$r $g $b" } # Convert RGB to hex rgb_to_hex() { local r=$(round "$1") local g=$(round "$2") local b=$(round "$3") # Clamp values r=$(clamp "$r" 0 255) g=$(clamp "$g" 0 255) b=$(clamp "$b" 0 255) # Use C locale to ensure proper number formatting LC_NUMERIC=C printf "#%02X%02X%02X" "$r" "$g" "$b" } # Convert RGB to HSL (returns "h s l") rgb_to_hsl() { local r=$1 g=$2 b=$3 # Normalize to 0-1 r=$(bc_calc "$r / 255") g=$(bc_calc "$g / 255") b=$(bc_calc "$b / 255") local max=$(max "$r" "$(max "$g" "$b")") local min=$(min "$r" "$(min "$g" "$b")") local delta=$(bc_calc "$max - $min") local h=0 s=0 l l=$(bc_calc "($max + $min) / 2") if bc_compare "$delta != 0"; then # Calculate saturation if bc_compare "$l < 0.5"; then s=$(bc_calc "$delta / ($max + $min)") else s=$(bc_calc "$delta / (2 - $max - $min)") fi # Calculate hue if bc_compare "$max == $r"; then h=$(bc_calc "(($g - $b) / $delta) + (if ($g < $b) then 6 else 0)") elif bc_compare "$max == $g"; then h=$(bc_calc "(($b - $r) / $delta) + 2") else h=$(bc_calc "(($r - $g) / $delta) + 4") fi h=$(bc_calc "$h / 6") fi # Convert to degrees and percentages h=$(bc_calc "$h * 360") s=$(bc_calc "$s * 100") l=$(bc_calc "$l * 100") echo "$h $s $l" } # Helper for HSL to RGB conversion hue_to_rgb() { local p=$1 q=$2 t=$3 # Normalize t to 0-1 if bc_compare "$t < 0"; then t=$(bc_calc "$t + 1") fi if bc_compare "$t > 1"; then t=$(bc_calc "$t - 1") fi if bc_compare "$t < 0.166666"; then echo "$(bc_calc "$p + ($q - $p) * 6 * $t")" elif bc_compare "$t < 0.5"; then echo "$q" elif bc_compare "$t < 0.666666"; then echo "$(bc_calc "$p + ($q - $p) * (0.666666 - $t) * 6")" else echo "$p" fi } # Convert HSL to RGB (returns "r g b") hsl_to_rgb() { local h=$1 s=$2 l=$3 # Normalize h=$(bc_calc "$h / 360") s=$(bc_calc "$s / 100") l=$(bc_calc "$l / 100") local r g b if bc_compare "$s == 0"; then # Achromatic (gray) r=$l g=$l b=$l else local q if bc_compare "$l < 0.5"; then q=$(bc_calc "$l * (1 + $s)") else q=$(bc_calc "$l + $s - $l * $s") fi local p=$(bc_calc "2 * $l - $q") r=$(hue_to_rgb "$p" "$q" "$(bc_calc "$h + 0.333333")") g=$(hue_to_rgb "$p" "$q" "$h") b=$(hue_to_rgb "$p" "$q" "$(bc_calc "$h - 0.333333")") fi # Convert to 0-255 r=$(bc_calc "$r * 255") g=$(bc_calc "$g * 255") b=$(bc_calc "$b * 255") echo "$r $g $b" } # ============================================================================ # Color Manipulation Functions # ============================================================================ # Adjust hue (degrees) adjust_hue() { local h=$1 adjustment=$2 h=$(bc_calc "$h + $adjustment") # Normalize to 0-360 while bc_compare "$h < 0"; do h=$(bc_calc "$h + 360") done while bc_compare "$h >= 360"; do h=$(bc_calc "$h - 360") done echo "$h" } # Generate tint (mix with white) generate_tint() { local hex="$1" local percentage=$2 read -r r g b <<< "$(hex_to_rgb "$hex")" r=$(bc_calc "$r + (255 - $r) * ($percentage / 100)") g=$(bc_calc "$g + (255 - $g) * ($percentage / 100)") b=$(bc_calc "$b + (255 - $b) * ($percentage / 100)") rgb_to_hex "$r" "$g" "$b" } # Generate shade (mix with black) generate_shade() { local hex="$1" local percentage=$2 read -r r g b <<< "$(hex_to_rgb "$hex")" r=$(bc_calc "$r * (1 - $percentage / 100)") g=$(bc_calc "$g * (1 - $percentage / 100)") b=$(bc_calc "$b * (1 - $percentage / 100)") rgb_to_hex "$r" "$g" "$b" } # Generate tone (mix with gray) generate_tone() { local hex="$1" local percentage=$2 read -r r g b <<< "$(hex_to_rgb "$hex")" local gray=$(bc_calc "($r + $g + $b) / 3") r=$(bc_calc "$r + ($gray - $r) * ($percentage / 100)") g=$(bc_calc "$g + ($gray - $g) * ($percentage / 100)") b=$(bc_calc "$b + ($gray - $b) * ($percentage / 100)") rgb_to_hex "$r" "$g" "$b" } # Adjust lightness adjust_lightness() { local hex="$1" local adjustment=$2 read -r r g b <<< "$(hex_to_rgb "$hex")" read -r h s l <<< "$(rgb_to_hsl "$r" "$g" "$b")" l=$(bc_calc "$l + $adjustment") l=$(clamp "$l" 0 100) read -r r g b <<< "$(hsl_to_rgb "$h" "$s" "$l")" rgb_to_hex "$r" "$g" "$b" } # Adjust saturation adjust_saturation() { local hex="$1" local adjustment=$2 read -r r g b <<< "$(hex_to_rgb "$hex")" read -r h s l <<< "$(rgb_to_hsl "$r" "$g" "$b")" s=$(bc_calc "$s + $adjustment") s=$(clamp "$s" 0 100) read -r r g b <<< "$(hsl_to_rgb "$h" "$s" "$l")" rgb_to_hex "$r" "$g" "$b" } # ============================================================================ # Scale Generation # ============================================================================ generate_color_scale() { local base_hex="$1" local group_name="$2" local style="$3" local -a scale_values=(50 100 200 300 400 500 600 700 800 900 950) local base_index=5 # 500 is the base # Set base color PALETTE_DATA["${group_name}.500"]="$base_hex" # Generate lighter variations (50-400) for i in {4..0}; do local step=$((base_index - i)) local scale_val=${scale_values[$i]} local color if [[ "$style" == "tints" ]]; then local percentage=$(bc_calc "$step / $base_index * 85") color=$(generate_tint "$base_hex" "$percentage") elif [[ "$style" == "tones" ]]; then read -r r g b <<< "$(hex_to_rgb "$base_hex")" read -r h s l <<< "$(rgb_to_hsl "$r" "$g" "$b")" local new_l=$(bc_calc "95 - $i * 8") local new_s=$(bc_calc "$(max 10 "$(bc_calc "$s - ($base_index - $i) * 5")")") read -r r g b <<< "$(hsl_to_rgb "$h" "$new_s" "$new_l")" color=$(rgb_to_hex "$r" "$g" "$b") else # Default: lighten local adjustment=$(bc_calc "$step * 12") color=$(adjust_lightness "$base_hex" "$adjustment") if [[ $i -le 2 ]]; then local sat_adj=$(bc_calc "-($base_index - $i) * 8") color=$(adjust_saturation "$color" "$sat_adj") fi fi PALETTE_DATA["${group_name}.${scale_val}"]="$color" done # Generate darker variations (600-950) for i in {6..10}; do local step=$((i - base_index)) local scale_val=${scale_values[$i]} local color if [[ "$style" == "shades" ]]; then local percentage=$(bc_calc "$step / (${#scale_values[@]} - $base_index - 1) * 75") color=$(generate_shade "$base_hex" "$percentage") elif [[ "$style" == "tones" ]]; then read -r r g b <<< "$(hex_to_rgb "$base_hex")" read -r h s l <<< "$(rgb_to_hsl "$r" "$g" "$b")" local new_l=$(bc_calc "45 - ($i - $base_index) * 7") new_l=$(max 5 "$new_l") local new_s=$(bc_calc "$(max 10 "$(bc_calc "$s - $step * 3")")") read -r r g b <<< "$(hsl_to_rgb "$h" "$new_s" "$new_l")" color=$(rgb_to_hex "$r" "$g" "$b") else # Default: darken local adjustment=$(bc_calc "-$step * 10") color=$(adjust_lightness "$base_hex" "$adjustment") if [[ $i -ge 9 ]]; then local sat_adj=$(bc_calc "-$step * 5") color=$(adjust_saturation "$color" "$sat_adj") fi fi PALETTE_DATA["${group_name}.${scale_val}"]="$color" done } # ============================================================================ # Palette Generation # ============================================================================ generate_palette() { local base_hex="$1" local palette_type="$2" local style="$3" read -r r g b <<< "$(hex_to_rgb "$base_hex")" read -r h s l <<< "$(rgb_to_hsl "$r" "$g" "$b")" case "$palette_type" in monochromatic) COLOR_GROUPS=("primary") generate_color_scale "$base_hex" "primary" "$style" ;; analogous) COLOR_GROUPS=("primary" "analogous1" "analogous2") generate_color_scale "$base_hex" "primary" "$style" # Analogous 1: -30 degrees local h1=$(adjust_hue "$h" -30) read -r r g b <<< "$(hsl_to_rgb "$h1" "$s" "$l")" local color1=$(rgb_to_hex "$r" "$g" "$b") generate_color_scale "$color1" "analogous1" "$style" # Analogous 2: +30 degrees local h2=$(adjust_hue "$h" 30) read -r r g b <<< "$(hsl_to_rgb "$h2" "$s" "$l")" local color2=$(rgb_to_hex "$r" "$g" "$b") generate_color_scale "$color2" "analogous2" "$style" ;; complementary) COLOR_GROUPS=("primary" "complement") generate_color_scale "$base_hex" "primary" "$style" # Complement: 180 degrees local hc=$(adjust_hue "$h" 180) read -r r g b <<< "$(hsl_to_rgb "$hc" "$s" "$l")" local colorc=$(rgb_to_hex "$r" "$g" "$b") generate_color_scale "$colorc" "complement" "$style" ;; split-complementary) COLOR_GROUPS=("primary" "split1" "split2") generate_color_scale "$base_hex" "primary" "$style" # Split 1: 150 degrees local hs1=$(adjust_hue "$h" 150) read -r r g b <<< "$(hsl_to_rgb "$hs1" "$s" "$l")" local colors1=$(rgb_to_hex "$r" "$g" "$b") generate_color_scale "$colors1" "split1" "$style" # Split 2: 210 degrees local hs2=$(adjust_hue "$h" 210) read -r r g b <<< "$(hsl_to_rgb "$hs2" "$s" "$l")" local colors2=$(rgb_to_hex "$r" "$g" "$b") generate_color_scale "$colors2" "split2" "$style" ;; triadic) COLOR_GROUPS=("primary" "triadic1" "triadic2") generate_color_scale "$base_hex" "primary" "$style" # Triadic 1: 120 degrees local ht1=$(adjust_hue "$h" 120) read -r r g b <<< "$(hsl_to_rgb "$ht1" "$s" "$l")" local colort1=$(rgb_to_hex "$r" "$g" "$b") generate_color_scale "$colort1" "triadic1" "$style" # Triadic 2: 240 degrees local ht2=$(adjust_hue "$h" 240) read -r r g b <<< "$(hsl_to_rgb "$ht2" "$s" "$l")" local colort2=$(rgb_to_hex "$r" "$g" "$b") generate_color_scale "$colort2" "triadic2" "$style" ;; tetradic) COLOR_GROUPS=("primary" "tetradic1" "tetradic2" "tetradic3") generate_color_scale "$base_hex" "primary" "$style" # Tetradic colors: 90, 180, 270 degrees for deg in 90 180 270; do local idx=$((deg / 90)) local hn=$(adjust_hue "$h" "$deg") read -r r g b <<< "$(hsl_to_rgb "$hn" "$s" "$l")" local colorn=$(rgb_to_hex "$r" "$g" "$b") generate_color_scale "$colorn" "tetradic${idx}" "$style" done ;; esac } # ============================================================================ # Output Functions # ============================================================================ generate_yaml_output() { local name="$1" local type="$2" local mode="$3" local style="$4" local base="$5" cat << EOF name: '$name' type: '$type' mode: '$mode' style: '$style' base: '$base' colors: EOF for group in "${COLOR_GROUPS[@]}"; do echo " ${group}:" for scale in 50 100 200 300 400 500 600 700 800 900 950; do local key="${group}.${scale}" if [[ -n "${PALETTE_DATA[$key]:-}" ]]; then LC_NUMERIC=C printf " %s: '%s'\n" "$scale" "${PALETTE_DATA[$key]}" fi done done cat << EOF metadata: generated: '$(date -u +"%Y-%m-%dT%H:%M:%SZ")' generator: 'css_color_palette.sh' version: '1.0.0' EOF } generate_json_output() { local name="$1" local type="$2" local mode="$3" local style="$4" local base="$5" echo "{" echo " \"name\": \"$name\"," echo " \"type\": \"$type\"," echo " \"mode\": \"$mode\"," echo " \"style\": \"$style\"," echo " \"base\": \"$base\"," echo " \"colors\": {" local group_count=0 for group in "${COLOR_GROUPS[@]}"; do ((group_count++)) echo " \"${group}\": {" local scale_count=0 for scale in 50 100 200 300 400 500 600 700 800 900 950; do local key="${group}.${scale}" if [[ -n "${PALETTE_DATA[$key]:-}" ]]; then ((scale_count++)) if [[ $scale_count -lt 11 ]]; then echo " \"$scale\": \"${PALETTE_DATA[$key]}\"," else echo " \"$scale\": \"${PALETTE_DATA[$key]}\"" fi fi done if [[ $group_count -lt ${#COLOR_GROUPS[@]} ]]; then echo " }," else echo " }" fi done echo " }," echo " \"metadata\": {" echo " \"generated\": \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"," echo " \"generator\": \"css_color_palette_bash.sh\"," echo " \"version\": \"1.0.0\"" echo " }" echo "}" } # ============================================================================ # Display Functions # ============================================================================ draw_color_swatch() { local hex="$1" local label="$2" local hex_clean="${hex#\#}" read -r r g b <<< "$(hex_to_rgb "$hex")" if [[ ${COLORS:-0} -ge 256 ]]; then local bg="\033[48;2;${r};${g};${b}m" local fg="\033[38;2;${r};${g};${b}m" local reset="\033[0m" # Determine text color based on luminance local luminance=$(bc_calc "(0.299*$r + 0.587*$g + 0.114*$b)/255") local text_color if bc_compare "$luminance > 0.5"; then text_color="\033[38;2;0;0;0m" else text_color="\033[38;2;255;255;255m" fi LC_NUMERIC=C printf "${bg}${text_color} %-20s ${reset} ${fg}%s${reset} %s\n" "$label" "■" "$hex" else LC_NUMERIC=C printf " %-20s %s\n" "$label" "$hex" fi } display_palette_preview() { if [[ "$VERBOSE" != "true" ]]; then return fi echo "" echo "${BOLD}================================================================${RESET}" echo "${BOLD} Color Palette Preview ${RESET}" echo "${BOLD}================================================================${RESET}" echo "" echo "${BOLD}Palette Name:${RESET} $PALETTE_NAME" echo "${BOLD}Type:${RESET} $PALETTE_TYPE" echo "${BOLD}Base Color:${RESET} $BASE_COLOR" echo "" for group in "${COLOR_GROUPS[@]}"; do echo "${BOLD}${CYAN}${group}:${RESET}" for scale in 50 100 200 300 400 500 600 700 800 900 950; do local key="${group}.${scale}" if [[ -n "${PALETTE_DATA[$key]:-}" ]]; then draw_color_swatch "${PALETTE_DATA[$key]}" "${group}.${scale}" fi done echo "" done echo "${BOLD}----------------------------------------------------------------${RESET}" echo "" } # ============================================================================ # Interactive Mode # ============================================================================ interactive_mode() { echo "" echo "${BOLD}${BLUE}+================================================================+${RESET}" echo "${BOLD}${BLUE}| CSS Color Palette Generator (Interactive) |${RESET}" echo "${BOLD}${BLUE}+================================================================+${RESET}" echo "" while true; do echo -n "${BOLD}Enter base color${RESET} ${DIM}(hex, or 'q' to quit):${RESET} " read -r color_input if [[ "$color_input" =~ ^[qQ]$ ]]; then echo "" success "Goodbye!" exit 0 fi if [[ -z "$color_input" ]]; then continue fi if ! validate_hex "$color_input"; then warning "Invalid hex color format" continue fi local hex=$(normalize_hex "$color_input") echo "" echo -n "${BOLD}Palette type${RESET} ${DIM}[monochromatic]:${RESET} " read -r palette_input palette_input=${palette_input:-monochromatic} echo -n "${BOLD}Output file${RESET} ${DIM}[./colors.yaml]:${RESET} " read -r output_input output_input=${output_input:-./colors.yaml} echo -n "${BOLD}Style${RESET} ${DIM}[all]:${RESET} " read -r style_input style_input=${style_input:-all} echo "" info "Generating palette..." # Reset palette data PALETTE_DATA=() COLOR_GROUPS=() BASE_COLOR="$hex" PALETTE_TYPE="$palette_input" OUTPUT_FILE="$output_input" STYLE_TYPE="$style_input" PALETTE_NAME="${palette_input}-${hex//\#/}" generate_palette "$BASE_COLOR" "$PALETTE_TYPE" "$STYLE_TYPE" VERBOSE=true display_palette_preview VERBOSE=false # Generate output local extension="${OUTPUT_FILE##*.}" if [[ "$extension" == "json" ]]; then generate_json_output "$PALETTE_NAME" "$PALETTE_TYPE" "$COLOR_MODE" "$STYLE_TYPE" "$BASE_COLOR" > "$OUTPUT_FILE" else generate_yaml_output "$PALETTE_NAME" "$PALETTE_TYPE" "$COLOR_MODE" "$STYLE_TYPE" "$BASE_COLOR" > "$OUTPUT_FILE" fi success "Palette saved to: ${BOLD}$OUTPUT_FILE${RESET}" # Count colors local total=0 for key in "${!PALETTE_DATA[@]}"; do ((total++)) done info "Total colors generated: $total" echo "" echo -n "${DIM}Press Enter to continue...${RESET}" read -r echo "" done } # ============================================================================ # Main Script Logic # ============================================================================ main() { # Check dependencies check_dependencies # Parse arguments while [[ $# -gt 0 ]]; do case "$1" in -h|--help) print_usage exit 0 ;; -p|--palette) PALETTE_TYPE="$2" shift 2 ;; -o|--output) OUTPUT_FILE="$2" shift 2 ;; -m|--mode) COLOR_MODE="$2" shift 2 ;; -s|--style) STYLE_TYPE="$2" shift 2 ;; -n|--name) PALETTE_NAME="$2" shift 2 ;; --scales) SCALE_STEPS="$2" shift 2 ;; -i|--interactive) INTERACTIVE=true shift ;; -v|--verbose) VERBOSE=true shift ;; -*) error "Unknown option: $1. Use --help for usage information." ;; *) BASE_COLOR="$1" shift ;; esac done # Interactive mode if [[ "$INTERACTIVE" == "true" ]]; then interactive_mode exit 0 fi # Validate inputs if [[ -z "$BASE_COLOR" ]]; then error "No base color specified. Use --help for usage information." fi if ! validate_hex "$BASE_COLOR"; then error "Invalid hex color format: $BASE_COLOR" fi # Normalize color BASE_COLOR=$(normalize_hex "$BASE_COLOR") # Auto-generate palette name if not provided if [[ -z "$PALETTE_NAME" ]]; then PALETTE_NAME="${PALETTE_TYPE}-${BASE_COLOR//\#/}" fi # Validate palette type case "$PALETTE_TYPE" in monochromatic|analogous|complementary|split-complementary|triadic|tetradic) ;; *) error "Invalid palette type: $PALETTE_TYPE" ;; esac # Validate style type case "$STYLE_TYPE" in all|shades|tints|tones) ;; *) error "Invalid style type: $STYLE_TYPE" ;; esac # Generate palette if [[ "$VERBOSE" == "true" ]]; then info "Generating $PALETTE_TYPE palette from $BASE_COLOR..." fi generate_palette "$BASE_COLOR" "$PALETTE_TYPE" "$STYLE_TYPE" # Display preview if verbose display_palette_preview # Generate output file local extension="${OUTPUT_FILE##*.}" if [[ "$extension" == "json" ]]; then generate_json_output "$PALETTE_NAME" "$PALETTE_TYPE" "$COLOR_MODE" "$STYLE_TYPE" "$BASE_COLOR" > "$OUTPUT_FILE" else generate_yaml_output "$PALETTE_NAME" "$PALETTE_TYPE" "$COLOR_MODE" "$STYLE_TYPE" "$BASE_COLOR" > "$OUTPUT_FILE" fi success "Palette saved to: ${BOLD}$OUTPUT_FILE${RESET}" if [[ "$VERBOSE" == "true" ]]; then local total=0 for key in "${!PALETTE_DATA[@]}"; do ((total++)) done info "Total colors generated: $total" fi } # Run main function main "$@"