Files
omarchy/test/acceptance.d/base-test.sh
T
David Heinemeier HanssonandClaude Opus 5.5 24f1243120 Make the theme picker open instantly (#13403)
* Run menu summon actions in-process

A menu action that only summons another shell plugin spawned bash and a
qs ipc client to ask this same shell to do it, about 60ms of the path.
Call shell.summon directly instead, and fall back to bash when the call
is refused or the action is anything more than a bare summon.

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

* Open the theme picker from rows held in the shell

Opening the theme picker ran omarchy-theme-switcher to rebuild its index
and then made a second IPC call, about 170ms before the picker mapped.
The picker now holds the theme rows itself, opens from them at once, and
refreshes them behind the open via omarchy-theme-switcher --print-rows.
It applies the chosen theme with omarchy-theme-set directly, so
omarchy-theme-set no longer preloads the picker.

From the keybinding to the overlay mapped drops from ~245ms to ~83ms.

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

* Keep the image picker surface mapped between opens

Each open mapped a fresh surface, which rendered its first frames before
Hyprland sent its fractional scale: the pixel ratio stepped 2, 1, then
1.6, so the picker flashed blurry for ~130ms and re-uploaded every
thumbnail texture. Keep the surface and park it transparent and
input-less on the bottom layer while closed, since anything on the
overlay layer blocks direct scanout for fullscreen apps. It follows the
focused monitor on each open.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:10:49 +02:00

137 lines
3.8 KiB
Bash

#!/bin/bash
if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
echo "source test/acceptance.d/base-test.sh from an acceptance test; do not run it directly" >&2
exit 1
fi
ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)
ARTIFACTS="${OMARCHY_ACCEPTANCE_DIR:-/tmp/omarchy-acceptance}"
mkdir -p "$ARTIFACTS"
pass() {
printf 'ok - %s\n' "$1"
}
fail() {
local description="$1"
local detail="${2:-}"
local step=${description,,}
step=${step// /-}
step=${step//[^a-z0-9-]/}
[[ -n $detail ]] && printf '%s\n' "$detail" >&2
screenshot "failure-$step"
printf 'not ok - %s\n' "$description" >&2
exit 1
}
screenshot() {
timeout 10 grim "$ARTIFACTS/$1.png" 2>/dev/null || true
}
screen_contains() {
local text="$1"
local snapshot="/tmp/omarchy-acceptance-ocr-$$.png"
# Capture at 2x scale: tesseract routinely drops small caption text at
# native resolution (the weather panel's detail labels, for one).
if ! timeout 10 grim -s 2 "$snapshot" 2>/dev/null; then
rm -f "$snapshot"
return 1
fi
tesseract "$snapshot" stdout --psm 11 2>/dev/null | grep -Fi -- "$text" >/dev/null
local status=$?
rm -f "$snapshot"
return $status
}
# Poll a command until it succeeds; screenshot and fail on timeout.
wait_until() {
local description="$1" timeout="$2"
shift 2
local deadline=$((SECONDS + timeout))
until "$@" >/dev/null 2>&1; do
if ((SECONDS >= deadline)); then
fail "$description" "timed out after ${timeout}s waiting for: $*"
fi
sleep 1
done
pass "$description"
}
window_present() {
hyprctl -j clients | jq -e --arg class "$1" '[.[] | select(.class | test($class))] | length > 0'
}
window_absent() {
! window_present "$1"
}
layer_present() {
hyprctl -j layers | jq -e --arg ns "$1" '[.. | objects | select(.namespace? == $ns)] | length > 0'
}
layer_absent() {
! layer_present "$1"
}
# The image selector stays mapped between opens, parked on the bottom layer, so
# it keeps its textures and fractional scale. Showing means on the overlay.
layer_on_overlay() {
hyprctl -j layers | jq -e --arg ns "$1" '[.[].levels["3"][]? | select(.namespace == $ns)] | length > 0'
}
layer_off_overlay() {
! layer_on_overlay "$1"
}
# A layer can be mapped but parked off the monitor: the bar hides that way so
# revealing it does not have to rebuild the surface. Assert on geometry when
# what matters is that the user can actually see it. Layer boxes are local to
# their monitor and in logical coordinates, so compare them with local bounds
# derived from the monitor's scaled pixel size.
layer_on_screen() {
local monitors
monitors=$(hyprctl -j monitors) || return 1
hyprctl -j layers | jq -e --arg ns "$1" --argjson monitors "$monitors" '
to_entries[]
| .key as $name
| .value as $levels
| ($monitors[] | select(.name == $name)) as $m
| (if ($m.transform // 0) % 2 == 1 then $m.height else $m.width end) / $m.scale | round as $width
| (if ($m.transform // 0) % 2 == 1 then $m.width else $m.height end) / $m.scale | round as $height
| [$levels | .. | objects | select(.namespace? == $ns)][]
| select(
.x + .w > 0 and .x < $width and
.y + .h > 0 and .y < $height
)
' >/dev/null
}
layer_off_screen() {
! layer_on_screen "$1"
}
# Close every window matching a class regex, by address so multi-window apps
# are fully closed. Tries the quattro Lua dispatcher first, then classic.
close_windows() {
local class="$1"
local addr
while read -r addr; do
hyprctl dispatch "hl.dsp.window.close({ window = \"address:$addr\" })" >/dev/null 2>&1 ||
hyprctl dispatch closewindow "address:$addr" >/dev/null 2>&1 || true
done < <(hyprctl -j clients | jq -r --arg class "$class" '.[] | select(.class | test($class)) | .address')
}
launch_app() {
setsid -f bash -c "$1" >/dev/null 2>&1
}