A theme switch that plays an intro gave the shell the new still only when the intro failed. The shell kept the old theme's path, which the theme swap deletes, and loaded it again when OWE handed the desktop back, leaving it empty. The new still is now handed over either way. The shell also re-reads the background link whenever OWE hands the desktop back or a wallpaper fails to load, falling back to the theme's first background when the link itself is broken. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
533 lines
19 KiB
Bash
Executable File
533 lines
19 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# omarchy:summary=Apply an Omarchy theme
|
|
# omarchy:args=<theme-name>
|
|
# omarchy:examples=omarchy theme list | omarchy theme set "Tokyo Night"
|
|
|
|
if [[ -z $1 ]]; then
|
|
echo "Usage: omarchy-theme-set <theme-name>"
|
|
exit 1
|
|
fi
|
|
|
|
CURRENT_THEME_PATH="$HOME/.local/state/omarchy/current/theme"
|
|
CURRENT_THEME_NAME_PATH="$HOME/.local/state/omarchy/current/theme.name"
|
|
NEXT_THEME_PATH="$HOME/.local/state/omarchy/current/next-theme"
|
|
CURRENT_BACKGROUND_LINK="$HOME/.local/state/omarchy/current/background"
|
|
THEME_BACKGROUND_STATE_PATH="$HOME/.local/state/omarchy/theme-backgrounds"
|
|
BACKGROUND_TRANSITION_CACHE="$HOME/.cache/omarchy/background-transitions"
|
|
THEME_SET_LOCK="${XDG_RUNTIME_DIR:-/tmp}/omarchy-theme-set.lock"
|
|
USER_THEMES_PATH="$HOME/.config/omarchy/themes"
|
|
OMARCHY_THEMES_PATH="$OMARCHY_PATH/themes"
|
|
|
|
# What a theme installed from a git repo may not ship, because these run code.
|
|
# Hyprland requires a theme's hyprland.lua and gum_env.lua at login and Neovim
|
|
# loads its neovim.lua at startup, so no .lua from such a theme is staged at all.
|
|
# Each terminal config names the program the terminal launches, and vscode.json
|
|
# names an extension omarchy-theme-set-vscode installs, a VS Code extension being
|
|
# arbitrary JavaScript. Everything else a theme ships is colour and is kept.
|
|
#
|
|
# Adding a template for another terminal, or for another editor that loads Lua,
|
|
# means adding it here. test/shell.d/theme-staging-test.sh fails on a generated
|
|
# theme file that is neither denied here nor recorded there as colour-only.
|
|
INSTALLED_THEME_DENIED=(alacritty.toml foot.ini ghostty.conf kitty.conf vscode.json)
|
|
IGNORED_THEME_FILES=()
|
|
|
|
run_parallel() {
|
|
local pid
|
|
local pids=()
|
|
|
|
for command in "$@"; do
|
|
bash -lc "$command" &
|
|
pids+=("$!")
|
|
done
|
|
|
|
for pid in "${pids[@]}"; do
|
|
wait "$pid"
|
|
done
|
|
}
|
|
|
|
shell_ipc() {
|
|
timeout 2 omarchy-shell "$@" >/dev/null 2>&1
|
|
}
|
|
|
|
wait_for_theme_intro() {
|
|
local method="$1" token="$2" pending="$3" deadline=$((SECONDS + 3))
|
|
while (( SECONDS < deadline )) && [[ $(omarchy-shell shell "$method" "$token" 2>/dev/null) == "$pending" ]]; do
|
|
sleep 0.02
|
|
done
|
|
}
|
|
|
|
is_video_path() {
|
|
[[ ${1,,} =~ \.(mp4|m4v|mov|webm|mkv|avi)$ ]]
|
|
}
|
|
|
|
snapshot_background_path() {
|
|
local background="$1"
|
|
local name="$2"
|
|
local snapshot extension
|
|
|
|
[[ -f $background ]] || return
|
|
is_video_path "$background" && return
|
|
|
|
mkdir -p "$BACKGROUND_TRANSITION_CACHE"
|
|
extension=${background##*.}
|
|
snapshot="$BACKGROUND_TRANSITION_CACHE/$name-$$.$extension"
|
|
ln "$background" "$snapshot" 2>/dev/null || cp "$background" "$snapshot"
|
|
echo "$snapshot"
|
|
}
|
|
|
|
snapshot_current_background() {
|
|
local current_background snapshot
|
|
|
|
# An interrupted intro is still moving; its final still would flash as a
|
|
# cover. Capture the frame already playing instead, when OWE supports it.
|
|
mkdir -p "$BACKGROUND_TRANSITION_CACHE"
|
|
snapshot="$BACKGROUND_TRANSITION_CACHE/previous-$$.ppm"
|
|
if owe render "$(jq -nc --arg path "$snapshot" '{cmd: "snapshot", path: $path}')" >/dev/null 2>&1; then
|
|
echo "$snapshot"
|
|
else
|
|
current_background=$(readlink -f "$CURRENT_BACKGROUND_LINK" 2>/dev/null || true)
|
|
snapshot_background_path "$current_background" "previous"
|
|
fi
|
|
}
|
|
|
|
theme_background_state_file() {
|
|
local theme="$1"
|
|
|
|
[[ -n $theme && ${theme##*/} == $theme && $theme != "." && $theme != ".." ]] || return 1
|
|
printf '%s/%s\n' "$THEME_BACKGROUND_STATE_PATH" "$theme"
|
|
}
|
|
|
|
remember_current_theme_background() {
|
|
local current_background state_file
|
|
|
|
state_file=$(theme_background_state_file "$PREVIOUS_THEME_NAME") || return
|
|
current_background=$(readlink -f "$CURRENT_BACKGROUND_LINK" 2>/dev/null || true)
|
|
[[ -f $current_background ]] || return
|
|
|
|
mkdir -p "$THEME_BACKGROUND_STATE_PATH"
|
|
printf '%s\n' "$current_background" >"$state_file"
|
|
}
|
|
|
|
background_transition_uses_snapshots() {
|
|
local next_background="$1"
|
|
local current_background
|
|
|
|
current_background=$(readlink -f "$CURRENT_BACKGROUND_LINK" 2>/dev/null || true)
|
|
! is_video_path "$current_background" && ! is_video_path "$next_background"
|
|
}
|
|
|
|
choose_theme_background() {
|
|
local theme_path="${1:-$CURRENT_THEME_PATH}"
|
|
local current_theme_backgrounds="$CURRENT_THEME_PATH/backgrounds"
|
|
local backgrounds=()
|
|
local current_background remembered_background state_file index next_index i
|
|
|
|
CHOSEN_THEME_BACKGROUND=""
|
|
mapfile -d '' -t backgrounds < <(
|
|
find -L "$HOME/.config/omarchy/backgrounds/$THEME_NAME/" "$theme_path/backgrounds/" -maxdepth 1 -type f \
|
|
\( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.gif' -o -iname '*.bmp' -o -iname '*.webp' \
|
|
-o -iname '*.mp4' -o -iname '*.m4v' -o -iname '*.mov' -o -iname '*.webm' -o -iname '*.mkv' -o -iname '*.avi' \) \
|
|
-print0 2>/dev/null | sort -z
|
|
)
|
|
|
|
(( ${#backgrounds[@]} > 0 )) || return 1
|
|
|
|
if [[ $PREVIOUS_THEME_NAME == "$THEME_NAME" ]]; then
|
|
current_background=$(readlink "$CURRENT_BACKGROUND_LINK" 2>/dev/null || true)
|
|
if [[ $theme_path != $CURRENT_THEME_PATH && ${current_background%/*} == $current_theme_backgrounds ]]; then
|
|
current_background="$theme_path/backgrounds/${current_background##*/}"
|
|
fi
|
|
|
|
index=-1
|
|
for i in "${!backgrounds[@]}"; do
|
|
if [[ ${backgrounds[$i]} == $current_background ]]; then
|
|
index=$i
|
|
break
|
|
fi
|
|
done
|
|
|
|
if (( index != -1 )); then
|
|
next_index=$(((index + 1) % ${#backgrounds[@]}))
|
|
CHOSEN_THEME_BACKGROUND="${backgrounds[$next_index]}"
|
|
else
|
|
CHOSEN_THEME_BACKGROUND="${backgrounds[0]}"
|
|
fi
|
|
return
|
|
fi
|
|
|
|
state_file=$(theme_background_state_file "$THEME_NAME" 2>/dev/null || true)
|
|
remembered_background=$(cat "$state_file" 2>/dev/null || true)
|
|
if [[ $theme_path != $CURRENT_THEME_PATH && ${remembered_background%/*} == $current_theme_backgrounds ]]; then
|
|
remembered_background="$theme_path/backgrounds/${remembered_background##*/}"
|
|
fi
|
|
if [[ -f $remembered_background ]]; then
|
|
CHOSEN_THEME_BACKGROUND="$remembered_background"
|
|
return
|
|
fi
|
|
|
|
CHOSEN_THEME_BACKGROUND="${backgrounds[0]}"
|
|
}
|
|
|
|
choose_staged_theme_background() {
|
|
local next_theme_backgrounds="$NEXT_THEME_PATH/backgrounds"
|
|
|
|
choose_theme_background "$NEXT_THEME_PATH" || return 1
|
|
STAGED_THEME_BACKGROUND="$CHOSEN_THEME_BACKGROUND"
|
|
|
|
if [[ ${CHOSEN_THEME_BACKGROUND%/*} == $next_theme_backgrounds ]]; then
|
|
CHOSEN_THEME_BACKGROUND="$CURRENT_THEME_PATH/backgrounds/${CHOSEN_THEME_BACKGROUND##*/}"
|
|
fi
|
|
}
|
|
|
|
set_theme_background_link() {
|
|
choose_theme_background || return 1
|
|
ln -nsf "$CHOSEN_THEME_BACKGROUND" "$CURRENT_BACKGROUND_LINK"
|
|
}
|
|
|
|
set_theme_background() {
|
|
local new_background new_background_snapshot theme_id intro_prepared=false
|
|
|
|
if [[ -z $CHOSEN_THEME_BACKGROUND || ! -f $CHOSEN_THEME_BACKGROUND ]]; then
|
|
if ! choose_theme_background; then
|
|
omarchy-notification-send "No background was found for theme" -t 2000
|
|
shell_ipc shell applyTheme "$colors_payload" "$shell_payload" || true
|
|
return
|
|
fi
|
|
fi
|
|
|
|
new_background="$CHOSEN_THEME_BACKGROUND"
|
|
if [[ -n $THEME_BACKGROUND_INTRO ]]; then
|
|
if [[ -n $INTRO_PREPARE_PID ]] && wait "$INTRO_PREPARE_PID"; then
|
|
intro_prepared=true
|
|
fi
|
|
theme_id=$(stat -Lc '%d:%i' "$CURRENT_THEME_PATH")
|
|
THEME_INTRO_TOKEN="$theme_id"
|
|
shell_ipc shell prepareThemeIntro "$OLD_BACKGROUND_SNAPSHOT" "$theme_id" "$colors_payload" "$shell_payload" || \
|
|
shell_ipc shell applyTheme "$colors_payload" "$shell_payload" || true
|
|
ln -nsf "$new_background" "$CURRENT_BACKGROUND_LINK"
|
|
(
|
|
# Played or not, the shell gets the new still: it still holds the old
|
|
# theme's path, which the theme swap above deleted, and loads it again
|
|
# when OWE hands the desktop back after the intro. While the intro plays
|
|
# the shell's background is suspended under it, so the swap is unseen.
|
|
omarchy-theme-bg-boot-intro --theme-switch "$new_background" "$theme_id" "$intro_prepared" || true
|
|
if [[ $(readlink -f "$CURRENT_BACKGROUND_LINK") == "$(readlink -f "$new_background")" &&
|
|
$(stat -Lc '%d:%i' "$CURRENT_THEME_PATH" 2>/dev/null) == "$theme_id" ]]; then
|
|
shell_ipc background setInstant "$new_background" || true
|
|
fi
|
|
shell_ipc shell finishThemeIntro "$theme_id" || true
|
|
wait_for_theme_intro themeIntroStatus "$theme_id" "pending"
|
|
# Reloading Hyprland interrupts video presentation. Wait for playback,
|
|
# then serialize with theme selection so an old intro cannot reload
|
|
# the compositor during the next theme's video.
|
|
(
|
|
flock 9
|
|
if [[ $(stat -Lc '%d:%i' "$CURRENT_THEME_PATH" 2>/dev/null) == "$theme_id" ]]; then
|
|
omarchy-restart-hyprctl
|
|
fi
|
|
) 9>"$THEME_SET_LOCK"
|
|
[[ -z $OLD_BACKGROUND_SNAPSHOT ]] || rm -f "$OLD_BACKGROUND_SNAPSHOT"
|
|
) 9>&- &
|
|
return
|
|
fi
|
|
new_background_snapshot=""
|
|
if [[ -n $PREPARED_BACKGROUND_SNAPSHOT && $PREPARED_BACKGROUND == "$new_background" ]]; then
|
|
new_background_snapshot="$PREPARED_BACKGROUND_SNAPSHOT"
|
|
else
|
|
[[ -z $PREPARED_BACKGROUND_SNAPSHOT ]] || rm -f "$PREPARED_BACKGROUND_SNAPSHOT"
|
|
if [[ $BACKGROUND_TRANSITION_SNAPSHOTS == "true" ]]; then
|
|
new_background_snapshot=$(snapshot_background_path "$new_background" "next")
|
|
fi
|
|
fi
|
|
|
|
if [[ -f $OLD_BACKGROUND_SNAPSHOT && -f $new_background_snapshot ]]; then
|
|
shell_ipc background themeTransition "$OLD_BACKGROUND_SNAPSHOT" "$new_background_snapshot" "$new_background" "$colors_payload" "$shell_payload" || \
|
|
shell_ipc shell applyTheme "$colors_payload" "$shell_payload" || true
|
|
(sleep 3; rm -f "$OLD_BACKGROUND_SNAPSHOT" "$new_background_snapshot") &
|
|
elif [[ -f $new_background_snapshot ]]; then
|
|
shell_ipc background themeTransition "" "$new_background_snapshot" "$new_background" "$colors_payload" "$shell_payload" || \
|
|
shell_ipc shell applyTheme "$colors_payload" "$shell_payload" || true
|
|
(sleep 3; rm -f "$new_background_snapshot") &
|
|
else
|
|
shell_ipc background themeTransition "$OLD_BACKGROUND_SNAPSHOT" "$new_background" "$new_background" "$colors_payload" "$shell_payload" || \
|
|
shell_ipc shell applyTheme "$colors_payload" "$shell_payload" || true
|
|
if [[ -f $OLD_BACKGROUND_SNAPSHOT ]]; then
|
|
(sleep 3; rm -f "$OLD_BACKGROUND_SNAPSHOT") &
|
|
fi
|
|
fi
|
|
|
|
ln -nsf "$new_background" "$CURRENT_BACKGROUND_LINK"
|
|
}
|
|
|
|
is_denied_installed_file() {
|
|
local name="$1"
|
|
local denied
|
|
|
|
[[ $name == *.lua ]] && return 0
|
|
|
|
for denied in "${INSTALLED_THEME_DENIED[@]}"; do
|
|
[[ $name == "$denied" ]] && return 0
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
# Themes older than colors.toml still get their palette, but their
|
|
# alacritty.toml never reaches the staged theme: an Alacritty config names the
|
|
# program the terminal launches.
|
|
stage_installed_colors_from_alacritty() {
|
|
local source="$1"
|
|
local scratch
|
|
|
|
if [[ -f $NEXT_THEME_PATH/colors.toml ]]; then
|
|
return
|
|
fi
|
|
|
|
if [[ ! -f $source/alacritty.toml || -L $source/alacritty.toml ]]; then
|
|
return
|
|
fi
|
|
|
|
scratch=$(mktemp -d)
|
|
cp "$source/alacritty.toml" "$scratch/alacritty.toml"
|
|
omarchy-theme-colors-from-alacritty "$scratch"
|
|
|
|
if [[ -f $scratch/colors.toml ]]; then
|
|
cp "$scratch/colors.toml" "$NEXT_THEME_PATH/colors.toml"
|
|
fi
|
|
|
|
rm -rf "$scratch"
|
|
}
|
|
|
|
# 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.
|
|
stage_installed_dir() {
|
|
local source="$1"
|
|
local dest="$2"
|
|
local entry name
|
|
|
|
mkdir -p "$dest"
|
|
|
|
for entry in "$source"/*; do
|
|
[[ -e $entry && ! -L $entry ]] || continue
|
|
name=${entry##*/}
|
|
|
|
if [[ -d $entry ]]; then
|
|
stage_installed_dir "$entry" "$dest/$name"
|
|
else
|
|
cp "$entry" "$dest/$name"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# `omarchy theme install` clones into ~/.config/omarchy/themes, so a .git
|
|
# directory there means the contents came from a stranger and are held to the
|
|
# list above. A directory the user wrote themselves, and a symlink to their own
|
|
# working copy, are theirs to fill however they like -- the same distinction
|
|
# omarchy-theme-extras draws when it decides which themes it may pull.
|
|
theme_came_from_a_repo() {
|
|
local source="$1"
|
|
|
|
[[ ! -L $source && -d $source/.git ]]
|
|
}
|
|
|
|
stage_installed_theme() {
|
|
local source="$1"
|
|
local entry name
|
|
|
|
[[ -d $source ]] || return 0
|
|
|
|
for entry in "$source"/*; do
|
|
[[ -e $entry ]] || continue
|
|
name=${entry##*/}
|
|
|
|
if [[ -L $entry ]] || is_denied_installed_file "$name"; then
|
|
case "${name,,}" in
|
|
readme* | license* | changelog* | *.md | *.txt) ;;
|
|
*) IGNORED_THEME_FILES+=("$name") ;;
|
|
esac
|
|
elif [[ -d $entry ]]; then
|
|
stage_installed_dir "$entry" "$NEXT_THEME_PATH/$name"
|
|
else
|
|
cp "$entry" "$NEXT_THEME_PATH/$name"
|
|
fi
|
|
done
|
|
|
|
stage_installed_colors_from_alacritty "$source"
|
|
}
|
|
|
|
report_ignored_theme_files() {
|
|
(( ${#IGNORED_THEME_FILES[@]} > 0 )) || return 0
|
|
|
|
echo "Ignored in $USER_THEMES_PATH/$THEME_NAME: ${IGNORED_THEME_FILES[*]}" >&2
|
|
echo "A theme installed from a git repo cannot supply Lua, a terminal config, or vscode.json." >&2
|
|
}
|
|
|
|
THEME_NAME=$(echo "$1" | sed -E 's/<[^>]+>//g' | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
|
|
THEME_HEADLESS=0
|
|
if [[ ${OMARCHY_THEME_HEADLESS:-} == "1" || ${OMARCHY_THEME_OFFLINE:-} == "1" ]]; then
|
|
THEME_HEADLESS=1
|
|
fi
|
|
|
|
if [[ -z $THEME_NAME || $THEME_NAME == .* || $THEME_NAME == */* ]]; then
|
|
echo "Invalid theme name: $1"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -d $OMARCHY_THEMES_PATH/$THEME_NAME ]] && [[ ! -d $USER_THEMES_PATH/$THEME_NAME ]]; then
|
|
echo "Theme '$THEME_NAME' does not exist"
|
|
exit 1
|
|
fi
|
|
|
|
# Serialize theme changes. Theme switching rebuilds a shared next-theme staging
|
|
# directory and updates current theme/background symlinks; concurrent calls can
|
|
# otherwise race and make one selection appear to be ignored.
|
|
exec 9>"$THEME_SET_LOCK"
|
|
flock 9
|
|
|
|
# A current video frame only represents the screen after its opening fade.
|
|
# Finish that short reveal before capturing the next outgoing cover.
|
|
if [[ $THEME_HEADLESS != "1" && $OMARCHY_THEME_SKIP_BACKGROUND != "1" ]]; then
|
|
wait_for_theme_intro themeIntroStatus "$(stat -Lc '%d:%i' "$CURRENT_THEME_PATH" 2>/dev/null)" "pending"
|
|
fi
|
|
|
|
PREVIOUS_THEME_NAME=$(cat "$CURRENT_THEME_NAME_PATH" 2>/dev/null || true)
|
|
remember_current_theme_background
|
|
|
|
# Setup clean next theme directory (for atomic theme config swapping)
|
|
rm -rf "$NEXT_THEME_PATH"
|
|
mkdir -p "$NEXT_THEME_PATH"
|
|
|
|
# Copy official theme first, then overlay the user's theme on top
|
|
cp -r "$OMARCHY_THEMES_PATH/$THEME_NAME/"* "$NEXT_THEME_PATH/" 2>/dev/null
|
|
|
|
if theme_came_from_a_repo "$USER_THEMES_PATH/$THEME_NAME"; then
|
|
stage_installed_theme "$USER_THEMES_PATH/$THEME_NAME"
|
|
report_ignored_theme_files
|
|
else
|
|
cp -r "$USER_THEMES_PATH/$THEME_NAME/"* "$NEXT_THEME_PATH/" 2>/dev/null
|
|
fi
|
|
|
|
# Generate colors.toml from alacritty.toml if theme is missing colors.toml
|
|
if [[ ! -f $NEXT_THEME_PATH/colors.toml && -f $NEXT_THEME_PATH/alacritty.toml ]]; then
|
|
omarchy-theme-colors-from-alacritty "$NEXT_THEME_PATH"
|
|
fi
|
|
|
|
CHOSEN_THEME_BACKGROUND=""
|
|
STAGED_THEME_BACKGROUND=""
|
|
OLD_BACKGROUND_SNAPSHOT=""
|
|
PREPARED_BACKGROUND=""
|
|
PREPARED_BACKGROUND_SNAPSHOT=""
|
|
BACKGROUND_TRANSITION_SNAPSHOTS=true
|
|
THEME_BACKGROUND_INTRO=""
|
|
INTRO_PREPARE_PID=""
|
|
THEME_INTRO_TOKEN=""
|
|
if [[ $THEME_HEADLESS != "1" && $OMARCHY_THEME_SKIP_BACKGROUND != "1" ]]; then
|
|
# Resolve the staged choice while the old theme still exists. Video changes
|
|
# switch directly to the durable path in QML, so neither side needs a copy.
|
|
if choose_staged_theme_background; then
|
|
if [[ $PREVIOUS_THEME_NAME != "$THEME_NAME" && ! -f $HOME/.local/state/omarchy/toggles/background-intros-off ]] &&
|
|
[[ $(hyprctl -j getoption animations:enabled 2>/dev/null | jq -r '.bool') == "true" ]]; then
|
|
THEME_BACKGROUND_INTRO=$(omarchy-theme-bg-boot-intro --resolve-only "$STAGED_THEME_BACKGROUND")
|
|
fi
|
|
if [[ -n $THEME_BACKGROUND_INTRO ]]; then
|
|
OLD_BACKGROUND_SNAPSHOT=$(snapshot_current_background)
|
|
# OWE retains the opened clip across the staged directory's rename.
|
|
owe intro-prepare "$THEME_BACKGROUND_INTRO" >/dev/null 2>&1 9>&- &
|
|
INTRO_PREPARE_PID=$!
|
|
shell_ipc shell prepareThemeIntro "$OLD_BACKGROUND_SNAPSHOT" "$(stat -Lc '%d:%i' "$NEXT_THEME_PATH")" "" "" || true
|
|
elif background_transition_uses_snapshots "$CHOSEN_THEME_BACKGROUND"; then
|
|
OLD_BACKGROUND_SNAPSHOT=$(snapshot_current_background)
|
|
|
|
# Hand the shell the next background now, so it decodes while the
|
|
# templates render and the theme swaps in. The staged file is the same
|
|
# inode the swap moves into place, so its snapshot is the final image.
|
|
PREPARED_BACKGROUND="$CHOSEN_THEME_BACKGROUND"
|
|
PREPARED_BACKGROUND_SNAPSHOT=$(snapshot_background_path "$STAGED_THEME_BACKGROUND" "next")
|
|
if [[ -n $PREPARED_BACKGROUND_SNAPSHOT ]]; then
|
|
shell_ipc background prepare "$PREPARED_BACKGROUND_SNAPSHOT" 9>&- &
|
|
fi
|
|
else
|
|
BACKGROUND_TRANSITION_SNAPSHOTS=false
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Generate dynamic configs
|
|
omarchy-theme-set-templates
|
|
|
|
# Replacing the theme's still can cancel an active OWE intro even before the
|
|
# background link changes. Present its cover before replacing either path.
|
|
if [[ -n $THEME_BACKGROUND_INTRO ]]; then
|
|
wait_for_theme_intro themeIntroCoverStatus "$(stat -Lc '%d:%i' "$NEXT_THEME_PATH")" "loading"
|
|
fi
|
|
|
|
# Swap next theme in as current
|
|
rm -rf "$CURRENT_THEME_PATH"
|
|
mv "$NEXT_THEME_PATH" "$CURRENT_THEME_PATH"
|
|
|
|
# Store theme name for reference
|
|
echo "$THEME_NAME" >"$CURRENT_THEME_NAME_PATH"
|
|
|
|
# Make the running shell pick up the new palette immediately while the rest of
|
|
# the theme hooks run.
|
|
colors_payload=$([[ -f $CURRENT_THEME_PATH/colors.toml ]] && base64 -w 0 "$CURRENT_THEME_PATH/colors.toml")
|
|
shell_payload=$([[ -f $CURRENT_THEME_PATH/shell.toml ]] && base64 -w 0 "$CURRENT_THEME_PATH/shell.toml")
|
|
if [[ $THEME_HEADLESS == "1" ]]; then
|
|
# No shell/session bus exists during ISO chroot finalization, but the first
|
|
# real login still needs a current background symlink for omarchy-shell to
|
|
# render.
|
|
[[ $OMARCHY_THEME_SKIP_BACKGROUND == "1" ]] || set_theme_background_link || true
|
|
elif [[ $OMARCHY_THEME_SKIP_BACKGROUND == "1" ]]; then
|
|
shell_ipc shell applyTheme "$colors_payload" "$shell_payload" || true
|
|
else
|
|
set_theme_background
|
|
fi
|
|
|
|
# The shared staging/current symlinks are updated and the shell has accepted
|
|
# the transition. Let another theme selection queue only behind that critical
|
|
# section, not behind slower app-retint hooks and selector cache warmups.
|
|
flock -u 9
|
|
|
|
post_theme_commands=(
|
|
omarchy-restart-terminal
|
|
omarchy-restart-btop
|
|
omarchy-restart-opencode
|
|
omarchy-restart-helix
|
|
omarchy-theme-set-foot
|
|
omarchy-theme-set-tmux
|
|
omarchy-theme-set-hunk
|
|
omarchy-theme-set-gnome
|
|
omarchy-theme-set-pi
|
|
omarchy-theme-set-claude
|
|
omarchy-theme-set-hermes
|
|
omarchy-theme-set-t3code
|
|
omarchy-theme-set-browser
|
|
omarchy-theme-set-vscode
|
|
omarchy-theme-set-obsidian
|
|
omarchy-theme-set-keyboard
|
|
)
|
|
|
|
if [[ $THEME_HEADLESS != "1" ]]; then
|
|
# Let the wallpaper and bar finish their reveal before app retints compete
|
|
# with decoding and the shell's frame rendering. An intro reloads Hyprland
|
|
# after playback; ordinary theme changes reload it with the app retints.
|
|
if [[ -n $THEME_INTRO_TOKEN ]]; then
|
|
wait_for_theme_intro themeIntroStatus "$THEME_INTRO_TOKEN" "pending"
|
|
else
|
|
post_theme_commands+=(omarchy-restart-hyprctl)
|
|
fi
|
|
run_parallel "${post_theme_commands[@]}"
|
|
|
|
# Call hook on theme set
|
|
omarchy-hook theme-set "$THEME_NAME" >/dev/null
|
|
|
|
# Mirror the theme to herdr machines. SSH can take a while, so this never holds up the theme change.
|
|
setsid omarchy-theme-set-herdr-machines >>"$HOME/.local/state/omarchy/theme-set-herdr-machines.log" 2>&1 </dev/null &
|
|
|
|
# Warm the background selector's thumbnails. The theme picker keeps its own
|
|
# rows in the shell and refreshes them each time it opens.
|
|
omarchy-theme-bg-cache >/dev/null 2>&1 &
|
|
fi
|