#!/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/.local/state/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 } shell_gradient_value() { local spec index spec=$(resolve_theme_ref "$1" "${2:-}") parse_gradient "$spec" if (( ${#GRADIENT_COLORS[@]} == 0 )); then printf "%s" "$spec" return fi for index in "${!GRADIENT_COLORS[@]}"; do (( index > 0 )) && printf " " printf "%s" "${GRADIENT_COLORS[$index]}" done [[ -n $GRADIENT_ANGLE ]] && printf " %sdeg" "$GRADIENT_ANGLE" } 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") ;; shell_gradient) value=$(shell_gradient_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|shell_gradient)[[:space:]]+[^}]+[[:space:]]*\}\}' "$tpl" 2>/dev/null || true) done } strip_shell_section_header() { local section="$1" local file="$2" awk -v section="$section" ' BEGIN { skipping_header = 0 } /^[[:space:]]*\[[^]]+\][[:space:]]*($|#)/ { if ($0 ~ "^[[:space:]]*\\[" section "\\][[:space:]]*($|#)") { skipping_header = 1 next } } { print } ' "$file" } apply_shell_section_override() { local override="$1" local section tmp body [[ -f $NEXT_THEME_DIR/shell.toml ]] || return [[ -f $override ]] || return section=$(basename "$override") section="${section#shell.}" section="${section%.toml}" [[ $section =~ ^[A-Za-z0-9_-]+$ ]] || return tmp=$(mktemp) body=$(mktemp) strip_shell_section_header "$section" "$override" >"$body" awk -v section="$section" -v body="$body" ' function emit_override() { if (emitted) return print "[" section "]" while ((getline line < body) > 0) print line close(body) emitted = 1 } /^[[:space:]]*\[[^]]+\][[:space:]]*($|#)/ { if (in_section) { emit_override() in_section = 0 } if ($0 ~ "^[[:space:]]*\\[" section "\\][[:space:]]*($|#)") { in_section = 1 next } } !in_section { print } END { if (in_section || !emitted) { if (NR > 0) print "" emit_override() } } ' "$NEXT_THEME_DIR/shell.toml" >"$tmp" mv "$tmp" "$NEXT_THEME_DIR/shell.toml" rm "$body" } apply_shell_section_overrides() { local override shopt -s nullglob for override in "$NEXT_THEME_DIR"/shell.*.toml; do [[ $(basename "$override") != "shell.toml" ]] || continue apply_shell_section_override "$override" 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) # Parsing and alias/fallback resolution (legacy colorN names, derived # shades, mode/theme_type) is shared with every other colors.toml consumer. while IFS=$'\t' read -r key value; do THEME_COLORS[$key]="$value" done < <(omarchy-theme-color --file "$COLORS_FILE" --all) for key in "${!THEME_COLORS[@]}"; do add_template_value "$key" "${THEME_COLORS[$key]}" done 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 apply_shell_section_overrides