feat(themes): add color derivation and themed app templates
Add script/omarchy-theme-color and expand omarchy-theme-set-templates to derive a full palette from each theme's colors.toml, then render new themed templates (shell, pi, vscode theme, gum, neovim, claude, hermes, t3code) from it. Adds backgrounds and palette updates for existing themes plus new themes (last-horizon, lupine, solitude, tidewater-*, miasma, ...).
This commit is contained in:
Executable
+304
@@ -0,0 +1,304 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Resolve semantic colors from an Omarchy theme colors.toml
|
||||
# omarchy:args=[--file <colors.toml>] (--all | --raw | <key> [fallback])
|
||||
# omarchy:hidden=true
|
||||
|
||||
# Shared colors.toml parser/resolver. The alias/fallback cascade mirrors what
|
||||
# omarchy-theme-set-templates bakes into the generated configs at theme-set
|
||||
# time, so every consumer (templates, OSC sequences, tmux, GNOME, previews)
|
||||
# resolves the exact same palette.
|
||||
#
|
||||
# --all print every resolved key<TAB>value pair (sorted by key)
|
||||
# --raw print only the key<TAB>value pairs defined in the file
|
||||
# <key> [fallback] print one resolved value; the fallback is tried as
|
||||
# another palette key first, then used verbatim
|
||||
#
|
||||
# Theme mode precedence: `mode` key, legacy `theme_type` key, a light.mode
|
||||
# file beside the colors.toml, background luminance auto-detect, dark.
|
||||
|
||||
COLORS_FILE="$HOME/.local/state/omarchy/current/theme/colors.toml"
|
||||
OUTPUT=""
|
||||
QUERY_KEY=""
|
||||
QUERY_FALLBACK=""
|
||||
|
||||
usage() {
|
||||
echo "Usage: omarchy-theme-color [--file <colors.toml>] (--all | --raw | <key> [fallback])"
|
||||
}
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--file)
|
||||
COLORS_FILE="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--all)
|
||||
OUTPUT="all"
|
||||
shift
|
||||
;;
|
||||
--raw)
|
||||
OUTPUT="raw"
|
||||
shift
|
||||
;;
|
||||
-h | --help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
if [[ -z $QUERY_KEY ]]; then
|
||||
QUERY_KEY="$1"
|
||||
elif [[ -z $QUERY_FALLBACK ]]; then
|
||||
QUERY_FALLBACK="$1"
|
||||
else
|
||||
usage >&2
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z $OUTPUT && -z $QUERY_KEY ]] || [[ -n $OUTPUT && -n $QUERY_KEY ]]; then
|
||||
usage >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
COLORS_DIR=$(dirname "$COLORS_FILE")
|
||||
|
||||
declare -A THEME_COLORS
|
||||
|
||||
# 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
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
alias_theme_color() {
|
||||
local key="$1"
|
||||
local fallback="$2"
|
||||
|
||||
[[ ${THEME_COLORS[$key]} ]] || THEME_COLORS[$key]="${THEME_COLORS[$fallback]}"
|
||||
}
|
||||
|
||||
resolve_theme_mode() {
|
||||
local bg_hex lum
|
||||
|
||||
[[ ${THEME_COLORS[mode]} ]] || THEME_COLORS[mode]="${THEME_COLORS[theme_type]}"
|
||||
[[ ${THEME_COLORS[mode]} ]] && return
|
||||
|
||||
if [[ -f $COLORS_DIR/light.mode ]]; then
|
||||
THEME_COLORS[mode]="light"
|
||||
elif [[ ${THEME_COLORS[background]} =~ ^#[0-9A-Fa-f]{6}$ ]]; then
|
||||
bg_hex="${THEME_COLORS[background]#\#}"
|
||||
lum=$(( $(printf "%d" "0x${bg_hex:0:2}") + $(printf "%d" "0x${bg_hex:2:2}") + $(printf "%d" "0x${bg_hex:4:2}") ))
|
||||
(( lum > 382 )) && THEME_COLORS[mode]="light" || THEME_COLORS[mode]="dark"
|
||||
else
|
||||
THEME_COLORS[mode]="dark"
|
||||
fi
|
||||
}
|
||||
|
||||
parse_colors_file() {
|
||||
local key value
|
||||
|
||||
[[ -f $COLORS_FILE ]] || return 0
|
||||
|
||||
while IFS='=' read -r key value; do
|
||||
key="${key//[\"\' ]/}" # strip quotes and spaces from key
|
||||
[[ $key && $key != \#* ]] || continue # skip empty lines and comments
|
||||
|
||||
if [[ $value == *[\"\']* ]]; then
|
||||
value="${value#*[\"\']}"
|
||||
value="${value%%[\"\']*}" # extract value between quotes (ignores inline comments)
|
||||
else
|
||||
value="${value#"${value%%[![:space:]]*}"}"
|
||||
value="${value%"${value##*[![:space:]]}"}" # trim unquoted values
|
||||
fi
|
||||
|
||||
# Values reach consumers as sed replacement text, so the charset excludes
|
||||
# the delimiter, backslash, and & while still covering everything a real
|
||||
# palette holds: hex, rgb()/rgba() lists, gradient angles like -45deg,
|
||||
# decimals, and bare words. Rejections are announced so a third-party theme
|
||||
# doesn't lose a key silently and leave a raw {{ placeholder }} behind.
|
||||
if [[ ! $key =~ ^[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-]+$ ]]; then
|
||||
printf 'omarchy-theme-color: skipping key with unsupported characters\n' >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ ! $value =~ ^[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#(),._+/%\ -]*$ ]]; then
|
||||
printf 'omarchy-theme-color: skipping %s: unsupported characters in value\n' "$key" >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
THEME_COLORS[$key]="$value"
|
||||
[[ $OUTPUT == "raw" ]] && printf '%s\t%s\n' "$key" "$value"
|
||||
done <"$COLORS_FILE"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
resolve_theme_colors() {
|
||||
local key
|
||||
|
||||
# Accept the complete legacy short-name palette before applying ANSI
|
||||
# fallbacks or deriving shades. Canonical names take precedence when a theme
|
||||
# defines both forms.
|
||||
declare -A legacy_palette_alias=(
|
||||
[background]=bg
|
||||
[dark_background]=dark_bg
|
||||
[darker_background]=darker_bg
|
||||
[lighter_background]=lighter_bg
|
||||
[foreground]=fg
|
||||
[dark_foreground]=dark_fg
|
||||
[light_foreground]=light_fg
|
||||
[bright_foreground]=bright_fg
|
||||
)
|
||||
for key in "${!legacy_palette_alias[@]}"; do
|
||||
alias_theme_color "$key" "${legacy_palette_alias[$key]}"
|
||||
done
|
||||
|
||||
# Themes generated before the semantic palette may only define ANSI names.
|
||||
[[ ${THEME_COLORS[background]} ]] || THEME_COLORS[background]="${THEME_COLORS[color0]}"
|
||||
[[ ${THEME_COLORS[foreground]} ]] || THEME_COLORS[foreground]="${THEME_COLORS[color7]}"
|
||||
[[ ${THEME_COLORS[background]} ]] && THEME_COLORS[color0]="${THEME_COLORS[background]}"
|
||||
[[ ${THEME_COLORS[foreground]} ]] && THEME_COLORS[color7]="${THEME_COLORS[foreground]}"
|
||||
|
||||
# Legacy compatibility: map ANSI color0..color15 to semantic names.
|
||||
declare -A legacy_alias=(
|
||||
[red]=color1
|
||||
[green]=color2
|
||||
[yellow]=color3
|
||||
[blue]=color4
|
||||
[magenta]=color5
|
||||
[cyan]=color6
|
||||
[bright_red]=color9
|
||||
[bright_green]=color10
|
||||
[bright_yellow]=color11
|
||||
[bright_blue]=color12
|
||||
[bright_magenta]=color13
|
||||
[bright_cyan]=color14
|
||||
)
|
||||
for key in "${!legacy_alias[@]}"; do
|
||||
alias_theme_color "$key" "${legacy_alias[$key]}"
|
||||
done
|
||||
alias_theme_color magenta purple
|
||||
alias_theme_color bright_magenta bright_purple
|
||||
|
||||
[[ ${THEME_COLORS[light_foreground]} ]] || THEME_COLORS[light_foreground]="${THEME_COLORS[color7]:-${THEME_COLORS[foreground]}}"
|
||||
[[ ${THEME_COLORS[bright_foreground]} ]] || THEME_COLORS[bright_foreground]="${THEME_COLORS[color15]:-${THEME_COLORS[foreground]}}"
|
||||
THEME_COLORS[cursor]="${THEME_COLORS[bright_foreground]}"
|
||||
[[ ${THEME_COLORS[lighter_background]} ]] || THEME_COLORS[lighter_background]="${THEME_COLORS[color0]:-${THEME_COLORS[background]}}"
|
||||
[[ ${THEME_COLORS[dark_foreground]} ]] || THEME_COLORS[dark_foreground]="${THEME_COLORS[color8]:-${THEME_COLORS[foreground]}}"
|
||||
[[ ${THEME_COLORS[muted]} ]] || THEME_COLORS[muted]="${THEME_COLORS[color8]:-${THEME_COLORS[dark_foreground]}}"
|
||||
[[ ${THEME_COLORS[selection]} ]] || THEME_COLORS[selection]="${THEME_COLORS[selection_background]:-${THEME_COLORS[color8]:-${THEME_COLORS[color0]:-${THEME_COLORS[background]}}}}"
|
||||
[[ ${THEME_COLORS[selection_background]} ]] || THEME_COLORS[selection_background]="${THEME_COLORS[selection]}"
|
||||
[[ ${THEME_COLORS[selection_foreground]} ]] || THEME_COLORS[selection_foreground]="${THEME_COLORS[bright_foreground]}"
|
||||
[[ ${THEME_COLORS[orange]} ]] || THEME_COLORS[orange]="${THEME_COLORS[yellow]}"
|
||||
[[ ${THEME_COLORS[brown]} ]] || THEME_COLORS[brown]=$(mix_color "${THEME_COLORS[orange]}" "#000000" 50%)
|
||||
|
||||
# Auto-derive shades from base accents when not defined and not aliased from colorN
|
||||
[[ ${THEME_COLORS[dark_background]} ]] || THEME_COLORS[dark_background]=$(mix_color "${THEME_COLORS[background]}" "#000000" 25%)
|
||||
[[ ${THEME_COLORS[darker_background]} ]] || THEME_COLORS[darker_background]=$(mix_color "${THEME_COLORS[background]}" "#000000" 50%)
|
||||
[[ ${THEME_COLORS[bright_red]} ]] || THEME_COLORS[bright_red]=$(mix_color "${THEME_COLORS[red]}" "#ffffff" 20%)
|
||||
[[ ${THEME_COLORS[bright_yellow]} ]] || THEME_COLORS[bright_yellow]=$(mix_color "${THEME_COLORS[yellow]}" "#ffffff" 20%)
|
||||
[[ ${THEME_COLORS[bright_green]} ]] || THEME_COLORS[bright_green]=$(mix_color "${THEME_COLORS[green]}" "#ffffff" 20%)
|
||||
[[ ${THEME_COLORS[bright_cyan]} ]] || THEME_COLORS[bright_cyan]=$(mix_color "${THEME_COLORS[cyan]}" "#ffffff" 20%)
|
||||
[[ ${THEME_COLORS[bright_blue]} ]] || THEME_COLORS[bright_blue]=$(mix_color "${THEME_COLORS[blue]}" "#ffffff" 20%)
|
||||
[[ ${THEME_COLORS[bright_magenta]} ]] || THEME_COLORS[bright_magenta]=$(mix_color "${THEME_COLORS[magenta]}" "#ffffff" 20%)
|
||||
alias_theme_color purple magenta
|
||||
alias_theme_color bright_purple bright_magenta
|
||||
|
||||
# Keep semantic themes compatible with consumers that still reference the
|
||||
# legacy ANSI names directly.
|
||||
declare -A ansi_alias=(
|
||||
[color0]=background
|
||||
[color1]=red
|
||||
[color2]=green
|
||||
[color3]=yellow
|
||||
[color4]=blue
|
||||
[color5]=magenta
|
||||
[color6]=cyan
|
||||
[color7]=foreground
|
||||
[color8]=muted
|
||||
[color9]=bright_red
|
||||
[color10]=bright_green
|
||||
[color11]=bright_yellow
|
||||
[color12]=bright_blue
|
||||
[color13]=bright_magenta
|
||||
[color14]=bright_cyan
|
||||
[color15]=bright_foreground
|
||||
)
|
||||
for key in "${!ansi_alias[@]}"; do
|
||||
alias_theme_color "$key" "${ansi_alias[$key]}"
|
||||
done
|
||||
|
||||
# Keep canonical themes compatible with old user templates and consumers
|
||||
# that still query the short palette names directly.
|
||||
for key in "${!legacy_palette_alias[@]}"; do
|
||||
if [[ ${THEME_COLORS[$key]} ]]; then
|
||||
THEME_COLORS["${legacy_palette_alias[$key]}"]="${THEME_COLORS[$key]}"
|
||||
fi
|
||||
done
|
||||
|
||||
resolve_theme_mode
|
||||
THEME_COLORS[theme_type]="${THEME_COLORS[mode]}"
|
||||
}
|
||||
|
||||
parse_colors_file
|
||||
|
||||
if [[ $OUTPUT == "raw" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
resolve_theme_colors
|
||||
|
||||
if [[ $OUTPUT == "all" ]]; then
|
||||
while IFS= read -r key; do
|
||||
printf '%s\t%s\n' "$key" "${THEME_COLORS[$key]}"
|
||||
done < <(printf '%s\n' "${!THEME_COLORS[@]}" | LC_ALL=C sort)
|
||||
exit 0
|
||||
fi
|
||||
|
||||
value="${THEME_COLORS[$QUERY_KEY]:-}"
|
||||
if [[ -z $value && -n $QUERY_FALLBACK ]]; then
|
||||
value="${THEME_COLORS[$QUERY_FALLBACK]:-$QUERY_FALLBACK}"
|
||||
fi
|
||||
|
||||
[[ -n $value ]] || exit 1
|
||||
printf '%s\n' "$value"
|
||||
@@ -7,7 +7,8 @@ TEMPLATES_DIR="$DOTFILES_DIR/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"
|
||||
THEME_MODE="$NEXT_THEME_DIR/light.mode"
|
||||
|
||||
declare -A THEME_COLORS
|
||||
|
||||
# Convert hex color to decimal RGB (e.g., "#1e1e2e" -> "30,30,46")
|
||||
hex_to_rgb() {
|
||||
@@ -15,35 +16,379 @@ hex_to_rgb() {
|
||||
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)
|
||||
|
||||
key="theme_mode";
|
||||
if [[ -f $THEME_MODE ]]; then
|
||||
printf 's|{{ %s }}|%s|g\n' "$key" "light" # {{ key }} -> value
|
||||
else
|
||||
printf 's|{{ %s }}|%s|g\n' "$key" "dark" # {{ key }} -> value
|
||||
fi >"$sed_script"
|
||||
|
||||
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)
|
||||
|
||||
printf 's|{{ %s }}|%s|g\n' "$key" "$value" # {{ key }} -> value
|
||||
printf 's|{{ %s_strip }}|%s|g\n' "$key" "${value#\#}" # {{ key_strip }} -> value without leading #
|
||||
if [[ $value =~ ^# ]]; then
|
||||
rgb=$(hex_to_rgb "$value")
|
||||
echo "s|{{ ${key}_rgb }}|${rgb}|g"
|
||||
fi
|
||||
done <"$COLORS_FILE" >>"$sed_script"
|
||||
|
||||
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 "$USER_TEMPLATES_DIR"/*.tpl "$TEMPLATES_DIR"/*.tpl; do
|
||||
for tpl in "${template_files[@]}"; do
|
||||
filename=$(basename "$tpl" .tpl)
|
||||
output_path="$NEXT_THEME_DIR/$filename"
|
||||
|
||||
@@ -55,3 +400,5 @@ if [[ -f $COLORS_FILE ]]; then
|
||||
|
||||
rm "$sed_script"
|
||||
fi
|
||||
|
||||
apply_shell_section_overrides
|
||||
|
||||
Reference in New Issue
Block a user