/ de-omarchy / bin / omarchy-theme-set
swadhinbiswas Initial de-omarchy: Omarchy UI layer for Arch-based distros · 8/22/2026
9f393b2
219 lines · 7.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#!/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"
NEXT_THEME_PATH="$HOME/.local/state/omarchy/current/next-theme"
CURRENT_BACKGROUND_LINK="$HOME/.local/state/omarchy/current/background"
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"

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
}

snapshot_background_path() {
  local background="$1"
  local name="$2"
  local snapshot extension

  [[ -f $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

  current_background=$(readlink -f "$CURRENT_BACKGROUND_LINK" 2>/dev/null || true)
  snapshot_background_path "$current_background" "previous"
}

choose_theme_background() {
  local backgrounds=()
  local current_background index next_index i

  CHOSEN_THEME_BACKGROUND=""
  mapfile -d '' -t backgrounds < <(
    find -L "$HOME/.config/omarchy/backgrounds/$THEME_NAME/" "$CURRENT_THEME_PATH/backgrounds/" -maxdepth 1 -type f \
      \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.gif' -o -iname '*.bmp' -o -iname '*.webp' \) \
      -print0 2>/dev/null | sort -z
  )

  (( ${#backgrounds[@]} > 0 )) || return 1

  current_background=$(readlink "$CURRENT_BACKGROUND_LINK" 2>/dev/null || true)
  index=-1
  for i in "${!backgrounds[@]}"; do
    if [[ ${backgrounds[$i]} == $current_background ]]; then
      index=$i
      break
    fi
  done

  if (( index == -1 )); then
    CHOSEN_THEME_BACKGROUND="${backgrounds[0]}"
  else
    next_index=$(((index + 1) % ${#backgrounds[@]}))
    CHOSEN_THEME_BACKGROUND="${backgrounds[$next_index]}"
  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

  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

  new_background="$CHOSEN_THEME_BACKGROUND"
  new_background_snapshot=$(snapshot_background_path "$new_background" "next")

  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"
}

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 [[ ! -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

# 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 user customizations on top
cp -r "$OMARCHY_THEMES_PATH/$THEME_NAME/"* "$NEXT_THEME_PATH/" 2>/dev/null
cp -r "$USER_THEMES_PATH/$THEME_NAME/"* "$NEXT_THEME_PATH/" 2>/dev/null

# 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

# Generate dynamic configs
omarchy-theme-set-templates

OLD_BACKGROUND_SNAPSHOT=""
if [[ $THEME_HEADLESS != "1" && $OMARCHY_THEME_SKIP_BACKGROUND != "1" ]]; then
  OLD_BACKGROUND_SNAPSHOT=$(snapshot_current_background)
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" >"$HOME/.local/state/omarchy/current/theme.name"

# 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-hyprctl
  omarchy-restart-btop
  omarchy-restart-opencode
  omarchy-restart-helix
  omarchy-theme-set-foot
  omarchy-theme-set-tmux
  omarchy-theme-set-gnome
  omarchy-theme-set-pi
  omarchy-theme-set-claude
  omarchy-theme-set-browser
  omarchy-theme-set-vscode
  omarchy-theme-set-obsidian
  omarchy-theme-set-keyboard
)

if [[ $THEME_HEADLESS != "1" ]]; then
  run_parallel "${post_theme_commands[@]}"

  # Call hook on theme set
  omarchy-hook theme-set "$THEME_NAME" >/dev/null

  # Warm selector caches after the theme is applied. The shell hot-reloads theme
  # colors/backgrounds, so keep the running instance alive and preload the picker
  # rows/selection to avoid first-open carousel settling after a theme change.
  omarchy-theme-switcher --preload >/dev/null 2>&1
  omarchy-theme-bg-cache >/dev/null 2>&1 &
fi