* 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>
552 lines
25 KiB
Bash
Executable File
552 lines
25 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
|
|
|
export PATH="$ROOT/bin:$PATH"
|
|
|
|
require_command jq
|
|
require_command lua
|
|
require_command python3
|
|
|
|
jq empty "$ROOT/config/omarchy/shell.json"
|
|
pass "default shell.json is valid JSON"
|
|
|
|
jq -e '.version == 1 and (.bar.layout.left | type == "array") and (.bar.layout.center | type == "array") and (.bar.layout.right | type == "array")' "$ROOT/config/omarchy/shell.json" >/dev/null
|
|
pass "default shell.json has versioned bar layout"
|
|
|
|
# Pinning the whole row made this fail every time an unrelated widget moved,
|
|
# so assert the adjacency the name is about and let the rest of the row change.
|
|
jq -e '
|
|
def ids: map(.id // .);
|
|
(.bar.layout.center | ids) as $ids |
|
|
($ids | index("omarchy.weather")) as $weather |
|
|
($ids | index("omarchy.system-update")) as $update |
|
|
$weather != null and $update == $weather + 1
|
|
' "$ROOT/config/omarchy/shell.json" >/dev/null
|
|
pass "default center layout keeps update next to weather"
|
|
|
|
jq -e '
|
|
(.bar.centerAnchor // "") as $anchor |
|
|
any(.bar.layout.center[]; (.id // .) == $anchor)
|
|
' "$ROOT/config/omarchy/shell.json" >/dev/null
|
|
pass "default center anchor exists in center layout"
|
|
|
|
jq -e '
|
|
def ids: map(.id // .);
|
|
(.bar.layout.center | ids) as $ids |
|
|
($ids | index("omarchy.elsewhen")) as $elsewhen |
|
|
($ids | index("omarchy.clock")) as $clock |
|
|
($ids | index("omarchy.weather")) as $weather |
|
|
$clock != null and $elsewhen == $clock + 1 and $weather > $elsewhen
|
|
' "$ROOT/config/omarchy/shell.json" >/dev/null
|
|
pass "default center layout puts elsewhen immediately after the clock and before weather"
|
|
|
|
jq -e '
|
|
any(.bar.layout.center[]; (.id // .) == "omarchy.clock" and (.formatAlt // "") == "d MMMM \u0027W\u0027ww yyyy")
|
|
' "$ROOT/config/omarchy/shell.json" >/dev/null
|
|
pass "default clock date format has no leading zero"
|
|
|
|
jq -e '
|
|
def ids: map(.id // .);
|
|
(.bar.layout.right | ids) as $ids |
|
|
($ids | index("omarchy.tray")) as $tray |
|
|
($ids | index("omarchy.agents")) as $agents |
|
|
$tray != null and $agents == $tray + 1
|
|
' "$ROOT/config/omarchy/shell.json" >/dev/null
|
|
pass "default right layout keeps agents next to the tray"
|
|
|
|
ROOT="$ROOT" python3 <<'PY'
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
root = Path(os.environ["ROOT"])
|
|
config = json.loads((root / "config/omarchy/shell.json").read_text())
|
|
manifests = {}
|
|
for manifest_path in (root / "shell/plugins").glob("**/*.manifest.json"):
|
|
data = json.loads(manifest_path.read_text())
|
|
manifests[data.get("id", "")] = (manifest_path, data)
|
|
for manifest_path in (root / "shell/plugins").glob("**/manifest.json"):
|
|
data = json.loads(manifest_path.read_text())
|
|
manifests[data.get("id", "")] = (manifest_path, data)
|
|
|
|
entries = []
|
|
for section in ("left", "center", "right"):
|
|
entries.extend(config["bar"]["layout"][section])
|
|
|
|
missing = []
|
|
bad = []
|
|
for entry in entries:
|
|
widget_id = entry["id"] if isinstance(entry, dict) else str(entry)
|
|
if not widget_id.startswith("omarchy."):
|
|
continue
|
|
|
|
row = manifests.get(widget_id)
|
|
if row is None:
|
|
missing.append(widget_id)
|
|
continue
|
|
manifest_path, manifest = row
|
|
|
|
if "bar-widget" not in manifest.get("kinds", []):
|
|
bad.append(f"{widget_id}: missing bar-widget kind")
|
|
entry_point = manifest.get("entryPoints", {}).get("barWidget")
|
|
if not entry_point:
|
|
bad.append(f"{widget_id}: missing barWidget entry point")
|
|
elif not (manifest_path.parent / entry_point).exists():
|
|
bad.append(f"{widget_id}: missing {entry_point}")
|
|
|
|
if missing or bad:
|
|
for item in missing:
|
|
print(f"missing manifest for {item}", file=sys.stderr)
|
|
for item in bad:
|
|
print(item, file=sys.stderr)
|
|
sys.exit(1)
|
|
PY
|
|
pass "default bar widget ids resolve to manifests and entry points"
|
|
|
|
ROOT="$ROOT" python3 <<'PY'
|
|
import os
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
root = Path(os.environ["ROOT"])
|
|
home = Path.home()
|
|
pkgs_candidates = [
|
|
root.parent / "omarchy-pkgs/pkgbuilds",
|
|
root.parent / "omarchy/omarchy-pkgs/pkgbuilds",
|
|
root.parent.parent / "omarchy-pkgs/pkgbuilds",
|
|
root.parent / "omacom/omarchy-pkgs/pkgbuilds",
|
|
root.parent.parent / "omacom/omarchy-pkgs/pkgbuilds",
|
|
home / "Work/omacom/omarchy-pkgs/pkgbuilds",
|
|
]
|
|
# Checkouts differ per machine, so allow an explicit pointer at the sibling repo.
|
|
# Accepts either the omarchy-pkgs checkout or its pkgbuilds/ directory.
|
|
override = os.environ.get("OMARCHY_PKGS_PATH")
|
|
if override:
|
|
pkgs_candidates = [Path(override) / "pkgbuilds", Path(override)] + pkgs_candidates
|
|
pkgs_root = next((path for path in pkgs_candidates if path.exists()), None)
|
|
if pkgs_root is None:
|
|
print("not ok - omarchy-pkgs checkout found for PKGBUILD coverage", file=sys.stderr)
|
|
print(
|
|
"looked in:\n " + "\n ".join(str(path) for path in pkgs_candidates) +
|
|
"\nset OMARCHY_PKGS_PATH to the omarchy-pkgs checkout",
|
|
file=sys.stderr,
|
|
)
|
|
sys.exit(1)
|
|
settings_pkgbuild_path = pkgs_root / "omarchy-settings/PKGBUILD"
|
|
omarchy_pkgbuild_path = pkgs_root / "omarchy/PKGBUILD"
|
|
if not settings_pkgbuild_path.exists():
|
|
settings_pkgbuild_path = pkgs_root / "omarchy-settings-dev/PKGBUILD"
|
|
if not omarchy_pkgbuild_path.exists():
|
|
omarchy_pkgbuild_path = pkgs_root / "omarchy-dev/PKGBUILD"
|
|
pkgbuild = settings_pkgbuild_path.read_text()
|
|
omarchy_pkgbuild = omarchy_pkgbuild_path.read_text()
|
|
errors = []
|
|
package_defaults = [
|
|
("default/uwsm/env.d/10-omarchy", "/usr/share/uwsm/env.d/10-omarchy", "uwsm/env"),
|
|
("default/uwsm/default", None, "uwsm/default"),
|
|
("default/environment.d/10-omarchy-fcitx.conf", "/usr/lib/environment.d/10-omarchy-fcitx.conf", "environment.d/fcitx.conf"),
|
|
("default/fontconfig/conf.avail/50-omarchy.conf", "/usr/share/fontconfig/conf.avail/50-omarchy.conf", "fontconfig/fonts.conf"),
|
|
("default/xdg-terminal-exec/hyprland-xdg-terminals.list", "/usr/share/xdg-terminal-exec/hyprland-xdg-terminals.list", "xdg-terminals.list"),
|
|
("default/applications/mimeapps.list", "/usr/share/applications/mimeapps.list", "mimeapps.list"),
|
|
("etc/fastfetch/config.jsonc", "/etc/fastfetch/config.jsonc", "fastfetch/config.jsonc"),
|
|
("default/systemd/user/bt-agent.service", "/usr/lib/systemd/user/bt-agent.service", "systemd/user/bt-agent.service"),
|
|
("default/systemd/user/omarchy-sleep-lock.service", "/usr/lib/systemd/user/omarchy-sleep-lock.service", "systemd/user/omarchy-sleep-lock.service"),
|
|
("default/systemd/user/omarchy-recover-internal-monitor.service", "/usr/lib/systemd/user/omarchy-recover-internal-monitor.service", "systemd/user/omarchy-recover-internal-monitor.service"),
|
|
("default/systemd/user/omarchy-migrate-notify.service", "/usr/lib/systemd/user/omarchy-migrate-notify.service", "systemd/user/omarchy-migrate-notify.service"),
|
|
("default/systemd/user/omarchy-tailscale-receive.service", "/usr/lib/systemd/user/omarchy-tailscale-receive.service", "systemd/user/omarchy-tailscale-receive.service"),
|
|
("default/systemd/user/omarchy-fcitx5.service", "/usr/lib/systemd/user/omarchy-fcitx5.service", "systemd/user/omarchy-fcitx5.service"),
|
|
("default/systemd/user/omarchy-crash-watch.service", "/usr/lib/systemd/user/omarchy-crash-watch.service", "systemd/user/omarchy-crash-watch.service"),
|
|
("default/systemd/zram-generator.conf.d/90-omarchy.conf", "/usr/lib/systemd/zram-generator.conf.d/90-omarchy.conf", "systemd/zram-generator.conf.d/90-omarchy.conf"),
|
|
("default/systemd/system/plocate-updatedb.service.d/10-omarchy.conf", "/usr/lib/systemd/system/plocate-updatedb.service.d/10-omarchy.conf", "systemd/system/plocate-updatedb.service.d/10-omarchy.conf"),
|
|
("default/fonts/omarchy/omarchy.ttf", "/usr/share/fonts/omarchy/omarchy.ttf", "omarchy.ttf"),
|
|
("default/snapper/root", "/etc/snapper/config-templates/omarchy", "snapper/root"),
|
|
]
|
|
|
|
for source, destination, legacy in package_defaults:
|
|
if not (root / source).exists():
|
|
errors.append(f"missing package default source: {source}")
|
|
if (root / "config" / legacy).exists():
|
|
errors.append(f"legacy path still in config/: {legacy}")
|
|
if destination and (source not in pkgbuild or destination not in pkgbuild):
|
|
errors.append(f"PKGBUILD does not explicitly install {source} -> {destination}")
|
|
|
|
# A user unit has to be on the machine before anything turns it on: shipped in
|
|
# /usr/lib/systemd/user, or copied out of default/ by the command enabling it.
|
|
def installs_first(text):
|
|
source = re.search(r'(\w+)="[^"\n]*default/systemd/user/', text)
|
|
held = rf"|\$\{{?{source[1]}\b" if source else ""
|
|
copy = re.search(rf"^[ \t]*(?:sudo[ \t]+)?(?:install|cp)\b[^\n]*(?:default/systemd/user/{held})", text, re.MULTILINE)
|
|
enable = text.find("systemctl --user enable")
|
|
return bool(copy) and (enable < 0 or copy.start() < enable)
|
|
|
|
named = 'unit="$OMARCHY_PATH/default/systemd/user/a.service"\n'
|
|
if installs_first(named + "systemctl --user enable a.service\n"):
|
|
errors.append("unit check accepts a command that names a unit's source but never installs it")
|
|
if installs_first(named + 'systemctl --user enable a.service\ninstall -Dm644 "$unit" "$target"\n'):
|
|
errors.append("unit check accepts a command that installs a unit after enabling it")
|
|
|
|
scripts = [path.read_text(errors="ignore") for folder in ("bin", "install", "migrations")
|
|
for path in (root / folder).rglob("*") if path.is_file()]
|
|
for unit in sorted(path.name for path in (root / "default/systemd/user").glob("*.service")):
|
|
if f"/usr/lib/systemd/user/{unit}" in pkgbuild:
|
|
continue
|
|
named = [text for text in scripts if unit in text]
|
|
if not any(installs_first(text) for text in named):
|
|
errors.append(f"PKGBUILD does not ship default/systemd/user/{unit} and no command installs it")
|
|
elif any("systemctl --user enable" in text and not installs_first(text) for text in named):
|
|
errors.append(f"{unit} is enabled without being shipped by PKGBUILD or installed first")
|
|
|
|
# Existing users have an absolute wants symlink to the old unit path, and the
|
|
# migration that repoints it only runs for users who run an update -- the
|
|
# opposite of who the notifier is for. Dropping this alias strands them.
|
|
notify_alias = 'ln -sfn omarchy-migrate-notify.service "$pkgdir/usr/lib/systemd/user/omarchy-update-user-notify.service"'
|
|
if notify_alias not in pkgbuild:
|
|
errors.append(
|
|
"PKGBUILD does not ship the omarchy-update-user-notify.service compatibility "
|
|
"alias, so users who have not run migration 1785095882 lose the login notifier"
|
|
)
|
|
|
|
alpm_hooks = [
|
|
"00-omarchy-update-guard.hook",
|
|
"10-omarchy-hyprland-reload-pause.hook",
|
|
"90-omarchy-hyprland-reload-resume.hook",
|
|
]
|
|
for hook in alpm_hooks:
|
|
source = f"default/libalpm/hooks/{hook}"
|
|
destination = f"/usr/share/libalpm/hooks/{hook}"
|
|
if not (root / source).exists():
|
|
errors.append(f"missing package default source: {source}")
|
|
if source not in omarchy_pkgbuild or destination not in omarchy_pkgbuild:
|
|
errors.append(f"omarchy PKGBUILD does not install {source} -> {destination}")
|
|
|
|
if errors:
|
|
print("\n".join(errors), file=sys.stderr)
|
|
sys.exit(1)
|
|
PY
|
|
pass "package-owned defaults live outside config"
|
|
|
|
grep -F 'dofile((os.getenv("OMARCHY_PATH") or "/usr/share/omarchy") .. "/default/hypr/bootstrap.lua")' "$ROOT/config/hypr/hyprland.lua" >/dev/null
|
|
grep -F 'require("default.hypr.omarchy")' "$ROOT/config/hypr/hyprland.lua" >/dev/null
|
|
grep -F 'package.path = home' "$ROOT/default/hypr/bootstrap.lua" >/dev/null
|
|
grep -F '/.local/state/?.lua;' "$ROOT/default/hypr/bootstrap.lua" >/dev/null
|
|
pass "Hyprland user entrypoint keeps package and state path bootstrap in defaults"
|
|
|
|
OMARCHY_PATH="$ROOT" lua <<'LUA'
|
|
package.loaded["default.hypr.omarchy"] = true
|
|
package.loaded["default.hypr.require_optional"] = true
|
|
package.loaded["hypr.looknfeel"] = true
|
|
package.loaded["omarchy.current.theme.hyprland"] = true
|
|
package.loaded["unrelated.module"] = true
|
|
|
|
dofile(os.getenv("OMARCHY_PATH") .. "/default/hypr/bootstrap.lua")
|
|
|
|
assert(package.loaded["default.hypr.omarchy"] == nil)
|
|
assert(package.loaded["default.hypr.require_optional"] == nil)
|
|
assert(package.loaded["hypr.looknfeel"] == nil)
|
|
assert(package.loaded["omarchy.current.theme.hyprland"] == nil)
|
|
assert(package.loaded["unrelated.module"] == true)
|
|
LUA
|
|
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"
|
|
cat >"$ipc_mock_bin/omarchy-shell" <<'SH'
|
|
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
mkdir -p "$HOME/.local/state/omarchy"
|
|
printf '%s\n' "$*" >>"$HOME/.local/state/omarchy/shell-ipc-calls"
|
|
printf 'ok\n'
|
|
SH
|
|
chmod +x "$ipc_mock_bin/omarchy-shell"
|
|
export PATH="$ipc_mock_bin:$PATH"
|
|
|
|
cat >"$TMPDIR/home/.config/omarchy/shell.json" <<'JSON'
|
|
{
|
|
"version": 1,
|
|
"bar": {
|
|
"layout": {
|
|
"left": [{ "id": "omarchy.menu" }, { "id": "omarchy.workspaces" }, { "id": "omarchy.active-window" }],
|
|
"center": [{ "id": "omarchy.clock" }, { "id": "omarchy.weather" }, { "id": "omarchy.system-update" }, { "id": "omarchy.tailscale" }],
|
|
"right": [{ "id": "omarchy.tray" }, { "id": "omarchy.microphone" }, { "id": "omarchy.bluetooth" }]
|
|
}
|
|
},
|
|
"plugins": []
|
|
}
|
|
JSON
|
|
|
|
mkdir -p "$TMPDIR/home/.config/omarchy/plugins/local.demo-bar"
|
|
cat >"$TMPDIR/home/.config/omarchy/plugins/local.demo-bar/manifest.json" <<'JSON'
|
|
{
|
|
"schemaVersion": 1,
|
|
"id": "local.demo-bar",
|
|
"name": "Demo bar",
|
|
"version": "1.0.0",
|
|
"author": "Test",
|
|
"description": "Replacement bar for config tests",
|
|
"kinds": ["bar"],
|
|
"entryPoints": { "bar": "Bar.qml" }
|
|
}
|
|
JSON
|
|
touch "$TMPDIR/home/.config/omarchy/plugins/local.demo-bar/Bar.qml"
|
|
|
|
if HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar use local.nonexistent-bar 2>/dev/null; then
|
|
fail "bar use accepted an unknown bar option"
|
|
fi
|
|
pass "bar use rejects an unknown bar option"
|
|
|
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar use local.demo-bar
|
|
jq -e '.bar.id == "local.demo-bar"' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
|
|
pass "shell config selects a bar option"
|
|
|
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar reset
|
|
jq -e '.bar.id == null' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
|
|
pass "shell config resets to built-in bar option"
|
|
|
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar move omarchy.active-window right
|
|
grep -Fqx 'shell moveBarWidget omarchy.active-window {"section":"right"}' \
|
|
"$TMPDIR/home/.local/state/omarchy/shell-ipc-calls"
|
|
pass "bar move accepts a positional target section"
|
|
|
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar move omarchy.active-window left
|
|
grep -Fqx 'shell moveBarWidget omarchy.active-window {"section":"left"}' \
|
|
"$TMPDIR/home/.local/state/omarchy/shell-ipc-calls"
|
|
pass "bar move can restore a widget with positional syntax"
|
|
|
|
if HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar move omarchy.active-window left --section right 2>/dev/null; then
|
|
fail "bar move accepted positional and flagged target sections"
|
|
fi
|
|
pass "bar move rejects conflicting target section syntax"
|
|
|
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar position bottom
|
|
jq -e '
|
|
.bar.position == "bottom" and
|
|
.plugins == []
|
|
' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
|
|
pass "shell config sets bar position"
|
|
|
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar transparent true
|
|
jq -e '
|
|
.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.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"
|
|
pass "bar set accepts false JSON values"
|
|
|
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar set omarchy.bluetooth optional null --json
|
|
grep -Fqx 'shell setBarWidget omarchy.bluetooth optional null {}' \
|
|
"$TMPDIR/home/.local/state/omarchy/shell-ipc-calls"
|
|
pass "bar set accepts null JSON values"
|
|
|
|
if HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar set omarchy.bluetooth broken '{' --json 2>/dev/null; then
|
|
fail "bar set accepted malformed JSON"
|
|
fi
|
|
pass "bar set rejects malformed JSON"
|
|
|
|
if HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" omarchy-bar set omarchy.bluetooth broken 'false null' --json 2>/dev/null; then
|
|
fail "bar set accepted multiple JSON values"
|
|
fi
|
|
pass "bar set rejects multiple JSON values"
|
|
|
|
mock_bin="$TMPDIR/mock-bin"
|
|
mkdir -p "$mock_bin"
|
|
|
|
cat >"$mock_bin/omarchy-refresh-config" <<'SH'
|
|
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
relative_path="${1:-}"
|
|
[[ -n $relative_path ]] || exit 1
|
|
mkdir -p "$HOME/.config/$(dirname "$relative_path")"
|
|
cp "$OMARCHY_PATH/config/$relative_path" "$HOME/.config/$relative_path"
|
|
SH
|
|
|
|
cat >"$mock_bin/omarchy-restart-shell" <<'SH'
|
|
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
mkdir -p "$HOME/.local/state/omarchy"
|
|
touch "$HOME/.local/state/omarchy/restart-shell-called"
|
|
SH
|
|
|
|
cat >"$mock_bin/omarchy-shell" <<'SH'
|
|
#!/bin/bash
|
|
[[ ${OMARCHY_TEST_SHELL_DOWN:-0} == "1" ]] && exit 1
|
|
printf 'ok\n'
|
|
SH
|
|
|
|
cat >"$mock_bin/omarchy-installed-service-dropbox" <<'SH'
|
|
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
[[ ${OMARCHY_TEST_DROPBOX:-0} == "1" ]]
|
|
SH
|
|
|
|
cat >"$mock_bin/omarchy-installed-service-tailscale" <<'SH'
|
|
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
[[ ${OMARCHY_TEST_TAILSCALE:-0} == "1" ]]
|
|
SH
|
|
|
|
chmod +x "$mock_bin"/*
|
|
mock_path="$mock_bin:$ROOT/bin:$PATH"
|
|
|
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" PATH="$mock_path" OMARCHY_TEST_DROPBOX=0 OMARCHY_TEST_TAILSCALE=0 omarchy-bar defaults
|
|
jq -e --slurpfile defaults "$ROOT/config/omarchy/shell.json" '
|
|
.bar == $defaults[0].bar and
|
|
.plugins == []
|
|
' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
|
|
pass "bar defaults restores the stock bar"
|
|
|
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" PATH="$mock_path" OMARCHY_TEST_DROPBOX=1 OMARCHY_TEST_TAILSCALE=1 omarchy-bar defaults
|
|
jq -e '
|
|
def ids: map(.id // .);
|
|
(.bar.layout.right | ids) as $right |
|
|
($right | index("omarchy.tray")) as $tray |
|
|
($right | index("omarchy.tailscale") == $tray + 1) and
|
|
($right | index("omarchy.dropbox") == $tray + 2) and
|
|
(.bar.layout.center | ids | index("omarchy.tailscale") == null)
|
|
' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
|
|
pass "bar defaults places plugins for running optional services"
|
|
|
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" PATH="$mock_path" \
|
|
OMARCHY_TEST_SHELL_DOWN=1 OMARCHY_TEST_DROPBOX=1 OMARCHY_TEST_TAILSCALE=1 \
|
|
omarchy-bar defaults
|
|
jq -e '
|
|
def ids: map(.id // .);
|
|
(.bar.layout.right | ids) as $right |
|
|
($right | index("omarchy.tray")) as $tray |
|
|
($right | index("omarchy.tailscale") == $tray + 1) and
|
|
($right | index("omarchy.dropbox") == $tray + 2) and
|
|
(.bar.layout.center | ids | index("omarchy.tailscale") == null)
|
|
' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
|
|
pass "bar defaults places service widgets without a running shell"
|
|
|
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" PATH="$mock_path" OMARCHY_TEST_DROPBOX=0 OMARCHY_TEST_TAILSCALE=0 omarchy-refresh-shell
|
|
jq -e '
|
|
def ids: map(.id // .);
|
|
([.bar.layout.left, .bar.layout.center, .bar.layout.right] | map(ids) | add) as $all |
|
|
($all | index("omarchy.dropbox") == null) and
|
|
($all | index("omarchy.tailscale") == null)
|
|
' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
|
|
pass "shell refresh keeps optional service widgets absent when services are unavailable"
|
|
|
|
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" PATH="$mock_path" OMARCHY_TEST_DROPBOX=1 OMARCHY_TEST_TAILSCALE=1 omarchy-refresh-shell
|
|
jq -e '
|
|
def ids: map(.id // .);
|
|
(.bar.layout.right | ids) as $right |
|
|
($right | index("omarchy.tray")) as $tray |
|
|
($right | index("omarchy.tailscale") == $tray + 1) and
|
|
($right | index("omarchy.dropbox") == $tray + 2) and
|
|
(.bar.layout.center | ids | index("omarchy.tailscale") == null)
|
|
' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
|
|
[[ -f $TMPDIR/home/.local/state/omarchy/restart-shell-called ]] || fail "shell refresh restarts shell"
|
|
pass "shell refresh places optional service widgets when services are available"
|
|
|
|
if grep -RIl 'upgrade-to-quattro\|Omarchy 4\.0 is upgraded' "$ROOT/migrations" >/dev/null; then
|
|
fail "4.0 upgrade is not modeled as a migration"
|
|
fi
|
|
pass "4.0 upgrade is handled outside the migration runner"
|