Share one theme color resolver across consumers

Five scripts each hand-rolled colors.toml parsing plus their own
alias/fallback cascade, and the tables had drifted: theme-osc grew a
divergent color8/color9 chain, tmux and gnome carried private mode
detection, and the preview tool duplicated the lot. The differences
never mattered for shipped themes but were a standing bug factory for
sparse user themes.

omarchy-theme-color is now the single resolver. Its cascade is a
verbatim port of the canonical theme-set-templates logic (semantic
keys, legacy colorN aliases, derived shades, selection/cursor
derivation, mode precedence), exposed as --all, --raw, or single-key
lookup with fallback.

Generated output is byte-identical across all 21 shipped themes on
every surface: 17 template files, OSC byte stream, tmux and gsettings
command logs, and previews. Sparse legacy themes now resolve the same
palette everywhere instead of five slightly different ones.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
David Heinemeier Hansson
2026-07-03 09:06:31 -07:00
co-authored by Claude Fable 5
parent 03f7420d72
commit 3bd6e72211
6 changed files with 327 additions and 296 deletions
+25 -39
View File
@@ -84,40 +84,38 @@ resolve_colors_file() {
fi
}
clean_toml_value() {
local value="$1"
omarchy_tool() {
local tool="$1"
shift
value="${value#*[\"\']}"
value="${value%%[\"\']*}"
printf '%s' "$value"
if [[ -x $OMARCHY_PATH/bin/$tool ]]; then
"$OMARCHY_PATH/bin/$tool" "$@"
else
"$tool" "$@"
fi
}
load_colors() {
local colors_file="$1"
local key value
local -A resolved=()
while IFS='=' read -r key value; do
key="${key//[\"\' ]/}"
[[ $key && $key != \#* ]] || continue
value=$(clean_toml_value "$value")
COLORS[$key]="$value"
done <"$colors_file"
while IFS=$'\t' read -r key value; do
resolved[$key]="$value"
done < <(omarchy_tool omarchy-theme-color --file "$colors_file" --all)
# Legacy semantic names win when present so older themes keep their intended
# primary text/background colors even if they also had dimmer fg/bg slots.
[[ ${COLORS[background]} ]] && COLORS[bg]="${COLORS[background]}"
[[ ${COLORS[foreground]} ]] && COLORS[fg]="${COLORS[foreground]}"
[[ ${COLORS[bg]} ]] || COLORS[bg]="${COLORS[color0]}"
[[ ${COLORS[fg]} ]] || COLORS[fg]="${COLORS[color7]}"
[[ ${COLORS[light_fg]} ]] || COLORS[light_fg]="${COLORS[color7]:-${COLORS[fg]}}"
[[ ${COLORS[bright_fg]} ]] || COLORS[bright_fg]="${COLORS[color15]:-${COLORS[fg]}}"
[[ ${COLORS[selection_background]} ]] || COLORS[selection_background]="${COLORS[selection]}"
[[ ${COLORS[selection_foreground]} ]] || COLORS[selection_foreground]="${COLORS[bright_fg]}"
# Preview the palette the theme itself defines, at canonically resolved values.
while IFS=$'\t' read -r key value; do
COLORS[$key]="${resolved[$key]:-}"
done < <(omarchy_tool omarchy-theme-color --file "$colors_file" --raw)
if hex_valid "${COLORS[bg]}"; then
[[ ${COLORS[dark_bg]} ]] || COLORS[dark_bg]=$(mix_hex "${COLORS[bg]}" "#000000" 1 4)
[[ ${COLORS[darker_bg]} ]] || COLORS[darker_bg]=$(mix_hex "${COLORS[bg]}" "#000000" 1 2)
fi
# Plus the core slots every theme resolves, so sparse and legacy themes
# still render a complete preview.
for key in mode bg fg light_fg bright_fg selection_background selection_foreground dark_bg darker_bg; do
[[ -n ${resolved[$key]:-} ]] && COLORS[$key]="${resolved[$key]}"
done
return 0
}
hex_parts() {
@@ -188,11 +186,7 @@ apply_terminal_osc() {
;;
esac
if [[ -x $OMARCHY_PATH/bin/omarchy-theme-osc ]]; then
"$OMARCHY_PATH/bin/omarchy-theme-osc" "$colors_file" || true
else
omarchy-theme-osc "$colors_file" || true
fi
omarchy_tool omarchy-theme-osc "$colors_file" || true
}
swatch() {
@@ -415,15 +409,7 @@ fi
load_colors "$colors_file"
mode="${COLORS[mode]}"
if [[ -z $mode && ${COLORS[bg]} =~ ^#[0-9A-Fa-f]{6}$ ]]; then
if (( $(luma_sum "${COLORS[bg]}") > 1275000 )); then
mode="light"
else
mode="dark"
fi
fi
[[ -n $mode ]] || mode="dark"
mode="${COLORS[mode]:-dark}"
theme_label="$THEME_REF"
if [[ -z $theme_label ]]; then
+270
View File
@@ -0,0 +1,270 @@
#!/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 [[ $# -gt 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[bg]} =~ ^#[0-9A-Fa-f]{6}$ ]]; then
bg_hex="${THEME_COLORS[bg]#\#}"
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
THEME_COLORS[$key]="$value"
[[ $OUTPUT == "raw" ]] && printf '%s\t%s\n' "$key" "$value"
done <"$COLORS_FILE"
return 0
}
resolve_theme_colors() {
local key
# Canonical palette keys are bg/fg. Preserve older themes that still define
# background/foreground by treating those semantic values as the source of
# truth, then expose the old names as aliases for legacy consumers.
[[ ${THEME_COLORS[background]} ]] && THEME_COLORS[bg]="${THEME_COLORS[background]}"
[[ ${THEME_COLORS[foreground]} ]] && THEME_COLORS[fg]="${THEME_COLORS[foreground]}"
[[ ${THEME_COLORS[bg]} ]] || THEME_COLORS[bg]="${THEME_COLORS[color0]}"
[[ ${THEME_COLORS[fg]} ]] || THEME_COLORS[fg]="${THEME_COLORS[color7]}"
[[ ${THEME_COLORS[bg]} ]] && THEME_COLORS[background]="${THEME_COLORS[bg]}"
[[ ${THEME_COLORS[fg]} ]] && THEME_COLORS[foreground]="${THEME_COLORS[fg]}"
[[ ${THEME_COLORS[bg]} ]] && THEME_COLORS[color0]="${THEME_COLORS[bg]}"
[[ ${THEME_COLORS[fg]} ]] && THEME_COLORS[color7]="${THEME_COLORS[fg]}"
# 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_fg]} ]] || THEME_COLORS[light_fg]="${THEME_COLORS[color7]:-${THEME_COLORS[fg]}}"
[[ ${THEME_COLORS[bright_fg]} ]] || THEME_COLORS[bright_fg]="${THEME_COLORS[color15]:-${THEME_COLORS[fg]}}"
THEME_COLORS[cursor]="${THEME_COLORS[bright_fg]}"
[[ ${THEME_COLORS[lighter_bg]} ]] || THEME_COLORS[lighter_bg]="${THEME_COLORS[color0]:-${THEME_COLORS[bg]}}"
[[ ${THEME_COLORS[dark_fg]} ]] || THEME_COLORS[dark_fg]="${THEME_COLORS[color8]:-${THEME_COLORS[fg]}}"
[[ ${THEME_COLORS[muted]} ]] || THEME_COLORS[muted]="${THEME_COLORS[color8]:-${THEME_COLORS[dark_fg]}}"
[[ ${THEME_COLORS[selection]} ]] || THEME_COLORS[selection]="${THEME_COLORS[selection_background]:-${THEME_COLORS[color8]:-${THEME_COLORS[color0]:-${THEME_COLORS[bg]}}}}"
[[ ${THEME_COLORS[selection_background]} ]] || THEME_COLORS[selection_background]="${THEME_COLORS[selection]}"
[[ ${THEME_COLORS[selection_foreground]} ]] || THEME_COLORS[selection_foreground]="${THEME_COLORS[bright_fg]}"
[[ ${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_bg]} ]] || THEME_COLORS[dark_bg]=$(mix_color "${THEME_COLORS[bg]}" "#000000" 25%)
[[ ${THEME_COLORS[darker_bg]} ]] || THEME_COLORS[darker_bg]=$(mix_color "${THEME_COLORS[bg]}" "#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]=bg
[color1]=red
[color2]=green
[color3]=yellow
[color4]=blue
[color5]=magenta
[color6]=cyan
[color7]=fg
[color8]=muted
[color9]=bright_red
[color10]=bright_green
[color11]=bright_yellow
[color12]=bright_blue
[color13]=bright_magenta
[color14]=bright_cyan
[color15]=bright_fg
)
for key in "${!ansi_alias[@]}"; do
alias_theme_color "$key" "${ansi_alias[$key]}"
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"
+21 -78
View File
@@ -7,84 +7,27 @@ theme="${1:-$HOME/.local/state/omarchy/current/theme/colors.toml}"
[[ -f $theme ]] || exit 0
awk -F= '
function clean(value) {
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
if (value ~ /^"/) {
sub(/^"/, "", value)
sub(/".*$/, "", value)
}
return value
declare -A COLORS
while IFS=$'\t' read -r key value; do
COLORS[$key]="$value"
done < <(omarchy-theme-color --file "$theme" --all)
emit_osc() {
local code="$1"
local key="$2"
[[ -n ${COLORS[$key]:-} ]] || return 0
printf '\033]%s;%s\007' "$code" "${COLORS[$key]}"
}
function alias_color(target, source) {
if (colors[target] == "" && colors[source] != "") colors[target] = colors[source]
}
emit_osc 10 foreground
emit_osc 11 background
emit_osc 12 cursor
emit_osc 17 selection_background
emit_osc 19 selection_foreground
function emit_color(color_index, key) {
if (colors[key] != "") printf "\033]4;%d;%s\007", color_index, colors[key]
}
{
key = $1
gsub(/^[[:space:]]+|[[:space:]]+$/, "", key)
value = clean($2)
colors[key] = value
}
END {
if (colors["background"] != "") colors["bg"] = colors["background"]
if (colors["foreground"] != "") colors["fg"] = colors["foreground"]
if (colors["bg"] != "") colors["background"] = colors["bg"]
if (colors["fg"] != "") colors["foreground"] = colors["fg"]
if (colors["bg"] != "") colors["color0"] = colors["bg"]
if (colors["fg"] != "") colors["color7"] = colors["fg"]
alias_color("color0", "bg")
alias_color("color0", "background")
alias_color("color1", "red")
alias_color("color2", "green")
alias_color("color3", "yellow")
alias_color("color4", "blue")
alias_color("color5", "magenta")
alias_color("color5", "purple")
alias_color("color6", "cyan")
alias_color("color7", "fg")
alias_color("color7", "foreground")
alias_color("color8", "muted")
alias_color("color8", "dark_fg")
alias_color("color8", "foreground")
alias_color("color9", "bright_red")
alias_color("color9", "red")
alias_color("color10", "bright_green")
alias_color("color10", "green")
alias_color("color11", "bright_yellow")
alias_color("color11", "yellow")
alias_color("color12", "bright_blue")
alias_color("color12", "blue")
alias_color("color13", "bright_magenta")
alias_color("color13", "bright_purple")
alias_color("color13", "magenta")
alias_color("color13", "purple")
alias_color("color14", "bright_cyan")
alias_color("color14", "cyan")
alias_color("color15", "bright_fg")
alias_color("color15", "foreground")
alias_color("light_fg", "color7")
alias_color("light_fg", "fg")
alias_color("bright_fg", "color15")
alias_color("bright_fg", "fg")
if (colors["bright_fg"] != "") colors["cursor"] = colors["bright_fg"]
if (colors["selection_background"] == "" && colors["selection"] != "") colors["selection_background"] = colors["selection"]
if (colors["selection_foreground"] == "") colors["selection_foreground"] = colors["bright_fg"]
if (colors["foreground"] != "") printf "\033]10;%s\007", colors["foreground"]
if (colors["background"] != "") printf "\033]11;%s\007", colors["background"]
if (colors["cursor"] != "") printf "\033]12;%s\007", colors["cursor"]
if (colors["selection_background"] != "") printf "\033]17;%s\007", colors["selection_background"]
if (colors["selection_foreground"] != "") printf "\033]19;%s\007", colors["selection_foreground"]
for (i = 0; i <= 15; i++) emit_color(i, "color" i)
}
' "$theme"
for i in {0..15}; do
[[ -n ${COLORS[color$i]:-} ]] || continue
printf '\033]4;%d;%s\007' "$i" "${COLORS[color$i]}"
done
+3 -27
View File
@@ -13,33 +13,9 @@ fi
THEME_DIR=$HOME/.local/state/omarchy/current/theme
COLORS_FILE="$THEME_DIR/colors.toml"
# In-tree light themes now declare `mode = "light"` in colors.toml instead of
# shipping a separate light.mode marker. Keep light.mode as a user-theme fallback.
theme_mode() {
[[ -f $COLORS_FILE ]] || return
awk -F= '
{
field = $1
gsub(/^[[:space:]]+|[[:space:]]+$/, "", field)
if (field == "mode") {
value = $2
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
if (value ~ /^"/) {
sub(/^"/, "", value)
sub(/".*$/, "", value)
}
print value
exit
}
}
' "$COLORS_FILE"
}
mode=$(theme_mode)
if [[ -z $mode && -f $THEME_DIR/light.mode ]]; then
mode="light"
fi
# omarchy-theme-color resolves mode from colors.toml (`mode = "light"`), a
# legacy light.mode marker beside it, or background luminance for user themes.
mode=$(omarchy-theme-color --file "$COLORS_FILE" mode)
if [[ $mode == "light" ]]; then
gsettings set org.gnome.desktop.interface color-scheme "prefer-light"
+4 -120
View File
@@ -367,30 +367,6 @@ apply_shell_section_overrides() {
done
}
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 $NEXT_THEME_DIR/light.mode ]]; then
THEME_COLORS[mode]="light"
elif [[ ${THEME_COLORS[bg]} =~ ^#[0-9A-Fa-f]{6}$ ]]; then
bg_hex="${THEME_COLORS[bg]#\#}"
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
}
# Only generate dynamic templates for themes with a colors.toml definition
if [[ -f $COLORS_FILE ]]; then
sed_script=$(mktemp)
@@ -398,103 +374,11 @@ if [[ -f $COLORS_FILE ]]; then
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)
# 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 <"$COLORS_FILE"
# Canonical palette keys are bg/fg. Preserve older themes that still define
# background/foreground by treating those semantic values as the source of
# truth, then expose the old names as template aliases for user templates.
[[ ${THEME_COLORS[background]} ]] && THEME_COLORS[bg]="${THEME_COLORS[background]}"
[[ ${THEME_COLORS[foreground]} ]] && THEME_COLORS[fg]="${THEME_COLORS[foreground]}"
[[ ${THEME_COLORS[bg]} ]] || THEME_COLORS[bg]="${THEME_COLORS[color0]}"
[[ ${THEME_COLORS[fg]} ]] || THEME_COLORS[fg]="${THEME_COLORS[color7]}"
[[ ${THEME_COLORS[bg]} ]] && THEME_COLORS[background]="${THEME_COLORS[bg]}"
[[ ${THEME_COLORS[fg]} ]] && THEME_COLORS[foreground]="${THEME_COLORS[fg]}"
[[ ${THEME_COLORS[bg]} ]] && THEME_COLORS[color0]="${THEME_COLORS[bg]}"
[[ ${THEME_COLORS[fg]} ]] && THEME_COLORS[color7]="${THEME_COLORS[fg]}"
# 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_fg]} ]] || THEME_COLORS[light_fg]="${THEME_COLORS[color7]:-${THEME_COLORS[fg]}}"
[[ ${THEME_COLORS[bright_fg]} ]] || THEME_COLORS[bright_fg]="${THEME_COLORS[color15]:-${THEME_COLORS[fg]}}"
THEME_COLORS[cursor]="${THEME_COLORS[bright_fg]}"
[[ ${THEME_COLORS[lighter_bg]} ]] || THEME_COLORS[lighter_bg]="${THEME_COLORS[color0]:-${THEME_COLORS[bg]}}"
[[ ${THEME_COLORS[dark_fg]} ]] || THEME_COLORS[dark_fg]="${THEME_COLORS[color8]:-${THEME_COLORS[fg]}}"
[[ ${THEME_COLORS[muted]} ]] || THEME_COLORS[muted]="${THEME_COLORS[color8]:-${THEME_COLORS[dark_fg]}}"
[[ ${THEME_COLORS[selection]} ]] || THEME_COLORS[selection]="${THEME_COLORS[selection_background]:-${THEME_COLORS[color8]:-${THEME_COLORS[color0]:-${THEME_COLORS[bg]}}}}"
[[ ${THEME_COLORS[selection_background]} ]] || THEME_COLORS[selection_background]="${THEME_COLORS[selection]}"
[[ ${THEME_COLORS[selection_foreground]} ]] || THEME_COLORS[selection_foreground]="${THEME_COLORS[bright_fg]}"
[[ ${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_bg]} ]] || THEME_COLORS[dark_bg]=$(mix_color "${THEME_COLORS[bg]}" "#000000" 25%)
[[ ${THEME_COLORS[darker_bg]} ]] || THEME_COLORS[darker_bg]=$(mix_color "${THEME_COLORS[bg]}" "#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 user templates that still reference
# the legacy ANSI placeholders directly.
declare -A ansi_alias=(
[color0]=bg
[color1]=red
[color2]=green
[color3]=yellow
[color4]=blue
[color5]=magenta
[color6]=cyan
[color7]=fg
[color8]=muted
[color9]=bright_red
[color10]=bright_green
[color11]=bright_yellow
[color12]=bright_blue
[color13]=bright_magenta
[color14]=bright_cyan
[color15]=bright_fg
)
for key in "${!ansi_alias[@]}"; do
alias_theme_color "$key" "${ansi_alias[$key]}"
done
# Resolve theme mode (dark|light) with this precedence:
# 1. `mode` key in colors.toml
# 2. legacy `light.mode` file shipped beside the theme
# 3. background luminance auto-detect
# `theme_type` is kept as an alias so existing templates ({{ theme_type }}) keep working.
resolve_theme_mode
THEME_COLORS[theme_type]="${THEME_COLORS[mode]}"
done < <(omarchy-theme-color --file "$COLORS_FILE" --all)
for key in "${!THEME_COLORS[@]}"; do
add_template_value "$key" "${THEME_COLORS[$key]}"
+6 -34
View File
@@ -37,11 +37,10 @@ sync_theme_environment() {
sync_colorfgbg() {
local colorfgbg="15;0"
local mode
mode=$(theme_color mode)
if [[ $mode == "light" ]] || [[ -z $mode && -f $CURRENT_THEME_PATH/light.mode ]]; then
# theme_color resolves mode from colors.toml, a legacy light.mode file, or
# background luminance, so a bare light check covers all theme styles.
if [[ $(theme_color mode) == "light" ]]; then
colorfgbg="0;15"
fi
@@ -53,34 +52,7 @@ theme_osc_sequences() {
}
theme_color() {
local key="$1"
local fallback="${2:-}"
awk -F= -v key="$key" -v fallback="$fallback" '
function clean(raw) {
gsub(/^[[:space:]]+|[[:space:]]+$/, "", raw)
if (raw ~ /^"/) {
sub(/^"/, "", raw)
sub(/".*$/, "", raw)
}
return raw
}
{
field = $1
gsub(/^[[:space:]]+|[[:space:]]+$/, "", field)
if (field == key) {
print clean($2)
found = 1
exit
}
if (fallback != "" && field == fallback) fallback_value = clean($2)
}
END {
if (!found && fallback_value != "") print fallback_value
}
' "$COLORS_TOML"
omarchy-theme-color --file "$COLORS_TOML" "$@"
}
sync_tmux_window_style() {
@@ -88,8 +60,8 @@ sync_tmux_window_style() {
[[ -f $COLORS_TOML ]] || return
foreground=$(theme_color foreground fg)
background=$(theme_color background bg)
foreground=$(theme_color foreground)
background=$(theme_color background)
[[ -n $foreground && -n $background ]] || return