#!/bin/bash # omarchy:summary=Generate themed config files from Omarchy templates # omarchy:hidden=true TEMPLATES_DIR="$OMARCHY_PATH/default/themed" USER_TEMPLATES_DIR="$HOME/.config/omarchy/themed" NEXT_THEME_DIR="$HOME/.config/omarchy/current/next-theme" COLORS_FILE="$NEXT_THEME_DIR/colors.toml" declare -A THEME_COLORS # Convert hex color to decimal RGB (e.g., "#1e1e2e" -> "30,30,46") hex_to_rgb() { local hex="${1#\#}" printf "%d,%d,%d" "0x${hex:0:2}" "0x${hex:2:2}" "0x${hex:4:2}" } # Mix two hex colors. Amount may be a fraction (0.30) or percentage (30%). mix_color() { local start="${1#\#}" local end="${2#\#}" local amount="$3" awk -v start="$start" -v end="$end" -v amount="$amount" ' function hex_value(char) { return index("0123456789abcdef", tolower(char)) - 1 } function hex_pair_to_int(hex, idx) { return hex_value(substr(hex, idx, 1)) * 16 + hex_value(substr(hex, idx + 1, 1)) } BEGIN { if (amount ~ /%$/) { sub(/%$/, "", amount) amount = amount / 100 } else { amount += 0 if (amount > 1) amount = amount / 100 } if (amount < 0) amount = 0 if (amount > 1) amount = 1 start_r = hex_pair_to_int(start, 1) start_g = hex_pair_to_int(start, 3) start_b = hex_pair_to_int(start, 5) end_r = hex_pair_to_int(end, 1) end_g = hex_pair_to_int(end, 3) end_b = hex_pair_to_int(end, 5) red = int(start_r * (1 - amount) + end_r * amount + 0.5) green = int(start_g * (1 - amount) + end_g * amount + 0.5) blue = int(start_b * (1 - amount) + end_b * amount + 0.5) printf "#%02x%02x%02x\n", red, green, blue } ' } trim() { local value="$1" value="${value#"${value%%[![:space:]]*}"}" value="${value%"${value##*[![:space:]]}"}" printf "%s" "$value" } resolve_theme_ref() { local ref="$1" local fallback="${2:-}" if [[ -n ${THEME_COLORS[$ref]+_} ]]; then printf "%s" "${THEME_COLORS[$ref]}" elif [[ -n $fallback && -n ${THEME_COLORS[$fallback]+_} ]]; then printf "%s" "${THEME_COLORS[$fallback]}" elif [[ -n $fallback ]]; then printf "%s" "$fallback" else printf "%s" "$ref" fi } resolve_gradient_color() { local color color=$(trim "$1") if [[ -n ${THEME_COLORS[$color]+_} ]]; then color="${THEME_COLORS[$color]}" fi printf "%s" "$color" } parse_gradient() { local spec="$1" local part color local -a parts GRADIENT_COLORS=() GRADIENT_ANGLE="" read -ra parts <<<"$spec" for part in "${parts[@]}"; do [[ -n $part ]] || continue if [[ $part =~ ^-?[0-9]+([.][0-9]+)?deg$ ]]; then GRADIENT_ANGLE="${part%deg}" else color=$(resolve_gradient_color "$part") GRADIENT_COLORS+=("$color") fi done } color_to_shell_hex() { local color r g b color=$(resolve_gradient_color "$1") if [[ $color =~ ^#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$ ]]; then printf "#%s" "${color:1:6}" elif [[ $color =~ ^[Rr][Gg][Bb][Aa]?\(([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?\)$ ]]; then printf "#%s" "${BASH_REMATCH[1]}" elif [[ $color =~ ^[Rr][Gg][Bb][Aa]?\(([0-9]+),([0-9]+),([0-9]+)(,[0-9.]+)?\)$ ]]; then r=${BASH_REMATCH[1]} g=${BASH_REMATCH[2]} b=${BASH_REMATCH[3]} (( r > 255 )) && r=255 (( g > 255 )) && g=255 (( b > 255 )) && b=255 printf "#%02x%02x%02x" "$r" "$g" "$b" elif [[ $color =~ ^0x[0-9A-Fa-f]{8}$ ]]; then printf "#%s" "${color:4:6}" else printf "%s" "$color" fi } hypr_gradient_value() { local spec color index spec=$(resolve_theme_ref "$1" "${2:-}") parse_gradient "$spec" if (( ${#GRADIENT_COLORS[@]} == 0 )); then printf '"%s"' "$spec" elif (( ${#GRADIENT_COLORS[@]} == 1 )); then printf '"%s"' "${GRADIENT_COLORS[0]}" else printf '{ colors = {' for index in "${!GRADIENT_COLORS[@]}"; do (( index > 0 )) && printf ',' printf ' "%s"' "${GRADIENT_COLORS[$index]}" done printf ' }' [[ -n $GRADIENT_ANGLE ]] && printf ', angle = %s' "$GRADIENT_ANGLE" printf ' }' fi } gradient_start_value() { local spec spec=$(resolve_theme_ref "$1" "${2:-}") parse_gradient "$spec" if (( ${#GRADIENT_COLORS[@]} == 0 )); then color_to_shell_hex "$spec" else color_to_shell_hex "${GRADIENT_COLORS[0]}" fi } add_template_value() { local key="$1" local value="$2" local rgb printf 's|{{ %s }}|%s|g\n' "$key" "$value" >>"$sed_script" printf 's|{{ %s_strip }}|%s|g\n' "$key" "${value#\#}" >>"$sed_script" if [[ $value =~ ^#[0-9A-Fa-f]{6}$ ]]; then rgb=$(hex_to_rgb "$value") printf 's|{{ %s_rgb }}|%s|g\n' "$key" "$rgb" >>"$sed_script" fi } add_mix_value() { local token="$1" local content fn start_key end_key amount start end value content="${token#\{\{}" content="${content%\}\}}" read -r fn start_key end_key amount <<<"$content" start="${THEME_COLORS[$start_key]:-}" end="${THEME_COLORS[$end_key]:-}" [[ $start =~ ^#[0-9A-Fa-f]{6}$ && $end =~ ^#[0-9A-Fa-f]{6}$ ]] || return value=$(mix_color "$start" "$end" "$amount") case "$fn" in mix) ;; mix_strip) value="${value#\#}" ;; mix_rgb) value=$(hex_to_rgb "$value") ;; *) return ;; esac printf 's|%s|%s|g\n' "$token" "$value" >>"$sed_script" } add_mix_values() { local tpl token local -A seen=() for tpl in "${template_files[@]}"; do while IFS= read -r token; do [[ -n ${seen[$token]:-} ]] && continue seen[$token]=1 add_mix_value "$token" done < <(grep -hEo '\{\{[[:space:]]*mix(_strip|_rgb)?[[:space:]]+[A-Za-z0-9_]+[[:space:]]+[A-Za-z0-9_]+[[:space:]]+[0-9]+([.][0-9]+)?%?[[:space:]]*\}\}' "$tpl" 2>/dev/null || true) done } add_gradient_function_value() { local token="$1" local content fn key fallback value content="${token#\{\{}" content="${content%\}\}}" read -r fn key fallback <<<"$content" case "$fn" in hypr_gradient) value=$(hypr_gradient_value "$key" "$fallback") ;; gradient_start) value=$(gradient_start_value "$key" "$fallback") ;; *) return ;; esac printf 's|%s|%s|g\n' "$token" "$value" >>"$sed_script" } add_gradient_function_values() { local tpl token local -A seen=() for tpl in "${template_files[@]}"; do while IFS= read -r token; do [[ -n ${seen[$token]:-} ]] && continue seen[$token]=1 add_gradient_function_value "$token" done < <(grep -hEo '\{\{[[:space:]]*(hypr_gradient|gradient_start)[[:space:]]+[^}]+[[:space:]]*\}\}' "$tpl" 2>/dev/null || true) done } # Only generate dynamic templates for themes with a colors.toml definition if [[ -f $COLORS_FILE ]]; then sed_script=$(mktemp) shopt -s nullglob template_files=("$USER_TEMPLATES_DIR"/*.tpl "$TEMPLATES_DIR"/*.tpl) while IFS='=' read -r key value; do key="${key//[\"\' ]/}" # strip quotes and spaces from key [[ $key && $key != \#* ]] || continue # skip empty lines and comments value="${value#*[\"\']}" value="${value%%[\"\']*}" # extract value between quotes (ignores inline comments) THEME_COLORS[$key]="$value" add_template_value "$key" "$value" done <"$COLORS_FILE" add_mix_values add_gradient_function_values # Process user templates first, then built-in templates (user overrides built-in) for tpl in "${template_files[@]}"; do filename=$(basename "$tpl" .tpl) output_path="$NEXT_THEME_DIR/$filename" # Don't overwrite configs already exists in the output directory (copied from theme specific folder) if [[ ! -f $output_path ]]; then sed -f "$sed_script" "$tpl" >"$output_path" fi done rm "$sed_script" fi