#!/usr/bin/env bash # The first thing a new machine shows. # # Panama has thirty settings pages, which is the opposite of the usual problem: # somebody arriving from another desktop cannot tell which few things matter. # This is those few things, once. # # What must hold: # # 1. The chords come from the live keymap. A welcome screen that teaches the # wrong keys is worse than none, and it is the one surface read by # somebody with no way to tell it is wrong. # 2. It is shown once and dismissing marks it. A desktop that reintroduces # itself every login has failed to take no for an answer. # 3. Dismissing by ANY route counts, including Escape. # 4. It stays reachable afterwards. The moment somebody wants it again is # exactly when a one-shot has thrown it away. # 5. It does NOT close on a click outside, unlike every other surface here. # A stray click in the first thirty seconds would throw away the only # explanation on offer. # # The live half opens the real surface. It deliberately does not exercise the # dismiss path, because that would write to the running machine's settings and # there is no honest way to put it back. set -uo pipefail repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" module="$repo_dir/config/dot/quickshell/modules/welcome" welcome="$module/Welcome.qml" shell_qml="$repo_dir/config/dot/quickshell/shell.qml" state="$repo_dir/config/dot/quickshell/services/ShellState.qml" schema="$repo_dir/config/dot/quickshell/config/PreferenceSchema.qml" command_file="$repo_dir/config/local/share/vicinae/scripts/show-welcome" findings=() note() { findings+=("$1"); } [[ -r "$welcome" ]] || { printf 'welcome contract: %s is missing\n' "$welcome" >&2; exit 1; } # ── The module ─────────────────────────────────────────────────────────────── grep -q '^Welcome 1.0 Welcome.qml$' "$module/qmldir" \ || note 'Welcome is not registered in its qmldir, so the shell would fail to load entirely' # ── 1. The keys it teaches are the keys you have ───────────────────────────── grep -q 'Keybinds.binds' "$welcome" \ || note 'the welcome screen does not read the live keymap, so it could teach chords this machine does not have' grep -q 'function chordFor' "$welcome" \ || note 'there is no lookup from a bind description to its current chord' # A fallback is correct -- the keymap may not have loaded yet -- but it must be # a fallback rather than the source. grep -q 'fallback' "$welcome" \ || note 'no fallback chord, so the screen would be blank until the keymap loads' # ── 2 & 3. Once, and dismissing counts ─────────────────────────────────────── grep -q 'key: "welcomeSeen"' "$schema" \ || note 'there is no welcomeSeen preference, so the welcome cannot be shown once' grep -A2 'key: "welcomeSeen"' "$schema" | grep -q 'internal: true' \ || note 'welcomeSeen is not internal, so it would appear in settings search as something to toggle' grep -q 'DesktopPreferences.set("welcomeSeen", true)' "$welcome" \ || note 'dismissing does not mark the welcome as seen, so it would return every login' grep -q 'Keys.onEscapePressed: root.dismiss()' "$welcome" \ || note 'Escape does not go through dismiss(), so closing that way would show it again next login' # ── 4. Still reachable ─────────────────────────────────────────────────────── grep -q 'target: "welcome"' "$shell_qml" \ || note 'there is no welcome IPC target, so it cannot be reopened' [[ -x "$command_file" ]] || note 'there is no launcher command to show the welcome again' # ── 5. A stray click must not dismiss it ───────────────────────────────────── # # Every other overlay closes on an outside click. This one must not, and the # check is that the scrim's MouseArea swallows clicks without acting. python3 - "$welcome" <<'PY' || note 'the welcome scrim closes on an outside click, so a stray click would throw away the only explanation on offer' import re, sys text = open(sys.argv[1], encoding="utf-8").read() # The scrim is the MouseArea filling the dimming Rectangle. Anything that # reacts to a click there dismisses the surface. scrim = re.search(r"Rectangle\s*\{[^{}]*anchors\.fill:\s*parent.*?MouseArea\s*\{(.*?)\}", text, re.S) if scrim and re.search(r"onClicked|onPressed|onTapped", scrim.group(1)): raise SystemExit(1) PY # ── Wiring ─────────────────────────────────────────────────────────────────── grep -q 'import qs.modules.welcome' "$shell_qml" \ || note 'shell.qml does not import the welcome module' grep -qE '^\s*Welcome \{\}' "$shell_qml" \ || note 'shell.qml never instantiates the welcome screen' grep -q 'welcomeOpen: activeOverlay === "welcome"' "$state" \ || note 'ShellState does not track the welcome screen' # ── Live ───────────────────────────────────────────────────────────────────── if ! command -v qs >/dev/null 2>&1 || ! qs ipc call welcome status >/dev/null 2>&1; then if (( ${#findings[@]} > 0 )); then printf 'welcome contract: %d finding(s)\n' "${#findings[@]}" >&2 printf ' - %s\n' "${findings[@]}" >&2 exit 1 fi printf 'welcome contract: PASS (static; no running shell)\n' exit 0 fi was_open="$(qs ipc call welcome status | jq -r .open)" qs ipc call welcome open >/dev/null sleep 1 [[ "$(qs ipc call welcome status | jq -r .open)" == "true" ]] \ || note 'the welcome screen did not open' hyprctl layers -j | grep -q 'qs-popover-welcome' \ || note 'the welcome screen reports open but its layer never mapped' qs ipc call welcome close >/dev/null sleep 1 [[ "$(qs ipc call welcome status | jq -r .open)" == "false" ]] \ || note 'the welcome screen did not close' [[ "$was_open" == "true" ]] && qs ipc call welcome open >/dev/null if (( ${#findings[@]} > 0 )); then printf 'welcome contract: %d finding(s)\n' "${#findings[@]}" >&2 printf ' - %s\n' "${findings[@]}" >&2 exit 1 fi printf 'welcome contract: PASS\n'