Pick window transparency from Appearance > Window Transparency, and a Glassy bar background (#14991)

* Pick window transparency from Appearance > Window Transparency

Transparent, the default, keeps today's windows. Opaque turns the windows
Omarchy makes transparent solid. Glassy is the frosted glass Starship
(#14726): its blur behind every see-through window and shell surface, and a
see-through, blurred background in the theme's terminal files, since foot
turns compositor blur down and a Hyprland opacity rule fades text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Leave window transparency to Appearance when animations are off

Turning animations off no longer makes windows opaque. VM installs start
with opaque windows instead, and a migration keeps them opaque for anyone
who already has animations off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add a Glassy bar background, under Menu Bar > Bar Background

Menu Bar > Bar Background picks Opaque Bar, Glassy Bar or Clear Bar, and
Indicators becomes Bar Indicators to match. Glassy keeps the theme
background faintly and Hyprland blurs what is behind it, with the same blur
as Glassy windows.

omarchy bar background opaque|glassy|none|cycle sets it and prints it when
given nothing. omarchy bar transparent true|false|toggle stays for
compatibility, as none and opaque. Double-clicking the bar cycles through
the three backgrounds the same way, from clear to glassy to opaque.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Stop when a saved corner choice can't be removed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Make btop follow glassy windows

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
David Heinemeier HanssonandClaude Opus 5.5 authored and GitHub committed 2026-10-11 21:21:00 +09:00
1 parent be7c7bf378
commit a98e55eb91
32 files changed
+814 -64

No files matched your search

+77 -7
View File
@@ -2,7 +2,7 @@
# omarchy:summary=Configure the bar and its widget layout
# omarchy:group=bar
# omarchy:args=use <id> | reset | defaults | position [top|bottom|left|right] | transparent <true|false|toggle> | put <id> [placement] | move <id> [placement] | set <id> <key> <value> [--json] [placement] | get <id> <key>
# omarchy:args=use <id> | reset | defaults | position [top|bottom|left|right] | background [opaque|glassy|none|cycle] | put <id> [placement] | move <id> [placement] | set <id> <key> <value> [--json] [placement] | get <id> <key>
# omarchy:examples=omarchy bar use local.neon-bar | omarchy bar put omarchy.keyboard-layout --after omarchy.clock | omarchy bar move omarchy.clock --section center --index 0 | omarchy bar set omarchy.clock format HH:mm
set -euo pipefail
@@ -18,7 +18,11 @@ Usage: omarchy bar <command> [args...]
defaults Restore the default bar and service widgets
position [top|bottom|left|right] Bar position; prints the current one
when given none
transparent <true|false|toggle> Bar transparency
background [opaque|glassy|none|cycle]
Bar background: glassy shows what is
behind the bar blurred, cycle steps
none, glassy, opaque; prints the
current one when given none
put <id> [placement] Put a widget on the bar, leaving one
that is already there where it is
move <id> [placement] Move a widget within or between sections
@@ -194,6 +198,7 @@ cmd_defaults() {
" \
--slurpfile defaults "$DEFAULTS_FILE" \
--argjson widgets "$optional_widgets"
sync_glass || fail "could not change the blur behind the bar"
echo "Restored the default Omarchy bar"
}
@@ -210,18 +215,75 @@ cmd_position() {
echo "Bar position set to $position"
}
# The bar background is opaque, glassy, or none. A shell.json from before it
# had one says transparent instead, which is none, and setting a background
# drops it.
current_background() {
jq -r '.bar.background // (if .bar.transparent == true then "none" else "opaque" end)' "$(source_file)"
}
cmd_background() {
if (( $# == 0 )); then
current_background
return
fi
local background="$1"
(( $# == 1 )) || fail "background takes a single value"
[[ $background =~ ^(opaque|glassy|none|cycle)$ ]] || fail "background must be opaque, glassy, none, or cycle"
if [[ $background == "cycle" ]]; then
# Each step changes the bar by one degree: clear, then glassy, then opaque.
case $(current_background) in
none) background="glassy" ;;
glassy) background="opaque" ;;
*) background="none" ;;
esac
fi
local previous
previous=$(current_background)
commit "$NORMALIZE | .bar.background = \$background | del(.bar.transparent)" --arg background "$background"
if ! sync_glass; then
commit "$NORMALIZE | .bar.background = \$background | del(.bar.transparent)" --arg background "$previous"
fail "could not change the blur behind the bar"
fi
echo "Bar background set to $background"
}
# The older on/off form of the background, kept for scripts and dotfiles:
# transparent is none, and not transparent is opaque.
cmd_transparent() {
local transparent="${1:-}"
[[ -n $transparent ]] || fail "transparent is required"
(( $# == 1 )) || fail "transparent takes a single value"
[[ $transparent =~ ^(true|false|toggle)$ ]] || fail "transparent must be true, false, or toggle"
if [[ $transparent == "toggle" ]]; then
commit "$NORMALIZE | .bar.transparent = (.bar.transparent != true)"
echo "Bar transparency toggled"
else
commit "$NORMALIZE | .bar.transparent = \$transparent" --argjson transparent "$transparent"
echo "Bar transparency set to $transparent"
[[ $(current_background) == "none" ]] && transparent="false" || transparent="true"
fi
if [[ $transparent == "true" ]]; then
cmd_background none
else
cmd_background opaque
fi
}
# Hyprland blurs behind the bar only while the bar-glassy toggle is on, so it
# follows the bar background, reloading Hyprland only when it changes. The
# toggle sits where Hyprland reads it, under XDG_STATE_HOME.
sync_glass() {
local dir="${XDG_STATE_HOME:-$HOME/.local/state}/omarchy/toggles/hypr"
local flag="$dir/bar-glassy.lua"
if [[ $(current_background) == "glassy" ]]; then
[[ ! -f $flag ]] || return 0
mkdir -p "$dir" && cp "$OMARCHY_PATH/default/hypr/toggles/bar-glassy.lua" "$flag" || return 1
else
[[ -f $flag ]] || return 0
rm -f "$flag" || return 1
fi
hyprctl reload >/dev/null 2>&1 || true
}
bar_widget_default_section() {
@@ -406,6 +468,14 @@ case "$command" in
transparent)
cmd_transparent "$@"
;;
background)
cmd_background "$@"
;;
# Internal: the shell runs this when it loads a bar background, so one set
# by editing shell.json gets its blur too.
sync-glass)
sync_glass || fail "could not change the blur behind the bar"
;;
put)
cmd_put "$@"
;;
+1 -1
View File
@@ -34,7 +34,7 @@ if [[ $1 != "theme" ]]; then
mkdir -p "$TOGGLES_DIR" && cp "$OMARCHY_PATH/default/hypr/toggles/corners-$1.lua" "$TOGGLES_DIR/corners-$1.lua" || exit 1
fi
for mode in sharp rounded; do
[[ $mode == "$1" ]] || rm -f "$TOGGLES_DIR/corners-$mode.lua"
[[ $mode == "$1" ]] || rm -f "$TOGGLES_DIR/corners-$mode.lua" || exit 1
done
hyprctl reload >/dev/null || exit 1
+70
View File
@@ -0,0 +1,70 @@
#!/bin/bash
# omarchy:summary=Show or set how see-through windows are: opaque, transparent, or glassy
# omarchy:args=[opaque|transparent|glassy]
# omarchy:examples=omarchy hyprland window transparency | omarchy hyprland window transparency glassy
TOGGLES_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/omarchy/toggles/hypr"
# Opaque and glassy are toggle files loaded after every theme: opaque makes the
# windows Omarchy makes transparent solid, and glassy blurs what is behind any
# see-through window and gives terminals a see-through background. Transparent,
# the default, removes both.
current_mode() {
if [[ -f $TOGGLES_DIR/window-opaque.lua ]]; then
echo "opaque"
elif [[ -f $TOGGLES_DIR/window-glassy.lua ]]; then
echo "glassy"
else
echo "transparent"
fi
}
# Save the new choice before dropping the old one, so a failed save keeps it.
save_mode() {
if [[ $1 != "transparent" ]]; then
mkdir -p "$TOGGLES_DIR" && cp "$OMARCHY_PATH/default/hypr/toggles/window-$1.lua" "$TOGGLES_DIR/window-$1.lua" || return 1
fi
local mode
for mode in opaque glassy; do
[[ $mode == "$1" ]] || rm -f "$TOGGLES_DIR/window-$mode.lua" || return 1
done
}
if (( $# == 0 )); then
current_mode
exit 0
fi
case $1 in
opaque) LABEL="Opaque windows" ;;
transparent) LABEL="Transparent windows" ;;
glassy) LABEL="Glassy windows" ;;
*)
echo "Usage: omarchy-hyprland-window-transparency [opaque|transparent|glassy]" >&2
exit 1
;;
esac
# A theme switch renders the next theme's terminal files from the saved choice
# and then replaces the current theme, so the choice and the glass written into
# the current theme are changed under its lock, or the switch could drop them.
exec 9>"${XDG_RUNTIME_DIR:-/tmp}/omarchy-theme-set.lock"
flock 9
# A choice whose glass cannot be written is taken back, so the saved choice
# is always the one on screen.
previous=$(current_mode)
save_mode "$1" || exit 1
if ! omarchy-theme-set-glass; then
save_mode "$previous"
omarchy-theme-set-glass
exit 1
fi
flock -u 9
hyprctl reload >/dev/null || exit 1
omarchy-restart-terminal
omarchy-theme-set-foot
omarchy-restart-btop
omarchy-notification-send -g 󰂵 "$LABEL"
+12 -1
View File
@@ -1,9 +1,10 @@
#!/bin/bash
# omarchy:summary=Apply current Omarchy theme colors to running Foot terminals
# omarchy:summary=Apply current Omarchy theme colors and background alpha to running Foot terminals
# omarchy:hidden=true
colors_toml=$HOME/.local/state/omarchy/current/theme/colors.toml
foot_ini=$HOME/.local/state/omarchy/current/theme/foot.ini
if [[ ! -f $colors_toml ]] || ! pgrep -x foot >/dev/null; then
exit 0
@@ -12,6 +13,16 @@ fi
foot_osc=$(omarchy-theme-osc "$colors_toml")
[[ -n $foot_osc ]] || exit 0
# foot takes a background alpha as an urxvt-style [percent] prefix, so a theme
# going see-through or back, as with Glassy windows, reaches running terminals.
# The last alpha in the theme's foot.ini wins, as it does when foot reads it.
alpha=$(sed -n 's/^[[:space:]]*alpha[[:space:]]*=[[:space:]]*\([0-9.]*\).*/\1/p' "$foot_ini" 2>/dev/null | tail -n 1)
background=$(omarchy-theme-color --file "$colors_toml" background 2>/dev/null)
if [[ -n $background ]]; then
percent=$(awk -v alpha="${alpha:-1}" 'BEGIN { printf "%d", alpha * 100 + 0.5 }')
foot_osc+=$(printf '\033]11;[%d]%s\007' "$percent" "$background")
fi
for foot_pid in $(pgrep -x foot); do
for child_pid in $(pgrep -P "$foot_pid"); do
tty=$(readlink "/proc/$child_pid/fd/1" 2>/dev/null)
+122
View File
@@ -0,0 +1,122 @@
#!/bin/bash
# omarchy:summary=Give terminals, btop, and GTK apps a see-through background while windows are glassy
# omarchy:args=[theme-dir]
# omarchy:hidden=true
# A Hyprland opacity rule fades a terminal's text along with its background,
# and foot turns compositor blur down unless its own config asks for it, so the
# glass belongs in each terminal's theme file, as in the glassy Starship
# (#14726). The lines sit between markers so leaving glassy takes them out
# again, and a theme that sets its own opacity keeps it.
THEME_DIR="${1:-$HOME/.local/state/omarchy/current/theme}"
TOGGLES_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/omarchy/toggles/hypr"
GLASSY=false
[[ -f $TOGGLES_DIR/window-glassy.lua ]] && GLASSY=true
status=0
# A literal for a sed address, whose delimiter is /.
regex() {
sed 's/[][\\.*^$/]/\\&/g' <<<"$1"
}
# A block added to a file that did not end in a newline has to supply one, so
# its begin marker says so, and taking the block out takes that newline back.
# Leaving glassy then gives the file back exactly as it was. Markers come as
# an opening and a closing part, so CSS can wrap them in a comment.
remove_block() {
local file="$1" open="$2" close="$3"
local begin end
begin=$(regex "$open omarchy-glass begin")
end=$(regex "$open omarchy-glass end$close")
[[ -f $file ]] && grep -qF "$open omarchy-glass begin" "$file" || return 0
local added_newline=false
grep -qxF "$open omarchy-glass begin after a line without a newline$close" "$file" && added_newline=true
sed -i "/^$begin/,/^$end\$/d" "$file" || return 1
if [[ $added_newline == "true" && -s $file && -z $(tail -c 1 "$file") ]]; then
truncate -s -1 "$file" || return 1
fi
}
append_block() {
local file="$1" open="$2" close="$3" lines="$4"
if [[ -s $file && -n $(tail -c 1 "$file") ]]; then
printf '\n%s\n%s\n%s\n' "$open omarchy-glass begin after a line without a newline$close" "$lines" "$open omarchy-glass end$close" >>"$file"
else
printf '%s\n%s\n%s\n' "$open omarchy-glass begin$close" "$lines" "$open omarchy-glass end$close" >>"$file"
fi
}
# Each setting comes with a pattern for the theme setting it already, which
# keeps the theme's own; an empty pattern always adds it. Lines that belong in
# a section go in under its header. A file that already has the section gets
# them inside it, since TOML refuses a table declared twice; otherwise the
# header comes along with them.
glass() {
local file="$THEME_DIR/$1" section="$2"
shift 2
[[ -f $file ]] || return 0
remove_block "$file" "#" "" || return 1
[[ $GLASSY == "true" ]] || return 0
local lines=""
while (( $# >= 2 )); do
if [[ -z $1 ]] || ! grep -qE "$1" "$file"; then
lines+="${lines:+$'\n'}$2"
fi
shift 2
done
[[ -n $lines ]] || return 0
# A header may carry trailing space or a comment.
local header="^$(regex "$section")[[:space:]]*(#.*)?\$"
if [[ -n $section ]] && grep -qE "$header" "$file"; then
local block
block=$(mktemp) || return 1
printf '# omarchy-glass begin\n%s\n# omarchy-glass end\n' "$lines" >"$block"
sed -i -E "0,/$header/{/$header/r $block
}" "$file"
local result=$?
rm -f "$block"
return $result
fi
append_block "$file" "#" "" "${section:+$section$'\n'}$lines"
}
# Alacritty's own blur works only on macOS and KDE; Hyprland blurs it anyway.
glass alacritty.toml '[window]' \
'^[[:space:]]*opacity[[:space:]]*=' 'opacity = 0.65' || status=1
glass foot.ini '[colors-dark]' \
'^[[:space:]]*alpha[[:space:]]*=' 'alpha=0.65' \
'^[[:space:]]*blur[[:space:]]*=' 'blur=yes' || status=1
glass kitty.conf '' \
'^[[:space:]]*background_opacity[[:space:]]' 'background_opacity 0.65' || status=1
# btop paints its own background over the terminal's glass. An empty main_bg
# uses the terminal background; the last theme value wins, so removing the
# block restores the theme's colour without changing btop's user config.
glass btop.theme '' \
'^[[:space:]]*theme\[main_bg\][[:space:]]*=[[:space:]]*""' 'theme[main_bg]=""' || status=1
# GTK only asks Hyprland for blur under a CSS backdrop-filter, which Ghostty
# takes as custom CSS and reloads live; its background-blur speaks only KDE's
# blur protocol.
glass ghostty.conf '' \
'^[[:space:]]*background-opacity[[:space:]]*=' 'background-opacity = 0.65' \
'' "gtk-custom-css = $OMARCHY_PATH/default/gtk/glass.css" || status=1
# Other GTK apps, such as Nautilus, read the user's gtk.css at launch, so the
# same backdrop blur is imported there. It follows the mode alone, so a theme
# switch, which names the theme it is rendering, leaves it be.
if (( $# == 0 )); then
gtk_css="${XDG_CONFIG_HOME:-$HOME/.config}/gtk-4.0/gtk.css"
remove_block "$gtk_css" "/*" " */" || status=1
if [[ $GLASSY == "true" ]]; then
{ mkdir -p "${gtk_css%/*}" &&
append_block "$gtk_css" "/*" " */" "@import url(\"file://$OMARCHY_PATH/default/gtk/glass.css\");"; } || status=1
fi
fi
exit $status
+1
View File
@@ -451,3 +451,4 @@ if [[ -f $COLORS_FILE ]]; then
fi
apply_shell_section_overrides
omarchy-theme-set-glass "$NEXT_THEME_DIR"
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# omarchy:summary=Toggle animations, transparency and other effects that are slow without a GPU
# omarchy:summary=Toggle animations, blur and shadows, which are slow without a GPU
# omarchy:args=[toggle|on|off]
# omarchy:examples=omarchy toggle animations | omarchy toggle animations off | omarchy toggle animations on
+1 -1
View File
@@ -6,7 +6,7 @@
},
"bar": {
"position": "top",
"transparent": false,
"background": "opaque",
"centerAnchor": "omarchy.clock",
"layout": {
"left": [
+7
View File
@@ -0,0 +1,7 @@
/* GTK asks the compositor to blur behind a see-through window only where a
backdrop-filter blur covers it, so Glassy windows load this into GTK apps,
such as Ghostty and Nautilus. The compositor's own blur settings decide
how it looks. */
window {
backdrop-filter: blur(20px);
}
+22
View File
@@ -0,0 +1,22 @@
-- Frosted glass from the glassy Starship (#14726), shared by Glassy windows and
-- the Glassy bar: whatever is behind a see-through surface is blurred.
return function()
hl.config({
decoration = {
blur = {
enabled = true,
size = 4,
passes = 2,
new_optimizations = true,
xray = false,
contrast = 1.0,
brightness = 0.75,
noise = 0.01,
vibrancy = 0.35,
vibrancy_darkness = 0.35,
popups = true,
special = true,
},
},
})
end
+3 -2
View File
@@ -281,7 +281,8 @@ function o.window(match, rules)
hl.window_rule(rules)
end
-- Opt a window in to Omarchy's standard active/inactive transparency.
-- Opt a window in to Omarchy's standard active/inactive transparency. The tag
-- lets Appearance > Window Transparency turn these windows opaque or glassy.
function o.transparent_window(match, opacity)
o.window(match, { opacity = opacity or "0.985 0.96" })
o.window(match, { opacity = opacity or "0.985 0.96", tag = "+transparent-window" })
end
+9
View File
@@ -0,0 +1,9 @@
-- The Glassy bar: the shell draws the bar see-through, and Hyprland blurs what
-- is behind it.
require("default.hypr.glass")()
hl.layer_rule({
match = { namespace = "^omarchy-bar$" },
blur = true,
ignore_alpha = 0.3,
})
+2 -4
View File
@@ -1,6 +1,6 @@
-- Draw nothing just for show, for machines that render on the CPU, like a VM:
-- no animations, blur or shadows, and opaque windows, so nothing behind a
-- window has to be blended into it.
-- no animations, blur or shadows. How see-through windows are is left to
-- Appearance > Window Transparency.
hl.config({
animations = {
enabled = false,
@@ -16,5 +16,3 @@ hl.config({
},
},
})
o.window(".*", { opacity = "1 1" })
+14
View File
@@ -0,0 +1,14 @@
-- Frosted glass from the glassy Starship (#14726): whatever is behind a
-- see-through window or shell surface is blurred. Terminals get the see-through
-- background itself from omarchy-theme-set-glass, which keeps their text crisp.
require("default.hypr.glass")()
-- GTK apps fade as a whole, since GTK draws their backgrounds solid, and get
-- their blur from the backdrop-filter Glassy adds to the user's gtk.css.
o.window("org\\.gnome\\.Nautilus", { opacity = "0.75 0.75" })
hl.layer_rule({
match = { namespace = "^(omarchy-bar|omarchy-menu|omarchy-notifications|omarchy-osd|omarchy-polkit|omarchy-reminders|omarchy-clipboard|omarchy-emojis|omarchy-image-selector|omarchy-keyboard-panel|omarchy-network-qr)$" },
blur = true,
ignore_alpha = 0.3,
})
+2
View File
@@ -0,0 +1,2 @@
-- Opaque windows, even those Omarchy makes transparent by default.
o.window({ tag = "transparent-window" }, { opacity = "1 1" })
+9 -3
View File
@@ -59,9 +59,11 @@
"trigger.appearance.top-bar.hide": {"icon":"󰈉","label":"Hide Bar","when":"! omarchy-toggle-enabled bar-off","action":"omarchy-toggle-bar"},
"trigger.appearance.top-bar.show": {"icon":"󰈈","label":"Show Bar","when":"omarchy-toggle-enabled bar-off","action":"omarchy-toggle-bar"},
"trigger.appearance.top-bar.position": {"icon":"","label":"Position Bar"},
"trigger.appearance.top-bar.transparent": {"icon":"󰂵","label":"Transparent Bar","when":"! jq -e .bar.transparent ~/.config/omarchy/shell.json >/dev/null 2>&1","action":"omarchy-bar transparent true"},
"trigger.appearance.top-bar.opaque": {"icon":"󰂵","label":"Opaque Bar","when":"jq -e .bar.transparent ~/.config/omarchy/shell.json >/dev/null 2>&1","action":"omarchy-bar transparent false"},
"trigger.appearance.top-bar.indicators": {"icon":"󰋽","label":"Indicators"},
"trigger.appearance.top-bar.background": {"icon":"󰂵","label":"Bar Background"},
"trigger.appearance.top-bar.background.opaque": {"icon":"󰝤","label":"Opaque Bar","checked":"[[ $(omarchy-bar background) == \"opaque\" ]]","action":"omarchy-bar background opaque"},
"trigger.appearance.top-bar.background.glassy": {"icon":"󰂵","label":"Glassy Bar","checked":"[[ $(omarchy-bar background) == \"glassy\" ]]","action":"omarchy-bar background glassy"},
"trigger.appearance.top-bar.background.clear": {"icon":"󰝣","label":"Clear Bar","checked":"[[ $(omarchy-bar background) == \"none\" ]]","action":"omarchy-bar background none"},
"trigger.appearance.top-bar.indicators": {"icon":"󰋽","label":"Bar Indicators"},
"trigger.appearance.top-bar.indicators.battery-percentage": {"icon":"󰁹","label":"Battery Percentage","when":"omarchy-hw-laptop","action":"omarchy-shell omarchy.power togglePercentage"},
"trigger.appearance.top-bar.indicators.clock": {"icon":"󰥔","label":"Clock"},
"trigger.appearance.top-bar.indicators.clock.sunday-24h": {"icon":"󰥔","label":"Sunday 14:30","when":"[[ $(omarchy-bar position) != \"left\" && $(omarchy-bar position) != \"right\" ]]","checked":"[[ $(omarchy-bar get omarchy.clock format) == \"dddd HH:mm\" ]]","action":"omarchy-bar set omarchy.clock format \"dddd HH:mm\""},
@@ -87,6 +89,10 @@
"trigger.appearance.window-corners.theme": {"icon":"󰏘","label":"Theme Corners","checked":"[[ $(omarchy-hyprland-corners) == \"theme\" ]]","action":"omarchy-hyprland-corners theme"},
"trigger.appearance.window-corners.sharp": {"icon":"󰝣","label":"Sharp Corners","checked":"[[ $(omarchy-hyprland-corners) == \"sharp\" ]]","action":"omarchy-hyprland-corners sharp"},
"trigger.appearance.window-corners.rounded": {"icon":"󰘇","label":"Rounded Corners","checked":"[[ $(omarchy-hyprland-corners) == \"rounded\" ]]","action":"omarchy-hyprland-corners rounded"},
"trigger.appearance.window-transparency": {"icon":"󰂵","label":"Window Transparency"},
"trigger.appearance.window-transparency.opaque": {"icon":"󰝤","label":"Opaque Windows","checked":"[[ $(omarchy-hyprland-window-transparency) == \"opaque\" ]]","action":"omarchy-hyprland-window-transparency opaque"},
"trigger.appearance.window-transparency.transparent": {"icon":"󰝣","label":"Transparent Windows","checked":"[[ $(omarchy-hyprland-window-transparency) == \"transparent\" ]]","action":"omarchy-hyprland-window-transparency transparent"},
"trigger.appearance.window-transparency.glassy": {"icon":"󰂵","label":"Glassy Windows","checked":"[[ $(omarchy-hyprland-window-transparency) == \"glassy\" ]]","action":"omarchy-hyprland-window-transparency glassy"},
"trigger.appearance.one-window-ratio": {"icon":"","label":"1-Window Ratio","action":"omarchy-hyprland-window-single-square-aspect-toggle"},
"trigger.appearance.workspace-layout": {"icon":"󱂬","label":"Workspace Layout","title":"Default Workspace Layout"},
"trigger.appearance.workspace-layout.dwindle": {"icon":"󱓻","label":"Tiling","checked":"[[ $(omarchy-hyprland-workspace-layout-default) == \"dwindle\" ]]","action":"omarchy-hyprland-workspace-layout-default dwindle"},
+3
View File
@@ -1,6 +1,9 @@
[colors-dark]
foreground={{ foreground_strip }}
background={{ background_strip }}
# Blur only applies while the background is see-through, as with Glassy windows,
# and foot reads it only at launch, so it is on from the start.
blur=yes
selection-foreground={{ selection_foreground_strip }}
selection-background={{ selection_background_strip }}
+4
View File
@@ -6,6 +6,10 @@ selection_background {{ selection_background }}
cursor {{ bright_foreground }}
cursor_text_color {{ background }}
# Lets a reload change the background opacity, as Glassy windows do, since
# kitty only allows that when this is on from launch.
dynamic_background_opacity yes
active_border_color {{ accent }}
active_tab_background {{ accent }}
+4 -4
View File
@@ -94,8 +94,8 @@ You can still install by hand: drop a plugin into
`~/.config/omarchy/plugins/<id>/`, run `omarchy-shell shell rescanPlugins`, then
`omarchy plugin enable <id>`. A bar widget starts in its declared default
section; enabling a full bar replaces the one in use. `omarchy bar` drives the
bar from the CLI — `use | reset | defaults | position | transparent | put |
move | set`, with placement flags such as `--section` and `--index`.
bar from the CLI — `use | reset | defaults | position | background | put |
move | set | get`, with placement flags such as `--section` and `--index`.
The lower-level IPC methods remain available through `omarchy-shell shell ...`.
## Elsewhen
@@ -121,7 +121,7 @@ or `omarchy.power`. There is no `bar` target.
| `rescanPlugins` | re-walk plugin dirs and hot-reload plugin code |
| `reloadConfig` | reload shell.json |
| `applyTheme <colorsB64> <shellB64>` | push theme colors + shell.toml |
| `toggleBarTransparency` | flip the bar background between solid and transparent |
| `toggleBarTransparency` | cycle the bar background from clear to glassy to opaque |
| `cycleInput <next\|back>` | switch to the next or previous input method or layout (Super+I, Super+Shift+I); `not running` without the input reader |
| `setPluginEnabled <id> <"true"\|…>` | flip enabled bit (`ok` / `unknown`) |
| `enablePlugin <id> <placementJson>` | enable and place in one mutation |
@@ -148,7 +148,7 @@ string on a miss.
"bar": {
"id": "omarchy.bar",
"position": "top",
"transparent": false,
"background": "opaque",
"centerAnchor": "omarchy.clock",
"layout": {
"left": [ { "id": "omarchy.menu" } ],
+5 -3
View File
@@ -1,9 +1,11 @@
# A VM usually renders on the CPU, where animations and transparency cost
# every frame, so start it without them. omarchy-toggle-animations brings them
# back. This runs before any Hyprland session exists, so place the flag
# directly rather than through omarchy-hyprland-toggle, which reloads Hyprland.
# every frame, so start it without them and with opaque windows.
# omarchy-toggle-animations and Appearance > Window Transparency bring them
# back. This runs before any Hyprland session exists, so place the flags
# directly rather than through the commands, which reload Hyprland.
if omarchy-hw-vm; then
echo "Detected a virtual machine. Turning off animations and transparency."
mkdir -p "$HOME/.local/state/omarchy/toggles/hypr"
cp "$OMARCHY_PATH/default/hypr/toggles/no-animations.lua" "$HOME/.local/state/omarchy/toggles/hypr/"
cp "$OMARCHY_PATH/default/hypr/toggles/window-opaque.lua" "$HOME/.local/state/omarchy/toggles/hypr/"
fi
+5 -5
View File
@@ -81,15 +81,15 @@ If you'd rather they were always visible, set `alwaysShow` to `true` on the widg
The bar configures itself. You don't have to open a config file to move things.
Grab an empty patch of the bar around the center and drag it toward another screen edge, and the bar moves there — left, right, top, or bottom all work, and every widget adapts (vertical bars fall back to compact icon-only forms). A click-and-hold starts the same drag. Double-left-click that same empty space to toggle transparency. And drag any widget to reorder it or throw it into another section.
Grab an empty patch of the bar around the center and drag it toward another screen edge, and the bar moves there — left, right, top, or bottom all work, and every widget adapts (vertical bars fall back to compact icon-only forms). A click-and-hold starts the same drag. Double-left-click that same empty space to cycle the bar background from clear to glassy to opaque. And drag any widget to reorder it or throw it into another section.
If you'd rather pick from a menu, **Style → Menu Bar** has both position and transparency.
If you'd rather pick from a menu, **Trigger → Appearance → Menu Bar** has the position, and **Bar Background** picks how the bar is drawn: **Opaque Bar**, **Glassy Bar** (see-through, with what's behind it blurred), or **Clear Bar**.
The same things have commands, which is what you want for a [dotfiles](31-dotfiles.md) setup:
```bash
omarchy bar position bottom
omarchy bar transparent toggle
omarchy bar background glassy # opaque, glassy, none, or cycle
omarchy bar move omarchy.clock --section center --index 0
omarchy bar set omarchy.clock format "HH:mm"
omarchy bar defaults # back to the shipped layout
@@ -104,7 +104,7 @@ omarchy plugin disable omarchy.weather
## Hiding the bar
`Super + Shift + Space` toggles the bar off and back on without killing the shell — panels and hotkeys keep working, you just get the pixels back. It's also in the menu under **Trigger → Toggle → Menu Bar**.
`Super + Shift + Space` toggles the bar off and back on without killing the shell — panels and hotkeys keep working, you just get the pixels back. It's also in the menu under **Trigger → Appearance → Menu Bar → Hide Bar**.
## The config file
@@ -115,7 +115,7 @@ All of it is stored in `~/.config/omarchy/shell.json`, under the `bar` key. Here
"version": 1,
"bar": {
"position": "top",
"transparent": false,
"background": "opaque",
"centerAnchor": "omarchy.clock",
"layout": {
"left": [{ "id": "omarchy.menu" }, { "id": "omarchy.workspaces" }],
+20
View File
@@ -0,0 +1,20 @@
echo "Keep windows opaque where animations are turned off"
# Turning animations off used to make windows opaque too. It no longer touches
# transparency, so drop that rule from the saved flag, where it would keep
# windows opaque whatever Appearance > Window Transparency says, and carry it
# over as Window Transparency > Opaque for anyone who has not picked one yet.
# omarchy-hyprland-toggle saved flags under ~/.local/state whatever
# XDG_STATE_HOME said, while Hyprland reads them from XDG_STATE_HOME, so both
# are fixed.
for state_home in "$HOME/.local/state" "${XDG_STATE_HOME:-}"; do
[[ -n $state_home ]] || continue
toggles_dir="$state_home/omarchy/toggles/hypr"
[[ -f $toggles_dir/no-animations.lua ]] || continue
sed -i '/^o\.window("\.\*", { opacity = "1 1" })$/d' "$toggles_dir/no-animations.lua"
if [[ ! -f $toggles_dir/window-opaque.lua && ! -f $toggles_dir/window-glassy.lua ]]; then
cp "$OMARCHY_PATH/default/hypr/toggles/window-opaque.lua" "$toggles_dir/"
fi
done
+26 -14
View File
@@ -45,7 +45,7 @@ Item {
property string omarchyConfigDir: home + "/.config/omarchy"
property var fallbackBarConfig: ({
position: "top",
transparent: false,
background: "opaque",
centerAnchor: "omarchy.clock",
layout: { left: [], center: [], right: [] }
})
@@ -54,6 +54,12 @@ Item {
property bool requestedTransparent: false
property bool useTransparentForeground: false
property bool transparent: false
// A see-through bar that Hyprland blurs behind (the bar-glassy toggle), so
// its theme background stays faintly. A transparent bar wins over it.
property bool glassy: false
// The look the blur behind the bar was last synced for. A background set by
// editing shell.json skips the CLI, so the bar syncs Hyprland's blur itself.
property string glassSyncedLook: ""
property bool centerSectionHovered: false
// One bar surface exists per monitor and each reports into this count, so a
// pointer crossing from one monitor's bar to another's stays counted however
@@ -79,6 +85,7 @@ Item {
property color barForeground: useTransparentForeground ? transparentForeground : themeForeground
property bool foregroundAnimationEnabled: true
property color background: Commons.Color.bar.background
readonly property color glassBackground: Qt.rgba(background.r, background.g, background.b, Math.min(background.a, 0.35))
property color urgent: Commons.Color.bar.active
Behavior on barForeground { enabled: root.foregroundAnimationEnabled; ColorAnimation { duration: Style.duration(420); easing.type: Easing.OutCubic } }
@@ -651,7 +658,18 @@ Item {
var config = Util.isPlainObject(barConfig) ? barConfig : fallbackBarConfig
position = normalizePosition(config.position)
setRequestedTransparency(config.transparent === true)
// opaque, glassy or none; a config from before background says
// transparent instead.
var look = config.background || (config.transparent === true ? "none" : "opaque")
setRequestedTransparency(look === "none")
// A bar going clear waits for its text color before dropping its
// background, so a glassy one stays glassy until then rather than
// flashing opaque. Once clear, transparent wins over a stale glassy.
if (look !== "none") glassy = look === "glassy"
if (look !== glassSyncedLook) {
glassSyncedLook = look
Util.execDetached("omarchy-bar sync-glass")
}
centerAnchor = Util.canonicalWidgetId(config.centerAnchor || "")
// layoutEntries feeds plain JS arrays to the module Repeaters, and QML
@@ -913,16 +931,10 @@ Item {
Util.execDetached(command)
}
function toggleTransparency() {
var nextTransparent = !(root.requestedTransparent === true)
if (root.shell && typeof root.shell.mutateShellConfig === "function") {
root.shell.mutateShellConfig(function(config) {
if (!Util.isPlainObject(config.bar)) config.bar = {}
config.bar.transparent = nextTransparent
})
} else {
root.setRequestedTransparency(nextTransparent)
}
// Steps the bar background from clear to glassy to opaque and around. Through
// omarchy-bar, which keeps Hyprland's blur behind a glassy bar in step.
function cycleBackground() {
Util.execDetached("omarchy-bar background cycle")
}
function rawLayoutSection(config, region) {
@@ -1357,7 +1369,7 @@ Item {
implicitWidth: root.vertical ? thickness : 0
implicitHeight: root.vertical ? 0 : thickness
color: root.transparent ? "transparent" : root.background
color: root.transparent ? "transparent" : (root.glassy ? root.glassBackground : root.background)
surfaceFormat.opaque: false
WlrLayershell.namespace: "omarchy-bar"
WlrLayershell.layer: WlrLayer.Top
@@ -1832,7 +1844,7 @@ Item {
return
}
if (mouse.button === Qt.LeftButton) {
root.toggleTransparency()
root.cycleBackground()
mouse.accepted = true
}
}
+2 -2
View File
@@ -16,7 +16,7 @@ the shell for its whole session.
The bar config lives under the `bar:` key of [`~/.config/omarchy/shell.json`](../../../docs/omarchy-shell.md#shelljson). Out of the box the shell uses [`config/omarchy/shell.json`](../../../config/omarchy/shell.json). Once you customize anything via the bar gestures, `omarchy bar ...`, or by editing shell.json directly, your file is canonical — there is no deep-merge.
The bar is configured directly on the bar itself: drag empty bar space (or click-and-hold) to move the bar to another screen edge, double-left-click empty center-bar space to toggle transparency, and drag widgets to reorder them. The `omarchy bar position`, `omarchy bar transparent`, `omarchy bar move`, and `omarchy bar set` commands do the same from scripts. Enable or disable widgets with `omarchy plugin enable` and `omarchy plugin disable` (widget ids come from `omarchy plugin list`).
The bar is configured directly on the bar itself: drag empty bar space (or click-and-hold) to move the bar to another screen edge, double-left-click empty center-bar space to cycle the bar background from clear to glassy to opaque, and drag widgets to reorder them. The `omarchy bar position`, `omarchy bar background`, `omarchy bar move`, and `omarchy bar set` commands do the same from scripts. Enable or disable widgets with `omarchy plugin enable` and `omarchy plugin disable` (widget ids come from `omarchy plugin list`).
Example `shell.json` (bar subtree only shown):
@@ -25,7 +25,7 @@ Example `shell.json` (bar subtree only shown):
"version": 1,
"bar": {
"position": "top",
"transparent": false,
"background": "opaque",
"centerAnchor": "omarchy.clock",
"layout": {
"left": [
+2
View File
@@ -393,6 +393,7 @@ function displayRow(items, itemOrder, checkedResults, disabledResults, entry, de
// cached while one expression runs lives in that subshell only, so a lazy
// memo never survives to the expression after it.
var GUARD_READERS = [
"omarchy-bar background",
"omarchy-bar get omarchy.clock format",
"omarchy-bar get omarchy.clock verticalFormat",
"omarchy-bar position",
@@ -404,6 +405,7 @@ var GUARD_READERS = [
"omarchy-default-dictation",
"omarchy-dns",
"omarchy-hyprland-corners",
"omarchy-hyprland-window-transparency",
"omarchy-hyprland-workspace-layout-default"
]
+5 -3
View File
@@ -47,7 +47,7 @@ ShellRoot {
},
bar: {
position: "top",
transparent: false,
background: "opaque",
centerAnchor: "omarchy.clock",
layout: {
left: [{ id: "omarchy.menu" }, { id: "omarchy.workspaces" }],
@@ -1794,9 +1794,11 @@ ShellRoot {
return "ok"
}
// Named for the two-state toggle it once was; it now cycles the bar
// background from clear to glassy to opaque and around.
function toggleBarTransparency(): string {
if (shell.bar && typeof shell.bar.toggleTransparency === "function") {
shell.bar.toggleTransparency()
if (shell.bar && typeof shell.bar.cycleBackground === "function") {
shell.bar.cycleBackground()
return "ok"
}
return "no-bar"
+1 -1
View File
@@ -59,7 +59,7 @@ const bar = requireFromRoot('shell/plugins/bar/BarModel.js')
const barSource = fs.readFileSync(root + '/shell/plugins/bar/Bar.qml', 'utf8')
const shellSource = fs.readFileSync(root + '/shell/shell.qml', 'utf8')
assert(/function toggleBarTransparency\(\): string \{[\s\S]*?shell\.bar\.toggleTransparency\(\)/.test(shellSource), 'shell exposes the bar transparency toggle over IPC')
assert(/function toggleBarTransparency\(\): string \{[\s\S]*?shell\.bar\.cycleBackground\(\)/.test(shellSource), 'shell exposes the bar background cycle over IPC')
// put tolerates a placement target the bar does not carry, so the IPC call
// must reach the registry's put rather than route back through enable.
+86 -2
View File
@@ -255,6 +255,9 @@ pass "Hyprland bootstrap reloads cached Omarchy config modules"
TMPDIR=$(mktemp -d)
mkdir -p "$TMPDIR/home/.config/omarchy"
# The bar keeps its blur toggle where Hyprland reads it, under XDG_STATE_HOME,
# so that points into the test home too.
export XDG_STATE_HOME="$TMPDIR/home/.local/state"
ipc_mock_bin="$TMPDIR/ipc-mock"
mkdir -p "$ipc_mock_bin"
@@ -335,16 +338,97 @@ pass "shell config sets bar position"
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar transparent true
jq -e '
.bar.transparent == true and
.bar.background == "none" and
.bar.position == "bottom" and
.plugins == []
' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
pass "shell config sets bar transparency"
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar transparent toggle
jq -e '.bar.transparent == false' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
jq -e '.bar.background == "opaque"' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
pass "shell config toggles bar transparency"
# The bar background is opaque, glassy or none. A glassy bar needs Hyprland's
# blur, so the bar-glassy toggle follows it.
printf '#!/bin/bash\nprintf "%%s\\n" "$*" >>"%s/hyprctl.log"\n' "$TMPDIR" >"$ipc_mock_bin/hyprctl"
chmod +x "$ipc_mock_bin/hyprctl"
glass_toggle="$TMPDIR/home/.local/state/omarchy/toggles/hypr/bar-glassy.lua"
bar_background() { HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar background "$@"; }
bar_background opaque >/dev/null
[[ $(bar_background) == "opaque" ]] || fail "an opaque bar background is reported"
bar_background glassy >/dev/null
jq -e '.bar.background == "glassy"' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null ||
fail "shell config sets a glassy bar"
[[ $(bar_background) == "glassy" ]] || fail "a glassy bar background is reported"
[[ -f $glass_toggle ]] || fail "a glassy bar turns on Hyprland's blur behind it"
grep -qx 'reload' "$TMPDIR/hyprctl.log" || fail "a glassy bar reloads Hyprland"
bar_background none >/dev/null
jq -e '.bar.background == "none"' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null ||
fail "a bar without a background is stored as none"
[[ ! -f $glass_toggle ]] || fail "a bar without a background turns Hyprland's blur off again"
pass "shell config sets the bar background, and Hyprland's blur follows a glassy one"
seen=()
for _ in 1 2 3; do
bar_background cycle >/dev/null
seen+=("$(bar_background)")
done
[[ ${seen[*]} == "glassy opaque none" ]] || fail "the bar background cycles from clear to glassy to opaque and around" "${seen[*]}"
if bar_background frosted 2>/dev/null; then
fail "an unknown bar background is refused"
fi
: >"$TMPDIR/hyprctl.log"
bar_background opaque >/dev/null
[[ ! -s $TMPDIR/hyprctl.log ]] || fail "Hyprland reloads only when the bar's glass changes"
pass "the bar background cycles through its looks and refuses unknown ones"
# transparent stays for scripts written against it: true is none, false opaque.
bar_background glassy >/dev/null
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar transparent true >/dev/null
[[ $(bar_background) == "none" ]] || fail "transparent true leaves the bar without a background"
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar transparent toggle >/dev/null
[[ $(bar_background) == "opaque" ]] || fail "toggling transparency off makes the bar opaque"
# A shell.json from before background says transparent, which reads as none
# and is dropped once a background is set.
jq '.bar.transparent = true | del(.bar.background)' "$TMPDIR/home/.config/omarchy/shell.json" >"$TMPDIR/legacy.json" &&
mv "$TMPDIR/legacy.json" "$TMPDIR/home/.config/omarchy/shell.json"
[[ $(bar_background) == "none" ]] || fail "an older transparent bar reads as no background"
bar_background glassy >/dev/null
jq -e '.bar.background == "glassy" and (.bar | has("transparent") | not)' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null ||
fail "setting a background drops the older transparent flag"
pass "the older transparent form still sets and reads as the bar background"
# Restoring the default bar also restores its opaque background, and the blur
# behind a glassy one goes with it.
bar_background glassy >/dev/null
[[ -f $glass_toggle ]] || fail "a glassy bar has its blur on before defaults"
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar defaults >/dev/null
[[ $(bar_background) == "opaque" && ! -f $glass_toggle ]] || fail "omarchy bar defaults turns a glassy bar's blur off"
pass "restoring the default bar turns a glassy bar's blur off"
# A background set by editing shell.json gets its blur when the shell syncs it.
jq '.bar.background = "glassy"' "$TMPDIR/home/.config/omarchy/shell.json" >"$TMPDIR/edited.json" &&
mv "$TMPDIR/edited.json" "$TMPDIR/home/.config/omarchy/shell.json"
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar sync-glass
[[ -f $glass_toggle ]] || fail "syncing a hand-edited glassy bar turns its blur on"
jq '.bar.background = "opaque"' "$TMPDIR/home/.config/omarchy/shell.json" >"$TMPDIR/edited.json" &&
mv "$TMPDIR/edited.json" "$TMPDIR/home/.config/omarchy/shell.json"
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar sync-glass
[[ ! -f $glass_toggle ]] || fail "syncing a hand-edited opaque bar turns its blur off"
pass "a bar background edited into shell.json gets its blur synced"
# A glassy bar whose blur can't be turned on keeps the background it had.
toggles_dir="${glass_toggle%/*}"
chmod a-w "$toggles_dir"
if bar_background glassy >/dev/null 2>&1; then
chmod u+w "$toggles_dir"
fail "a bar whose blur can't be turned on reports failure"
fi
chmod u+w "$toggles_dir"
[[ $(bar_background) == "opaque" && ! -f $glass_toggle ]] || fail "a bar whose blur can't be turned on keeps its old background" "$(bar_background)"
pass "a bar whose blur can't change keeps its old background"
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar set omarchy.bluetooth enabled false --json
grep -Fqx 'shell setBarWidget omarchy.bluetooth enabled false {}' \
"$TMPDIR/home/.local/state/omarchy/shell-ipc-calls"
+230
View File
@@ -0,0 +1,230 @@
#!/bin/bash
set -euo pipefail
source "$(dirname "${BASH_SOURCE[0]}")/base-test.sh"
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
stub_dir="$tmpdir/bin"
home_dir="$tmpdir/home"
state_home="$tmpdir/state"
toggles_dir="$state_home/omarchy/toggles/hypr"
mkdir -p "$stub_dir" "$home_dir"
for command in hyprctl omarchy-notification-send omarchy-restart-terminal omarchy-theme-set-foot omarchy-restart-btop; do
printf '#!/bin/bash\nprintf "%%s\\n" "%s $*" >>"%s/commands.log"\n' "$command" "$tmpdir" >"$stub_dir/$command"
done
chmod +x "$stub_dir"/*
run() {
HOME="$home_dir" XDG_RUNTIME_DIR="$tmpdir" XDG_STATE_HOME="$state_home" XDG_CONFIG_HOME="$home_dir/.config" OMARCHY_PATH="$ROOT" PATH="$stub_dir:$ROOT/bin:$PATH" "$@"
}
[[ $(run omarchy-hyprland-window-transparency) == "transparent" ]] || fail "windows start out transparent"
pass "windows start out transparent"
run omarchy-hyprland-window-transparency glassy
[[ $(run omarchy-hyprland-window-transparency) == "glassy" ]] || fail "glassy windows are reported"
cmp -s "$ROOT/default/hypr/toggles/window-glassy.lua" "$toggles_dir/window-glassy.lua" || fail "glassy windows are loaded after the theme"
grep -qx 'hyprctl reload' "$tmpdir/commands.log" || fail "new transparency reloads Hyprland"
grep -q '^omarchy-restart-btop ' "$tmpdir/commands.log" || fail "new transparency reloads running btop windows"
pass "glassy windows override the theme"
run omarchy-hyprland-window-transparency opaque
[[ $(run omarchy-hyprland-window-transparency) == "opaque" ]] || fail "opaque windows are reported"
[[ ! -e $toggles_dir/window-glassy.lua ]] || fail "opaque windows replace glassy ones"
pass "opaque windows replace glassy ones"
run omarchy-hyprland-window-transparency transparent
[[ $(run omarchy-hyprland-window-transparency) == "transparent" ]] || fail "transparent windows are reported"
[[ ! -e $toggles_dir/window-opaque.lua ]] || fail "transparent windows drop the override"
pass "transparent windows drop the override"
if run omarchy-hyprland-window-transparency frosted 2>/dev/null; then
fail "unknown transparency is refused"
fi
pass "unknown transparency is refused"
# A failed save keeps the transparency already chosen.
run omarchy-hyprland-window-transparency glassy
chmod a-w "$toggles_dir"
if run omarchy-hyprland-window-transparency opaque 2>/dev/null; then
chmod u+w "$toggles_dir"
fail "a failed save is reported"
fi
chmod u+w "$toggles_dir"
[[ $(run omarchy-hyprland-window-transparency) == "glassy" ]] || fail "a failed save keeps the transparency already chosen"
run omarchy-hyprland-window-transparency transparent
pass "a failed save keeps the transparency already chosen"
# GTK apps only ask Hyprland for blur under a CSS backdrop-filter, so glassy
# imports one into the user's gtk.css, leaving the rest of it alone.
gtk_css="$home_dir/.config/gtk-4.0/gtk.css"
mkdir -p "${gtk_css%/*}"
printf 'label { color: red; }\n' >"$gtk_css"
run omarchy-hyprland-window-transparency glassy
grep -qxF "@import url(\"file://$ROOT/default/gtk/glass.css\");" "$gtk_css" || fail "glassy GTK apps import the backdrop blur"
grep -qx 'label { color: red; }' "$gtk_css" || fail "glassy keeps the user's own GTK CSS"
run omarchy-hyprland-window-transparency glassy
(( $(grep -c 'omarchy-glass begin' "$gtk_css") == 1 )) || fail "glassy GTK apps import the backdrop blur once"
run omarchy-hyprland-window-transparency transparent
[[ $(<"$gtk_css") == 'label { color: red; }' ]] || fail "leaving glassy restores the user's GTK CSS"
grep -qF 'backdrop-filter: blur(' "$ROOT/default/gtk/glass.css" || fail "the GTK glass is a backdrop blur"
printf 'label { color: red; }' >"$gtk_css"
run omarchy-hyprland-window-transparency glassy
run omarchy-hyprland-window-transparency transparent
[[ $(od -An -c "$gtk_css" | tr -d ' \n') == 'label{color:red;}' ]] || fail "leaving glassy restores GTK CSS without a final newline"
# A GTK config that cannot be written fails the change rather than announcing it.
printf 'label { color: red; }\n' >"$gtk_css"
chmod a-w "$gtk_css"
: >"$tmpdir/commands.log"
if run omarchy-hyprland-window-transparency glassy 2>/dev/null; then
chmod u+w "$gtk_css"
fail "a GTK config that cannot be written fails the change"
fi
chmod u+w "$gtk_css"
! grep -q '^omarchy-notification-send' "$tmpdir/commands.log" || fail "a failed change is not announced"
[[ $(run omarchy-hyprland-window-transparency) == "transparent" ]] || fail "a failed change keeps the previous choice"
run omarchy-hyprland-window-transparency transparent
pass "glassy GTK apps get a backdrop blur, and leaving glassy takes it out"
# Opaque reaches the windows Omarchy makes transparent through their tag.
grep -qF 'tag = "+transparent-window"' "$ROOT/default/hypr/helpers.lua" || fail "transparent windows carry a tag"
grep -qF 'o.window({ tag = "transparent-window" }, { opacity = "1 1" })' "$ROOT/default/hypr/toggles/window-opaque.lua" ||
fail "opaque windows apply to the tagged windows"
pass "opaque windows apply to the windows Omarchy makes transparent"
# Terminals turn compositor blur down and fade their text with the window, so
# glassy gives each theme terminal file a see-through, blurred background.
theme_dir="$tmpdir/theme"
mkdir -p "$theme_dir"
printf '[colors.primary]\nbackground = "#000000"\n' >"$theme_dir/alacritty.toml"
printf '[colors-dark]\nbackground=000000\n' >"$theme_dir/foot.ini"
printf 'background #000000\n' >"$theme_dir/kitty.conf"
printf 'background = #000000\nbackground-opacity = 0.9\n' >"$theme_dir/ghostty.conf"
printf 'theme[main_bg]="#102030"\ntheme[main_fg]="#ffffff"\n' >"$theme_dir/btop.theme"
cp -r "$theme_dir" "$tmpdir/theme-before"
run omarchy-hyprland-window-transparency glassy
run omarchy-theme-set-glass "$theme_dir"
run omarchy-theme-set-glass "$theme_dir"
grep -qx 'opacity = 0.65' "$theme_dir/alacritty.toml" || fail "glassy alacritty is see-through"
grep -qx 'alpha=0.65' "$theme_dir/foot.ini" && grep -qx 'blur=yes' "$theme_dir/foot.ini" || fail "glassy foot is see-through and asks for blur"
grep -qx 'background_opacity 0.65' "$theme_dir/kitty.conf" || fail "glassy kitty is see-through"
(( $(grep -c 'omarchy-glass begin' "$theme_dir/foot.ini") == 1 )) || fail "glassy terminals get the glass once"
[[ $(grep '^theme\[main_bg\]=' "$theme_dir/btop.theme" | tail -1) == 'theme[main_bg]=""' ]] || fail "glassy btop uses the terminal background"
(( $(grep -c 'omarchy-glass begin' "$theme_dir/btop.theme") == 1 )) || fail "glassy btop gets the glass once"
pass "btop follows the glassy terminal background"
(( $(grep -c 'background-opacity' "$theme_dir/ghostty.conf") == 1 )) &&
grep -qx 'background-opacity = 0.9' "$theme_dir/ghostty.conf" || fail "a theme's own terminal opacity is kept"
grep -qx "gtk-custom-css = $ROOT/default/gtk/glass.css" "$theme_dir/ghostty.conf" ||
fail "a ghostty theme with its own opacity still gets the backdrop blur"
# A foot theme with its own alpha keeps it but still asks for blur, as
# Hyprland skips blur for foot unless it does.
own_alpha_dir="$tmpdir/own-alpha-theme"
mkdir -p "$own_alpha_dir"
printf '[colors-dark]\nbackground=000000\nalpha=0.8\n' >"$own_alpha_dir/foot.ini"
run omarchy-theme-set-glass "$own_alpha_dir"
(( $(grep -c '^alpha=' "$own_alpha_dir/foot.ini") == 1 )) && grep -qx 'alpha=0.8' "$own_alpha_dir/foot.ini" &&
grep -qx 'blur=yes' "$own_alpha_dir/foot.ini" || fail "a foot theme with its own alpha still asks for blur"
if command -v foot >/dev/null; then
foot --check-config -c "$own_alpha_dir/foot.ini" || fail "a foot theme with its own alpha stays valid"
fi
pass "a theme's own terminal opacity is kept, and its blur is still added"
if command -v foot >/dev/null; then
foot --check-config -c "$theme_dir/foot.ini" || fail "glassy foot config is valid"
fi
# A window table of the theme's own takes the glass in, as TOML refuses a
# table declared twice.
window_dir="$tmpdir/window-theme"
mkdir -p "$window_dir"
printf '[window] # mine\npadding = { x = 4, y = 4 }\nblur = false\n\n[colors.primary]\nbackground = "#000000"\n' >"$window_dir/alacritty.toml"
cp "$window_dir/alacritty.toml" "$tmpdir/window-before.toml"
run omarchy-theme-set-glass "$window_dir"
run omarchy-theme-set-glass "$window_dir"
(( $(grep -c '^\[window\]' "$window_dir/alacritty.toml") == 1 )) || fail "glassy alacritty keeps a single window table"
if command -v python3 >/dev/null; then
python3 -c 'import sys, tomllib; c = tomllib.load(open(sys.argv[1], "rb")); assert c["window"]["opacity"] == 0.65 and c["window"]["padding"]["x"] == 4' "$window_dir/alacritty.toml" ||
fail "glassy alacritty with a window table of its own stays valid TOML"
fi
HOME="$home_dir" XDG_STATE_HOME="$tmpdir/no-glass" OMARCHY_PATH="$ROOT" PATH="$stub_dir:$ROOT/bin:$PATH" omarchy-theme-set-glass "$window_dir"
cmp -s "$tmpdir/window-before.toml" "$window_dir/alacritty.toml" || fail "leaving glassy restores an alacritty window table"
pass "glassy alacritty joins a window table the theme already has"
# A file without a final newline gets one with the glass, and loses it again
# when the glass comes out, so leaving glassy restores it byte for byte.
bare_dir="$tmpdir/bare-theme"
mkdir -p "$bare_dir"
printf 'background #000000' >"$bare_dir/kitty.conf"
cp "$bare_dir/kitty.conf" "$tmpdir/bare-before.conf"
run omarchy-theme-set-glass "$bare_dir"
grep -qx 'background_opacity 0.65' "$bare_dir/kitty.conf" || fail "glassy reaches a file without a final newline"
grep -qx 'background #000000' "$bare_dir/kitty.conf" || fail "glassy ends the line before its block"
HOME="$home_dir" XDG_STATE_HOME="$tmpdir/no-glass" OMARCHY_PATH="$ROOT" PATH="$stub_dir:$ROOT/bin:$PATH" omarchy-theme-set-glass "$bare_dir"
cmp -s "$tmpdir/bare-before.conf" "$bare_dir/kitty.conf" || fail "leaving glassy restores a file without a final newline"
pass "leaving glassy restores a file without a final newline byte for byte"
btop_dir="$tmpdir/transparent-btop-theme"
mkdir -p "$btop_dir"
printf 'theme[main_bg]=""\n' >"$btop_dir/btop.theme"
cp "$btop_dir/btop.theme" "$tmpdir/btop-before.theme"
run omarchy-theme-set-glass "$btop_dir"
cmp -s "$tmpdir/btop-before.theme" "$btop_dir/btop.theme" || fail "btop themes already using the terminal background are kept"
pass "btop themes already using the terminal background are kept"
ghostty_dir="$tmpdir/ghostty-theme"
mkdir -p "$ghostty_dir"
printf 'background = #000000\n' >"$ghostty_dir/ghostty.conf"
run omarchy-theme-set-glass "$ghostty_dir"
grep -qx 'background-opacity = 0.65' "$ghostty_dir/ghostty.conf" &&
grep -qx "gtk-custom-css = $ROOT/default/gtk/glass.css" "$ghostty_dir/ghostty.conf" ||
fail "glassy ghostty is see-through and loads the GTK backdrop blur"
pass "glassy terminals get a see-through, blurred background"
run omarchy-hyprland-window-transparency transparent
run omarchy-theme-set-glass "$theme_dir"
diff -r "$tmpdir/theme-before" "$theme_dir" || fail "leaving glassy restores the terminal files"
pass "leaving glassy restores the terminal files"
# Running foot terminals take the theme's background alpha over OSC, so a
# change of transparency reaches them without a restart. script stands in for
# foot: its child sits on a real pty, and whatever reaches the pty lands in the
# typescript.
if command -v script >/dev/null; then
foot_home="$tmpdir/foot-home"
foot_theme="$foot_home/.local/state/omarchy/current/theme"
mkdir -p "$foot_theme" "$tmpdir/foot-bin"
printf 'background = "#102030"\nforeground = "#ffffff"\n' >"$foot_theme/colors.toml"
printf '[colors-dark]\nbackground=102030\n' >"$foot_theme/foot.ini"
script -qfc "sleep 30" "$tmpdir/typescript" >/dev/null 2>&1 &
script_pid=$!
for _ in {1..50}; do pgrep -P "$script_pid" >/dev/null && break; sleep 0.1; done
# Only the foot lookup is faked; finding its shell goes to the real pgrep.
real_pgrep=$(command -v pgrep)
printf '#!/bin/bash\nif [[ $1 == "-x" && $2 == "foot" ]]; then echo %s; else exec %s "$@"; fi\n' "$script_pid" "$real_pgrep" >"$tmpdir/foot-bin/pgrep"
chmod +x "$tmpdir/foot-bin/pgrep"
set_foot() {
HOME="$foot_home" PATH="$tmpdir/foot-bin:$ROOT/bin:$PATH" omarchy-theme-set-foot
sleep 0.3
}
set_foot
grep -qF $'\e]11;[100]#102030' "$tmpdir/typescript" || fail "running foot terminals stay opaque without glass"
printf '# omarchy-glass begin\n[colors-dark]\nalpha=0.65\nblur=yes\n# omarchy-glass end\n' >>"$foot_theme/foot.ini"
set_foot
grep -qF $'\e]11;[65]#102030' "$tmpdir/typescript" || fail "running foot terminals turn glassy"
kill "$script_pid" 2>/dev/null || true
pass "running foot terminals take the theme's background alpha"
else
skip "script is not installed"
fi
grep -qx 'blur=yes' "$ROOT/default/themed/foot.ini.tpl" || fail "foot starts ready to blur"
grep -qx 'dynamic_background_opacity yes' "$ROOT/default/themed/kitty.conf.tpl" || fail "kitty starts ready to change opacity"
pass "foot and kitty start ready for glass, since they read that only at launch"
+44 -8
View File
@@ -365,7 +365,7 @@ assert(
)
assertDeepEqual(
defaultItems.filter(item => item.parent === 'trigger.appearance.top-bar').map(item => item.label),
['Hide Bar', 'Show Bar', 'Position Bar', 'Transparent Bar', 'Opaque Bar', 'Indicators'],
['Hide Bar', 'Show Bar', 'Position Bar', 'Bar Background', 'Bar Indicators'],
'menu gathers the Menu Bar under Trigger > Appearance'
)
assert(
@@ -384,16 +384,43 @@ assert(
['top', 'bottom', 'left', 'right'].every(position => defaultById[`trigger.appearance.top-bar.position.${position}`].action === `omarchy-bar position ${position}`),
'menu lists all Menu Bar positions under Position'
)
assert(
defaultById['trigger.appearance.top-bar.transparent'].action === 'omarchy-bar transparent true'
&& defaultById['trigger.appearance.top-bar.opaque'].action === 'omarchy-bar transparent false'
&& defaultById['trigger.appearance.top-bar.transparent'].when === '! ' + defaultById['trigger.appearance.top-bar.opaque'].when,
'menu offers Transparent Bar while the bar is opaque and Opaque Bar while it is transparent'
assertDeepEqual(
defaultItems.filter(item => item.parent === 'trigger.appearance.top-bar.background').map(item => [item.label, item.action]),
[
['Opaque Bar', 'omarchy-bar background opaque'],
['Glassy Bar', 'omarchy-bar background glassy'],
['Clear Bar', 'omarchy-bar background none']
],
'menu picks the bar background from Menu Bar > Bar Background'
)
// Exactly the bar's current background is checked. Ask bash, against a
// shell.json in each state, which rows it checks.
{
const os = require('os')
const { execFileSync } = require('child_process')
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'menu-bar-background-'))
fs.mkdirSync(path.join(home, '.config/omarchy'), { recursive: true })
const checked = bar => {
fs.writeFileSync(path.join(home, '.config/omarchy/shell.json'), JSON.stringify({ bar }))
return ['opaque', 'glassy', 'clear'].filter(look => {
try {
execFileSync('bash', ['-c', defaultById[`trigger.appearance.top-bar.background.${look}`].checked], { env: { ...process.env, HOME: home, OMARCHY_PATH: root, PATH: `${root}/bin:${process.env.PATH}` } })
return true
} catch (e) {
return false
}
})
}
assertDeepEqual(checked({}), ['opaque'], 'menu checks Opaque Bar while the bar is opaque')
assertDeepEqual(checked({ background: 'glassy' }), ['glassy'], 'menu checks Glassy Bar while the bar is glassy')
assertDeepEqual(checked({ background: 'none' }), ['clear'], 'menu checks Clear Bar while the bar has no background')
assertDeepEqual(checked({ transparent: true }), ['clear'], 'menu checks Clear Bar for a bar made transparent before backgrounds')
fs.rmSync(home, { recursive: true, force: true })
}
assertDeepEqual(
defaultItems.filter(item => item.parent === 'trigger.appearance.top-bar.indicators').map(item => item.label),
['Battery Percentage', 'Clock'],
'menu gathers bar indicator options under Menu Bar > Indicators'
'menu gathers bar indicator options under Menu Bar > Bar Indicators'
)
const clockModel = requireFromRoot('shell/plugins/panels/clock/Model.js')
const clockRows = defaultItems.filter(item => item.parent === 'trigger.appearance.top-bar.indicators.clock')
@@ -417,8 +444,17 @@ assertDeepEqual(
[['Theme Corners', 'omarchy-hyprland-corners theme'], ['Sharp Corners', 'omarchy-hyprland-corners sharp'], ['Rounded Corners', 'omarchy-hyprland-corners rounded']],
'menu picks window corners from Appearance > Window Corners'
)
assertDeepEqual(
defaultItems.filter(item => item.parent === 'trigger.appearance.window-transparency').map(item => [item.label, item.action]),
[
['Opaque Windows', 'omarchy-hyprland-window-transparency opaque'],
['Transparent Windows', 'omarchy-hyprland-window-transparency transparent'],
['Glassy Windows', 'omarchy-hyprland-window-transparency glassy']
],
'menu picks window transparency from Appearance > Window Transparency'
)
const triggerIds = triggerItems.map(item => item.id)
const appearanceLabels = ['Animations', 'Menu Bar', 'Window Gaps', 'Window Corners', '1-Window Ratio', 'Workspace Layout', 'Herdr Theme Sync']
const appearanceLabels = ['Animations', 'Menu Bar', 'Window Gaps', 'Window Corners', 'Window Transparency', '1-Window Ratio', 'Workspace Layout', 'Herdr Theme Sync']
assertEqual(triggerIds.indexOf('trigger.toggle'), triggerIds.indexOf('trigger.appearance') + 1, 'menu places Toggle right after Appearance')
assertDeepEqual(
defaultItems.filter(item => item.parent === 'trigger.appearance').map(item => item.label),
+1 -1
View File
@@ -41,7 +41,7 @@ printf 'no newline {{ accent }}' >"$themed/tail.txt.tpl"
# Function arguments may be separated by any whitespace, tabs included.
printf '{{ mix\tbackground accent 30%% }} {{\tmix_rgb red\tblue 50%%\t}} {{ gradient_start\thyprland_active_border accent }}\n' >"$themed/tabs.txt.tpl"
HOME="$home" OMARCHY_PATH="$ROOT" PATH="$ROOT/bin:$PATH" "$ROOT/bin/omarchy-theme-set-templates"
HOME="$home" XDG_STATE_HOME="$home/.local/state" OMARCHY_PATH="$ROOT" PATH="$ROOT/bin:$PATH" "$ROOT/bin/omarchy-theme-set-templates"
# Expected values were produced by the sed-based renderer this replaced, so
# every theme keeps rendering byte for byte the same.
+23 -1
View File
@@ -106,8 +106,30 @@ for virt in vm none; do
HOME="$test_home" OMARCHY_PATH="$ROOT" bash "$ROOT/install/user/hardware/vm-no-animations.sh" >/dev/null
if [[ $virt == "vm" ]]; then
[[ -f $animations_flag ]] || fail "a VM install turns animations off"
[[ -f ${animations_flag%/*}/window-opaque.lua ]] || fail "a VM install starts with opaque windows"
else
[[ ! -f $animations_flag ]] || fail "a machine that is not a VM keeps its animations"
[[ ! -f ${animations_flag%/*}/window-opaque.lua ]] || fail "a machine that is not a VM keeps transparent windows"
fi
done
pass "a VM install starts without animations"
pass "a VM install starts without animations and with opaque windows"
# Animations off no longer makes windows opaque. The migration takes that rule
# out of a saved flag, keeping anything else in it, and keeps those windows
# opaque through Window Transparency instead.
migration_home=$(mktemp -d)
migration_flags="$migration_home/.local/state/omarchy/toggles/hypr"
mkdir -p "$migration_flags"
printf 'hl.config({ animations = { enabled = false } })\n\no.window(".*", { opacity = "1 1" })\n-- mine\n' >"$migration_flags/no-animations.lua"
for _ in 1 2; do
HOME="$migration_home" XDG_STATE_HOME="" OMARCHY_PATH="$ROOT" bash -euo pipefail "$ROOT/migrations/1791705142.sh" >/dev/null
done
! grep -q 'opacity' "$migration_flags/no-animations.lua" || fail "the migration drops the opacity rule from a saved animations flag"
grep -qx -- '-- mine' "$migration_flags/no-animations.lua" || fail "the migration keeps the rest of a saved animations flag"
[[ -f $migration_flags/window-opaque.lua ]] || fail "the migration keeps windows opaque where animations are off"
rm -f "$migration_flags/window-opaque.lua"
printf 'x\n' >"$migration_flags/window-glassy.lua"
HOME="$migration_home" XDG_STATE_HOME="" OMARCHY_PATH="$ROOT" bash -euo pipefail "$ROOT/migrations/1791705142.sh" >/dev/null
[[ ! -f $migration_flags/window-opaque.lua ]] || fail "the migration leaves a chosen transparency alone"
rm -rf "$migration_home"
pass "the migration moves opaque windows from the animations flag to Window Transparency"