Arrange the desktop, run "Save Layout as Project" from the launcher, name it. "Open Project" lays it out again on free workspaces, so it never lands on top of what you are already doing. Saved layouts are listed on the Desktop settings page, which is where they are removed. Recorded rather than written by hand, and a terminal's directory is most of why it is worth having: without it a project opens three terminals in your home folder and you change directory three times. This machine had two terminals in the same project when it was written, and reopening there is the difference between a layout and a working desktop. Four things had to be found by running it, none of which reading would have shown. A terminal's directory is not the terminal's working directory -- that is where it was launched from. The shell inside it is what has been cd'd. Reading the wrong one looked correct for exactly as long as the terminals under test had been started from the right place, which they had. gtk-launch cannot place a window. It activates over D-Bus, so the process Hyprland started exits and a [workspace N silent] rule has nothing left to apply to; Nautilus landed on whatever workspace was in front of you. The Exec line from the desktop entry is run directly instead. But DBusActivatable applications do the same thing regardless, so the window is found afterwards and moved by address -- which also means a window that never appeared is reported rather than assumed. /proc/PID/task/PID/children is a file of pids, not a directory. Listing it as one always raised, so the fast path was never once taken and everything went through pgrep. And kitty's --directory needs an equals sign or the short -d; the space-separated long form is accepted and silently ignored. The contract exercises a save and open round trip against a stubbed compositor, and reads a terminal's directory out of a real process tree rather than grepping the source for a shell name -- an earlier version passed against a helper that had been changed back, because the constant was still there. Claude-Session: https://claude.ai/code/session_01Q84axqUE5inJhf5Jz9CFy1
146 lines
6.3 KiB
Bash
Executable File
146 lines
6.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
commands_dir="$repo_dir/config/local/share/vicinae/scripts"
|
|
work="$(mktemp -d /tmp/panama-commands.XXXXXX)"
|
|
dispatch_log="$work/dispatch.log"
|
|
|
|
fail() {
|
|
printf 'Panama commands contract: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
cleanup() {
|
|
rm -rf "$work"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
declare -A expected=(
|
|
[open-control-center]=control-center
|
|
[open-notifications]=notifications
|
|
[open-calendar]=calendar
|
|
[open-clipboard]=clipboard
|
|
[open-mission-control]=overview
|
|
[open-settings]=settings
|
|
[check-system-health]=health
|
|
[toggle-dnd]=dnd
|
|
[toggle-caffeine]=caffeine
|
|
[toggle-night-light]=night-light
|
|
[start-focus]=focus-start
|
|
[end-focus]=focus-end
|
|
[capture]=capture
|
|
[screen-intelligence]=intelligence
|
|
[quick-screenshot]=screenshot
|
|
[toggle-microphone]=microphone
|
|
[open-prism-gallery]=gallery
|
|
[restart-shell]=restart-shell
|
|
)
|
|
|
|
# The per-page settings commands are generated from the settings page list, so
|
|
# they are enumerated from that list rather than restated here -- a hand-written
|
|
# copy would have to be edited every time a page is added, which is exactly the
|
|
# kind of second list this generator exists to avoid.
|
|
sidebar="$repo_dir/config/dot/quickshell/modules/settings/SettingsSidebar.qml"
|
|
while read -r page; do
|
|
[[ -n "$page" ]] || continue
|
|
[[ "$page" == "home" ]] && continue
|
|
expected[settings-$page]="settings-page $page"
|
|
done < <(grep -oE '\{ page: "[a-z-]+"' "$sidebar" | sed 's/.*"\([a-z-]*\)"/\1/')
|
|
|
|
(( ${#expected[@]} > 18 )) || fail 'no generated per-page commands were found; run scripts/panama-settings-commands'
|
|
|
|
# Commands that do not go through panama-action, and should not.
|
|
#
|
|
# Every command above asks the shell to do something, so routing them through
|
|
# one dispatcher is what keeps that surface small. search-web is a different
|
|
# animal: it takes a query and opens a browser, and neither half needs the
|
|
# shell. Sending it through panama-action would mean a web search stops working
|
|
# when Quickshell is down -- which is exactly when somebody is reaching for the
|
|
# launcher to look up what went wrong.
|
|
#
|
|
# The project commands are the same shape: they record or restore a window
|
|
# layout, which is panama-project's job and involves the shell not at all.
|
|
#
|
|
# They are still commands, so everything else below applies to them: a title,
|
|
# a description, search vocabulary, the Panama icon, and closing quietly.
|
|
declare -a standalone=(search-web save-project open-project)
|
|
|
|
# Generated commands must match their source. A stale command dispatches to a
|
|
# page that has been renamed or removed, and the launcher reports nothing wrong.
|
|
"$repo_dir/config/dot/quickshell/scripts/panama-settings-commands" --check >/dev/null \
|
|
|| fail 'the generated per-page commands are stale; run scripts/panama-settings-commands'
|
|
|
|
mkdir -p "$work/home/.config/quickshell/scripts"
|
|
cat >"$work/home/.config/quickshell/scripts/panama-action" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
printf '%s\n' "$*" >>"$PANAMA_COMMAND_TEST_LOG"
|
|
EOF
|
|
chmod +x "$work/home/.config/quickshell/scripts/panama-action"
|
|
|
|
[[ -d "$commands_dir" ]] || fail 'Vicinae command directory is missing'
|
|
|
|
# Every file in the directory, not a glob on an extension. These scripts carry
|
|
# none: a shebang and the executable bit already select the interpreter, and an
|
|
# extension is one more thing that has to stay in sync -- which it did not.
|
|
mapfile -t actual_files < <(find "$commands_dir" -maxdepth 1 -type f -printf '%f\n' | sort)
|
|
declared=$(( ${#expected[@]} + ${#standalone[@]} ))
|
|
[[ ${#actual_files[@]} -eq $declared ]] \
|
|
|| fail "expected $declared commands, found ${#actual_files[@]}"
|
|
|
|
declare -A seen_titles=()
|
|
for script_name in "${!expected[@]}" "${standalone[@]}"; do
|
|
script="$commands_dir/$script_name"
|
|
[[ -x "$script" ]] || fail "$script_name is missing or not executable"
|
|
|
|
vicinae script check "$script" >/dev/null \
|
|
|| fail "$script_name is rejected by Vicinae"
|
|
|
|
title="$(sed -n 's/^# @vicinae.title //p' "$script")"
|
|
# No product prefix: this is the desktop's own settings, not a third-party
|
|
# add-on announcing itself in someone else's launcher. Uniqueness still
|
|
# matters, because two commands with one title are indistinguishable there.
|
|
[[ -n $title ]] || fail "$script_name has no title"
|
|
[[ $title != Panama* ]] || fail "$script_name still carries the product prefix: $title"
|
|
[[ -z ${seen_titles[$title]+x} ]] || fail "duplicate launcher title: $title"
|
|
seen_titles[$title]=1
|
|
|
|
grep -Fxq '# @vicinae.mode silent' "$script" \
|
|
|| fail "$script_name does not close quietly after success"
|
|
grep -Fq '# @vicinae.description ' "$script" \
|
|
|| fail "$script_name has no searchable description"
|
|
grep -Fq '# @vicinae.keywords ' "$script" \
|
|
|| fail "$script_name has no search vocabulary"
|
|
grep -Fxq '# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg' "$script" \
|
|
|| fail "$script_name does not use the Panama application identity"
|
|
|
|
if [[ $script_name == check-system-health ]]; then
|
|
[[ $title == 'Check System Health' ]] \
|
|
|| fail "health command has the wrong title: $title"
|
|
grep -Fxq '# @vicinae.schemaVersion 1' "$script" \
|
|
|| fail 'health command does not use schema version 1'
|
|
grep -Fxq '# @vicinae.keywords ["health", "doctor", "repair", "services"]' "$script" \
|
|
|| fail 'health command has the wrong search vocabulary'
|
|
grep -Fxq 'exec "$HOME/.config/quickshell/scripts/panama-action" health' "$script" \
|
|
|| fail 'health command bypasses the stable dispatcher path'
|
|
fi
|
|
|
|
# A standalone command has nothing to dispatch, and running it would open a
|
|
# browser at whoever is running the tests.
|
|
if [[ -z ${expected[$script_name]+x} ]]; then
|
|
if grep -Fq 'panama-action' "$script"; then
|
|
fail "$script_name is listed as standalone but goes through panama-action"
|
|
fi
|
|
continue
|
|
fi
|
|
|
|
: >"$dispatch_log"
|
|
HOME="$work/home" PANAMA_COMMAND_TEST_LOG="$dispatch_log" "$script"
|
|
dispatched="$(cat "$dispatch_log")"
|
|
[[ $dispatched == "${expected[$script_name]}" ]] \
|
|
|| fail "$script_name dispatched [$dispatched], expected [${expected[$script_name]}]"
|
|
done
|
|
|
|
printf 'Panama commands contract: PASS (%d commands)\n' "$declared"
|