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, ...).
405 lines
9.7 KiB
Bash
Executable File
405 lines
9.7 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# omarchy:summary=Generate themed config files from Omarchy templates
|
|
# omarchy:hidden=true
|
|
|
|
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"
|
|
|
|
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
|