Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/omarchy-theme-colors-from-alacritty
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ background=${background:-$color0}
foreground=${foreground:-$color7}
color0=$background
color7=$foreground
selection_background=${selection_background:-$foreground}
selection_background=${selection_background:-$color8}
accent=$color4

mkdir -p "$THEME_SOURCE"
Expand Down
31 changes: 31 additions & 0 deletions bin/omarchy-theme-set
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,30 @@ stage_installed_colors_from_alacritty() {
rm -rf "$scratch"
}

# Old omarchy-theme-colors-from-alacritty wrote selection = foreground when
# alacritty.toml had no [colors.selection], which is invisible in Neovim, gum,
# and btop. Theme-set skips regeneration when colors.toml already exists, so an
# extra/git theme that committed that fingerprint would be recopied forever.
# Rewrite only the generated `selection = "#hex"` line, and only when it equals
# foreground, to color8 (bright black, falling back to normal black).
repair_stale_generated_selection() {
local file="$1"
local selection foreground color8 color0 replacement

[[ -f $file && ! -L $file ]] || return 0

selection=$(awk -F'"' '/^selection = /{print $2; exit}' "$file")
foreground=$(awk -F'"' '/^foreground = /{print $2; exit}' "$file")
[[ -n $selection && $selection == "$foreground" ]] || return 0

color8=$(awk -F'"' '/^color8 = /{print $2; exit}' "$file")
color0=$(awk -F'"' '/^color0 = /{print $2; exit}' "$file")
replacement=${color8:-$color0}
[[ -n $replacement && $replacement != "$foreground" ]] || return 0

sed -i "s|^selection = \"$selection\"$|selection = \"$replacement\"|" "$file"
}

# Copies a directory without ever following a symlink: in an installed theme one
# points wherever the theme author chose, which is how an unlock.png becomes a
# copy of any file the session can read.
Expand Down Expand Up @@ -280,6 +304,13 @@ if [[ ! -f $NEXT_THEME_PATH/colors.toml && -f $NEXT_THEME_PATH/alacritty.toml ]]
omarchy-theme-colors-from-alacritty "$NEXT_THEME_PATH"
fi

# Repair a baked-in generated fingerprint on the staged copy that is about to
# become current. The user/extra source is left untouched -- it may be a git
# clone or a symlink into the user's own dotfiles -- so this repeats on every
# switch of an affected theme, which is harmless. Official bundled palettes
# ship selection != foreground and are left alone.
repair_stale_generated_selection "$NEXT_THEME_PATH/colors.toml"

# Generate dynamic configs
omarchy-theme-set-templates

Expand Down
55 changes: 55 additions & 0 deletions migrations/1788139189.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
echo "Refresh the current theme if its selection color is invisible against the foreground"

# omarchy-theme-colors-from-alacritty used to fall back to foreground for
# selection when a theme's alacritty.toml had no [colors.selection], leaving
# Neovim's Visual highlight, gum prompts, and btop's selected row invisible.
# A theme active from before that fix keeps the stale value baked into its
# materialized colors.toml until something regenerates it. No authored theme
# sets selection equal to foreground on purpose -- that pairing is exactly
# what made the highlight invisible -- so refreshing on this fingerprint is
# safe for every theme, and a no-op for one whose colors.toml was hand-authored
# straight through without regeneration.
#
# omarchy-theme-set used to recopy a baked-in source colors.toml as-is, so
# refresh alone did not repair an extra/git theme that committed the old
# fingerprint. It now rewrites selection to color8 (bright black, falling
# back to normal black) on the staged copy when that fingerprint is present,
# before templates are generated from the repaired palette. The source
# colors.toml itself -- which may be a git clone or a symlink into the
# user's own dotfiles -- is left untouched, so this repair repeats on every
# switch of an affected theme, harmlessly.

theme_name_path="$HOME/.local/state/omarchy/current/theme.name"

[[ -s $theme_name_path ]] || exit 0

theme_name=$(<"$theme_name_path")

# A theme removed while it was still current leaves theme.name naming it and the
# staged copy behind, so omarchy-theme-refresh would exit 1 ("Theme does not
# exist") and stall every later migration. Seed the default instead, which is
# where a fresh install starts and what the removal should have left.
if [[ ! -d $OMARCHY_PATH/themes/$theme_name && ! -d $HOME/.config/omarchy/themes/$theme_name ]]; then
echo "Theme '$theme_name' no longer exists; applying the default instead"
omarchy-theme-set "Tokyo Night"
exit 0
fi

current_colors="$HOME/.local/state/omarchy/current/theme/colors.toml"
source_colors="$HOME/.config/omarchy/themes/$theme_name/colors.toml"

stale=0
if [[ -f $current_colors ]]; then
selection=$(awk -F'"' '/^selection = /{print $2; exit}' "$current_colors")
foreground=$(awk -F'"' '/^foreground = /{print $2; exit}' "$current_colors")
[[ -n $selection && $selection == "$foreground" ]] && stale=1
fi
if [[ -f $source_colors ]]; then
selection=$(awk -F'"' '/^selection = /{print $2; exit}' "$source_colors")
foreground=$(awk -F'"' '/^foreground = /{print $2; exit}' "$source_colors")
[[ -n $selection && $selection == "$foreground" ]] && stale=1
fi

(( stale )) || exit 0

omarchy-theme-refresh
56 changes: 56 additions & 0 deletions test/cli
Original file line number Diff line number Diff line change
Expand Up @@ -807,3 +807,59 @@ assert_output_contains "a --help behind -- belongs to the command and still forw

output=$("$TMPDIR/omarchy" parenthelp --helpme x--help)
assert_output_contains "help lookalike tokens do not trigger help" "$output" "parenthelp-parent-ran args=[--helpme x--help]"

# A theme that ships no [colors.selection] must not end up with selection ==
# foreground, since that makes selections/highlights invisible downstream.
make_tmpdir ALACRITTY_THEME_TMPDIR

cat >"$ALACRITTY_THEME_TMPDIR/alacritty.toml" <<'EOF'
[colors.primary]
background = "#101010"
foreground = "#f0f0f0"

[colors.normal]
black = "#111111"
red = "#ff0000"
green = "#00ff00"
yellow = "#ffff00"
blue = "#0000ff"
magenta = "#ff00ff"
cyan = "#00ffff"
white = "#f0f0f0"

[colors.bright]
black = "#222222"
red = "#ff1111"
green = "#11ff11"
yellow = "#ffff11"
blue = "#1111ff"
magenta = "#ff11ff"
cyan = "#11ffff"
white = "#ffffff"
EOF

"$ROOT/bin/omarchy-theme-colors-from-alacritty" "$ALACRITTY_THEME_TMPDIR"
grep -Fqx 'selection = "#222222"' "$ALACRITTY_THEME_TMPDIR/colors.toml" || fail "theme without [colors.selection] falls back to bright black instead of foreground"
pass "theme without [colors.selection] falls back to bright black instead of foreground"

make_tmpdir ALACRITTY_NO_BRIGHT_TMPDIR

cat >"$ALACRITTY_NO_BRIGHT_TMPDIR/alacritty.toml" <<'EOF'
[colors.primary]
background = "#101010"
foreground = "#f0f0f0"

[colors.normal]
black = "#111111"
red = "#ff0000"
green = "#00ff00"
yellow = "#ffff00"
blue = "#0000ff"
magenta = "#ff00ff"
cyan = "#00ffff"
white = "#f0f0f0"
EOF

"$ROOT/bin/omarchy-theme-colors-from-alacritty" "$ALACRITTY_NO_BRIGHT_TMPDIR"
grep -Fqx 'selection = "#111111"' "$ALACRITTY_NO_BRIGHT_TMPDIR/colors.toml" || fail "theme without [colors.selection] or [colors.bright] falls back to normal black instead of foreground"
pass "theme without [colors.selection] or [colors.bright] falls back to normal black instead of foreground"
165 changes: 165 additions & 0 deletions test/shell.d/theme-selection-color-migration-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#!/bin/bash

set -euo pipefail

source "$(dirname "${BASH_SOURCE[0]}")/base-test.sh"

migration="$ROOT/migrations/1788139189.sh"
test_dir=$(mktemp -d)
trap 'rm -rf "$test_dir"' EXIT

home="$test_dir/home"
omarchy_path="$test_dir/omarchy"
current_theme_dir="$home/.local/state/omarchy/current/theme"
theme_name_path="$home/.local/state/omarchy/current/theme.name"
current_colors="$current_theme_dir/colors.toml"
user_themes="$home/.config/omarchy/themes"
mkdir -p "$current_theme_dir" "$omarchy_path/themes" "$user_themes"

stub_bin="$test_dir/bin"
mkdir -p "$stub_bin"
refresh_called="$test_dir/refresh-called"
theme_set_args="$test_dir/theme-set-args"

cat >"$stub_bin/omarchy-theme-refresh" <<STUB
#!/bin/bash
touch "$refresh_called"
STUB
cat >"$stub_bin/omarchy-theme-set" <<STUB
#!/bin/bash
printf '%s\n' "\$*" >>"$theme_set_args"
STUB
cat >"$stub_bin/omarchy-notification-dismiss" <<'STUB'
#!/bin/bash
exit 0
STUB
chmod +x "$stub_bin"/*

write_stale_colors() {
cat >"$1" <<'COLORS'
accent = "#336699"
selection = "#f0f0f0"

background = "#101010"
foreground = "#f0f0f0"

color0 = "#111111"
color8 = "#222222"
COLORS
}

write_healthy_colors() {
cat >"$1" <<'COLORS'
accent = "#336699"
selection = "#222222"

background = "#101010"
foreground = "#f0f0f0"

color0 = "#111111"
color8 = "#222222"
COLORS
}

present_theme() {
local name="$1"
mkdir -p "$user_themes/$name"
printf '%s\n' "$name" >"$theme_name_path"
}

run_migration() {
rm -f "$refresh_called" "$theme_set_args"
HOME="$home" OMARCHY_PATH="$omarchy_path" PATH="$stub_bin:$PATH" \
bash -euo pipefail "$migration" >/dev/null
}

# An affected theme has an invisible selection: it exactly matches foreground.
present_theme extra-stale
write_stale_colors "$current_colors"
run_migration
[[ -e $refresh_called ]] || fail "migration refreshes a theme whose selection matches foreground"
[[ ! -e $theme_set_args ]] || fail "migration refreshes a present theme rather than seeding the default"
pass "migration refreshes a theme whose selection matches foreground"

# A baked-in source colors.toml with the same fingerprint also triggers refresh,
# even if the staged copy was already rewritten.
write_healthy_colors "$current_colors"
write_stale_colors "$user_themes/extra-stale/colors.toml"
run_migration
[[ -e $refresh_called ]] || fail "migration refreshes when the extra theme's source colors.toml is stale"
pass "migration refreshes when the extra theme's source colors.toml is stale"
rm -f "$user_themes/extra-stale/colors.toml"

# A healthy theme, where selection differs from foreground, is left alone.
write_healthy_colors "$current_colors"
run_migration
[[ ! -e $refresh_called ]] || fail "migration leaves a theme whose selection already differs from foreground"
pass "migration leaves a theme whose selection already differs from foreground"

# No current theme (e.g. a fresh install that has not set one yet) is a no-op.
rm -f "$current_colors" "$theme_name_path"
run_migration
[[ ! -e $refresh_called ]] || fail "migration no-ops when there is no current theme"
[[ ! -e $theme_set_args ]] || fail "migration does not seed a default when theme.name is missing"
pass "migration no-ops when there is no current theme"

# A theme.name with no corresponding dirs must not fail the migrate queue.
# Seed Tokyo Night the way 1787481315 does, then let later migrations run.
write_stale_colors "$current_colors"
printf 'gone-extra\n' >"$theme_name_path"

set +e
HOME="$home" OMARCHY_PATH="$omarchy_path" PATH="$stub_bin:$PATH" \
bash -euo pipefail "$migration" >/dev/null
removed_status=$?
set -e
[[ $removed_status -eq 0 ]] || fail "migration succeeds when the current theme was removed"
[[ -e $theme_set_args ]] || fail "migration seeds Tokyo Night when the current theme was removed"
grep -Fxq 'Tokyo Night' "$theme_set_args" || fail "migration seeds Tokyo Night when the current theme was removed" "$(cat "$theme_set_args")"
[[ ! -e $refresh_called ]] || fail "migration does not refresh a theme that no longer exists"
pass "migration seeds Tokyo Night when the current theme was removed"

# Prove the migrate runner writes the marker and continues past this file.
migrations_root="$test_dir/migrate-root"
mkdir -p "$migrations_root/migrations"
cp "$migration" "$migrations_root/migrations/1788139189.sh"
cat >"$migrations_root/migrations/1788139190.sh" <<'LATER'
echo later-ran >>"$TEST_LATER"
LATER

later="$test_dir/later"
: >"$later"
write_stale_colors "$current_colors"
printf 'gone-extra\n' >"$theme_name_path"
rm -f "$refresh_called" "$theme_set_args"

HOME="$home" OMARCHY_PATH="$migrations_root" PATH="$stub_bin:$ROOT/bin:$PATH" \
TEST_LATER="$later" \
"$ROOT/bin/omarchy-migrate" >/dev/null

[[ -f $home/.local/state/omarchy/migrations/1788139189.sh ]] ||
fail "migrate writes a marker when the current theme was removed"
[[ -f $home/.local/state/omarchy/migrations/1788139190.sh ]] ||
fail "migrate still runs later migrations after a removed current theme"
grep -Fxq 'later-ran' "$later" || fail "later migration body ran after a removed current theme"
grep -Fxq 'Tokyo Night' "$theme_set_args" || fail "removed-theme migrate seeds Tokyo Night"
pass "removed current theme does not fail migrate or skip later migrations"

# Missing theme.name is also a successful no-op that still lets later migrates run.
rm -rf "$home/.local/state/omarchy/migrations"
rm -f "$theme_name_path" "$theme_set_args" "$refresh_called"
write_stale_colors "$current_colors"
: >"$later"

HOME="$home" OMARCHY_PATH="$migrations_root" PATH="$stub_bin:$ROOT/bin:$PATH" \
TEST_LATER="$later" \
"$ROOT/bin/omarchy-migrate" >/dev/null

[[ -f $home/.local/state/omarchy/migrations/1788139189.sh ]] ||
fail "migrate writes a marker when theme.name is missing"
[[ -f $home/.local/state/omarchy/migrations/1788139190.sh ]] ||
fail "migrate still runs later migrations when theme.name is missing"
grep -Fxq 'later-ran' "$later" || fail "later migration body ran when theme.name is missing"
[[ ! -e $refresh_called ]] || fail "missing theme.name does not refresh"
[[ ! -e $theme_set_args ]] || fail "missing theme.name does not seed a default"
pass "missing theme.name does not fail migrate or skip later migrations"
Loading