From b28edcd01fb301273b9548722a55af1881aff455 Mon Sep 17 00:00:00 2001 From: Gabriel Brown Date: Tue, 18 Aug 2026 07:10:32 -0400 Subject: [PATCH] Add Panama commands to the launcher --- config/dot/quickshell/scripts/panama-action | 95 +++++++++++ config/dot/quickshell/scripts/panama-osd | 12 +- config/dot/quickshell/shell.qml | 21 +++ config/local/share/vicinae/scripts/capture.sh | 9 ++ .../local/share/vicinae/scripts/end-focus.sh | 9 ++ .../share/vicinae/scripts/open-calendar.sh | 9 ++ .../share/vicinae/scripts/open-clipboard.sh | 9 ++ .../vicinae/scripts/open-control-center.sh | 9 ++ .../vicinae/scripts/open-mission-control.sh | 9 ++ .../vicinae/scripts/open-notifications.sh | 9 ++ .../vicinae/scripts/open-prism-gallery.sh | 9 ++ .../share/vicinae/scripts/open-settings.sh | 9 ++ .../share/vicinae/scripts/quick-screenshot.sh | 9 ++ .../share/vicinae/scripts/restart-shell.sh | 9 ++ .../vicinae/scripts/screen-intelligence.sh | 9 ++ .../share/vicinae/scripts/start-focus.sh | 9 ++ .../share/vicinae/scripts/toggle-caffeine.sh | 9 ++ .../local/share/vicinae/scripts/toggle-dnd.sh | 9 ++ .../vicinae/scripts/toggle-microphone.sh | 9 ++ .../vicinae/scripts/toggle-night-light.sh | 9 ++ docs/UPSTREAM-INSPIRATION.md | 2 +- setup/scripts/link-dotfiles | 5 + setup/scripts/link-vicinae-scripts | 40 +++++ tests/quickshell/osd-helper-contract.sh | 4 + tests/quickshell/panama-action-contract.sh | 152 ++++++++++++++++++ .../quickshell/panama-action-ipc-contract.sh | 56 +++++++ .../panama-command-install-contract.sh | 61 +++++++ tests/quickshell/panama-commands-contract.sh | 82 ++++++++++ 28 files changed, 681 insertions(+), 2 deletions(-) create mode 100755 config/dot/quickshell/scripts/panama-action create mode 100755 config/local/share/vicinae/scripts/capture.sh create mode 100755 config/local/share/vicinae/scripts/end-focus.sh create mode 100755 config/local/share/vicinae/scripts/open-calendar.sh create mode 100755 config/local/share/vicinae/scripts/open-clipboard.sh create mode 100755 config/local/share/vicinae/scripts/open-control-center.sh create mode 100755 config/local/share/vicinae/scripts/open-mission-control.sh create mode 100755 config/local/share/vicinae/scripts/open-notifications.sh create mode 100755 config/local/share/vicinae/scripts/open-prism-gallery.sh create mode 100755 config/local/share/vicinae/scripts/open-settings.sh create mode 100755 config/local/share/vicinae/scripts/quick-screenshot.sh create mode 100755 config/local/share/vicinae/scripts/restart-shell.sh create mode 100755 config/local/share/vicinae/scripts/screen-intelligence.sh create mode 100755 config/local/share/vicinae/scripts/start-focus.sh create mode 100755 config/local/share/vicinae/scripts/toggle-caffeine.sh create mode 100755 config/local/share/vicinae/scripts/toggle-dnd.sh create mode 100755 config/local/share/vicinae/scripts/toggle-microphone.sh create mode 100755 config/local/share/vicinae/scripts/toggle-night-light.sh create mode 100755 setup/scripts/link-vicinae-scripts create mode 100755 tests/quickshell/panama-action-contract.sh create mode 100755 tests/quickshell/panama-action-ipc-contract.sh create mode 100755 tests/quickshell/panama-command-install-contract.sh create mode 100755 tests/quickshell/panama-commands-contract.sh diff --git a/config/dot/quickshell/scripts/panama-action b/config/dot/quickshell/scripts/panama-action new file mode 100755 index 0000000..7038338 --- /dev/null +++ b/config/dot/quickshell/scripts/panama-action @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +# One stable command boundary for launcher actions. Vicinae scripts stay tiny, +# while the actual mapping to Quickshell IPC remains testable in one place. + +set -euo pipefail + +script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +action="${1:-}" + +if command -v panama-osd >/dev/null 2>&1; then + osd_command=(panama-osd) +else + osd_command=("$script_dir/panama-osd") +fi + +if command -v panama-prism-gallery >/dev/null 2>&1; then + gallery_command=(panama-prism-gallery) +else + gallery_command=("$script_dir/panama-prism-gallery") +fi + +report_failure() { + local status=$? + trap - EXIT + if (( status != 0 )) && command -v notify-send >/dev/null 2>&1; then + notify-send -a Panama -i dialog-error-symbolic \ + "Panama action failed" \ + "The “${action//-/ }” action could not be completed." || true + fi + exit "$status" +} +trap report_failure EXIT + +show_state() { + local state="$1" on_icon="$2" off_icon="$3" label="$4" + case "$state" in + true) "${osd_command[@]}" message "$on_icon" "$label On" ;; + false) "${osd_command[@]}" message "$off_icon" "$label Off" ;; + *) + printf 'Panama action returned an invalid state: %s\n' "$state" >&2 + return 1 + ;; + esac +} + +toggle_state() { + local target="$1" on_icon="$2" off_icon="$3" label="$4" state + state="$(qs ipc call "$target" toggle)" + show_state "$state" "$on_icon" "$off_icon" "$label" +} + +case "$action" in + control-center) qs ipc call quicksettings open ;; + notifications) qs ipc call notifications open ;; + calendar) qs ipc call calendar-agenda open ;; + clipboard) qs ipc call clipboard open ;; + overview) qs ipc call overview open ;; + settings) qs ipc call settings open ;; + + dnd) + state="$(qs ipc call notifications dnd)" + show_state "$state" notifications-disabled-symbolic notifications-symbolic "Do Not Disturb" + ;; + caffeine) + toggle_state caffeine weather-clear-symbolic weather-clear-night-symbolic Caffeine + ;; + night-light) + toggle_state night-light night-light-symbolic night-light-disabled-symbolic "Night Light" + ;; + focus-start) + qs ipc call focus start + "${osd_command[@]}" message preferences-system-time-symbolic "Focus Session Started" + ;; + focus-end) + qs ipc call focus end + "${osd_command[@]}" message media-playback-stop-symbolic "Focus Session Ended" + ;; + + capture) qs ipc call capture open ;; + intelligence) qs ipc call screen-intelligence open ;; + screenshot) qs ipc call capture screenNow ;; + microphone) "${osd_command[@]}" microphone toggle ;; + + gallery) "${gallery_command[@]}" ;; + restart-shell) + qs kill + quickshell --daemonize + ;; + *) + printf 'Usage: panama-action {%s}\n' \ + 'control-center|notifications|calendar|clipboard|overview|settings|dnd|caffeine|night-light|focus-start|focus-end|capture|intelligence|screenshot|microphone|gallery|restart-shell' >&2 + exit 2 + ;; +esac diff --git a/config/dot/quickshell/scripts/panama-osd b/config/dot/quickshell/scripts/panama-osd index 675ed74..3960fdc 100755 --- a/config/dot/quickshell/scripts/panama-osd +++ b/config/dot/quickshell/scripts/panama-osd @@ -100,8 +100,18 @@ case "${1:-}" in microphone) shift; adjust_microphone "$@" ;; brightness) shift; adjust_brightness "$@" ;; media) shift; media_action "$@" ;; + message) + shift + kind="${1:-}" + [[ -n $kind && $# -ge 2 ]] || { + printf 'Usage: panama-osd message ICON LABEL\n' >&2 + exit 2 + } + shift + show_message "$kind" "$*" + ;; *) - printf 'Usage: panama-osd volume|microphone|brightness|media ACTION [step]\n' >&2 + printf 'Usage: panama-osd volume|microphone|brightness|media|message ACTION [value]\n' >&2 exit 2 ;; esac diff --git a/config/dot/quickshell/shell.qml b/config/dot/quickshell/shell.qml index fb78132..6af9630 100644 --- a/config/dot/quickshell/shell.qml +++ b/config/dot/quickshell/shell.qml @@ -257,6 +257,27 @@ ShellRoot { } } + // Small stateful controls used by launcher commands. Returning the state + // after mutation lets callers give accurate OSD feedback without keeping a + // second copy of service state in a shell script. + IpcHandler { + target: "caffeine" + function toggle(): bool { + Caffeine.toggle(); + return Caffeine.enabled; + } + function status(): bool { return Caffeine.enabled; } + } + + IpcHandler { + target: "night-light" + function toggle(): bool { + NightLight.toggle(); + return NightLight.active; + } + function status(): bool { return NightLight.active; } + } + IpcHandler { target: "kdeconnect" function fixture(name: string): void { KdeConnect.applyFixture(name); } diff --git a/config/local/share/vicinae/scripts/capture.sh b/config/local/share/vicinae/scripts/capture.sh new file mode 100755 index 0000000..550a153 --- /dev/null +++ b/config/local/share/vicinae/scripts/capture.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Screenshot and Recording +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Open the polished screenshot and screen-recording picker. +# @vicinae.keywords ["capture", "record", "selection", "window", "screen"] + +exec "$HOME/.config/quickshell/scripts/panama-action" capture diff --git a/config/local/share/vicinae/scripts/end-focus.sh b/config/local/share/vicinae/scripts/end-focus.sh new file mode 100755 index 0000000..e460bc3 --- /dev/null +++ b/config/local/share/vicinae/scripts/end-focus.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: End Focus Session +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description End the current focus timer and restore its previous states. +# @vicinae.keywords ["stop timer", "deep work", "pomodoro", "restore"] + +exec "$HOME/.config/quickshell/scripts/panama-action" focus-end diff --git a/config/local/share/vicinae/scripts/open-calendar.sh b/config/local/share/vicinae/scripts/open-calendar.sh new file mode 100755 index 0000000..323f068 --- /dev/null +++ b/config/local/share/vicinae/scripts/open-calendar.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Open Calendar +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Open the calendar, synchronized agenda, and ongoing activity. +# @vicinae.keywords ["agenda", "events", "appointments", "date", "nextcloud"] + +exec "$HOME/.config/quickshell/scripts/panama-action" calendar diff --git a/config/local/share/vicinae/scripts/open-clipboard.sh b/config/local/share/vicinae/scripts/open-clipboard.sh new file mode 100755 index 0000000..8110aed --- /dev/null +++ b/config/local/share/vicinae/scripts/open-clipboard.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Open Clipboard +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Open Panama's compact clipboard history surface. +# @vicinae.keywords ["copy", "paste", "history", "text"] + +exec "$HOME/.config/quickshell/scripts/panama-action" clipboard diff --git a/config/local/share/vicinae/scripts/open-control-center.sh b/config/local/share/vicinae/scripts/open-control-center.sh new file mode 100755 index 0000000..f3e76b1 --- /dev/null +++ b/config/local/share/vicinae/scripts/open-control-center.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Open Control Center +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Open Wi-Fi, Bluetooth, audio, phone, and Home controls. +# @vicinae.keywords ["quick settings", "wifi", "bluetooth", "audio", "home", "phone"] + +exec "$HOME/.config/quickshell/scripts/panama-action" control-center diff --git a/config/local/share/vicinae/scripts/open-mission-control.sh b/config/local/share/vicinae/scripts/open-mission-control.sh new file mode 100755 index 0000000..c7ef48f --- /dev/null +++ b/config/local/share/vicinae/scripts/open-mission-control.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Open Mission Control +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description See every workspace, tiled window, and scratchpad window. +# @vicinae.keywords ["overview", "workspaces", "windows", "expose", "scratchpad"] + +exec "$HOME/.config/quickshell/scripts/panama-action" overview diff --git a/config/local/share/vicinae/scripts/open-notifications.sh b/config/local/share/vicinae/scripts/open-notifications.sh new file mode 100755 index 0000000..e0946a4 --- /dev/null +++ b/config/local/share/vicinae/scripts/open-notifications.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Open Notifications +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Open notification history and the unified date center. +# @vicinae.keywords ["alerts", "messages", "history", "date center"] + +exec "$HOME/.config/quickshell/scripts/panama-action" notifications diff --git a/config/local/share/vicinae/scripts/open-prism-gallery.sh b/config/local/share/vicinae/scripts/open-prism-gallery.sh new file mode 100755 index 0000000..1e4ccf3 --- /dev/null +++ b/config/local/share/vicinae/scripts/open-prism-gallery.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Open Prism Gallery +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Open the living gallery of Panama's production components. +# @vicinae.keywords ["design system", "theme", "components", "tokyo night", "style"] + +exec "$HOME/.config/quickshell/scripts/panama-action" gallery diff --git a/config/local/share/vicinae/scripts/open-settings.sh b/config/local/share/vicinae/scripts/open-settings.sh new file mode 100755 index 0000000..e7dd245 --- /dev/null +++ b/config/local/share/vicinae/scripts/open-settings.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Open Settings +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Configure the Panama desktop from its native Settings app. +# @vicinae.keywords ["preferences", "configure", "desktop", "system"] + +exec "$HOME/.config/quickshell/scripts/panama-action" settings diff --git a/config/local/share/vicinae/scripts/quick-screenshot.sh b/config/local/share/vicinae/scripts/quick-screenshot.sh new file mode 100755 index 0000000..0416a18 --- /dev/null +++ b/config/local/share/vicinae/scripts/quick-screenshot.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Capture Entire Screen +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Save an immediate screenshot of the complete desktop. +# @vicinae.keywords ["quick screenshot", "full screen", "capture", "grim"] + +exec "$HOME/.config/quickshell/scripts/panama-action" screenshot diff --git a/config/local/share/vicinae/scripts/restart-shell.sh b/config/local/share/vicinae/scripts/restart-shell.sh new file mode 100755 index 0000000..9407e98 --- /dev/null +++ b/config/local/share/vicinae/scripts/restart-shell.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Restart Desktop Shell +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Restart Quickshell if a desktop surface becomes unresponsive. +# @vicinae.keywords ["reload", "repair", "quickshell", "restart", "recover"] + +exec "$HOME/.config/quickshell/scripts/panama-action" restart-shell diff --git a/config/local/share/vicinae/scripts/screen-intelligence.sh b/config/local/share/vicinae/scripts/screen-intelligence.sh new file mode 100755 index 0000000..8ab6f5b --- /dev/null +++ b/config/local/share/vicinae/scripts/screen-intelligence.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Screen Intelligence +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Select part of the screen for OCR, QR, or code recognition. +# @vicinae.keywords ["ocr", "text", "qr", "barcode", "recognize", "scan"] + +exec "$HOME/.config/quickshell/scripts/panama-action" intelligence diff --git a/config/local/share/vicinae/scripts/start-focus.sh b/config/local/share/vicinae/scripts/start-focus.sh new file mode 100755 index 0000000..ef0ef22 --- /dev/null +++ b/config/local/share/vicinae/scripts/start-focus.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Start Focus Session +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Start the configured focus timer with DND and Caffeine. +# @vicinae.keywords ["timer", "deep work", "pomodoro", "concentrate", "workspace"] + +exec "$HOME/.config/quickshell/scripts/panama-action" focus-start diff --git a/config/local/share/vicinae/scripts/toggle-caffeine.sh b/config/local/share/vicinae/scripts/toggle-caffeine.sh new file mode 100755 index 0000000..18de65d --- /dev/null +++ b/config/local/share/vicinae/scripts/toggle-caffeine.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Toggle Caffeine +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Keep the display awake and block automatic sleep. +# @vicinae.keywords ["awake", "sleep", "inhibit", "presentation", "idle"] + +exec "$HOME/.config/quickshell/scripts/panama-action" caffeine diff --git a/config/local/share/vicinae/scripts/toggle-dnd.sh b/config/local/share/vicinae/scripts/toggle-dnd.sh new file mode 100755 index 0000000..f769e64 --- /dev/null +++ b/config/local/share/vicinae/scripts/toggle-dnd.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Toggle Do Not Disturb +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Pause or resume notification banners, with OSD confirmation. +# @vicinae.keywords ["dnd", "notifications", "quiet", "silence", "alerts"] + +exec "$HOME/.config/quickshell/scripts/panama-action" dnd diff --git a/config/local/share/vicinae/scripts/toggle-microphone.sh b/config/local/share/vicinae/scripts/toggle-microphone.sh new file mode 100755 index 0000000..429db6e --- /dev/null +++ b/config/local/share/vicinae/scripts/toggle-microphone.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Toggle Microphone Mute +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Mute or unmute the default microphone with OSD feedback. +# @vicinae.keywords ["mic", "audio input", "privacy", "mute", "unmute"] + +exec "$HOME/.config/quickshell/scripts/panama-action" microphone diff --git a/config/local/share/vicinae/scripts/toggle-night-light.sh b/config/local/share/vicinae/scripts/toggle-night-light.sh new file mode 100755 index 0000000..521fe60 --- /dev/null +++ b/config/local/share/vicinae/scripts/toggle-night-light.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# @vicinae.schemaVersion 1 +# @vicinae.title Panama: Toggle Night Light +# @vicinae.mode silent +# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg +# @vicinae.description Toggle the warm display color temperature. +# @vicinae.keywords ["warm", "temperature", "blue light", "display", "hyprsunset"] + +exec "$HOME/.config/quickshell/scripts/panama-action" night-light diff --git a/docs/UPSTREAM-INSPIRATION.md b/docs/UPSTREAM-INSPIRATION.md index 4c7161c..3117cfa 100644 --- a/docs/UPSTREAM-INSPIRATION.md +++ b/docs/UPSTREAM-INSPIRATION.md @@ -14,12 +14,12 @@ Panama is its own desktop experience, but it should learn from strong open-sourc - `shell/plugins/osd/Osd.qml` and `OsdModel.js`: inspired the separation between a pure presentation model and a single, click-through per-monitor OSD surface. Panama rebuilt the behavior around freedesktop symbolic icons, Tokyo Night Moon tokens, its Prism glass material, Hyprland's focused monitor, and the existing `wpctl`/`brightnessctl`/`playerctl` keybindings. - `test/shell.d/osd-test.sh` and Omarchy's broader shell contract suite: reinforced Panama's use of fast model and integration contracts alongside live compositor verification. - Omarchy's small shared visual primitives: reinforced the decision to maintain `prism-gallery.qml`, which renders Panama's production widgets and Theme tokens instead of duplicating mock-only controls. +- `shell/plugins/menu/Menu.qml`: inspired a searchable desktop-action inventory. Panama keeps Vicinae as its one launcher and exposes native script commands backed by a single `panama-action` dispatcher, rather than adding Omarchy's separate shell menu or plugin host. No Omarchy user-facing layout or styling was transplanted. Panama keeps its GNOME-derived information architecture, Prism material, Tokyo Night Moon palette, app model, and established interaction choices. ### Candidates still under evaluation -- A command palette for shell actions, with Vicinae remaining the application launcher. - A plugin boundary for optional bar widgets and services once Panama has enough third-party modules to justify one. - A small first-run health check for missing desktop dependencies and broken service integrations. diff --git a/setup/scripts/link-dotfiles b/setup/scripts/link-dotfiles index e3eb5a3..2d2c5ab 100755 --- a/setup/scripts/link-dotfiles +++ b/setup/scripts/link-dotfiles @@ -88,6 +88,11 @@ else log "Linked Tokyo Night Moon theme → $VICINAE_THEME" fi +# Install the Panama command collection into Vicinae's XDG data directory. +# This is intentionally separate from ~/.config/vicinae: script commands are +# user data in Vicinae 0.26, just like custom themes. +"$PANAMA_PATH/setup/scripts/link-vicinae-scripts" + # Panama-native applications live in the user data directory so launchers can # discover them alongside system desktop entries. Keep each authored file in # the repository and expose it with a narrow per-file symlink. diff --git a/setup/scripts/link-vicinae-scripts b/setup/scripts/link-vicinae-scripts new file mode 100755 index 0000000..3054ab2 --- /dev/null +++ b/setup/scripts/link-vicinae-scripts @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# Install Panama's searchable launcher actions as one narrow directory link. +# Keeping the runtime path stable lets Vicinae watch it while every command +# remains authored and reviewed in the Panama repository. + +set -euo pipefail + +panama_path="${PANAMA_PATH:-$HOME/.local/share/Panama}" +vicinae_data_dir="${VICINAE_DATA_DIR:-$HOME/.local/share/vicinae}" +source_dir="$panama_path/config/local/share/vicinae/scripts" +scripts_dir="$vicinae_data_dir/scripts" +target_dir="$scripts_dir/panama" +backup_dir="$scripts_dir/panama.pre-panama" + +[[ -d "$source_dir" ]] || { + printf 'Panama command source is missing: %s\n' "$source_dir" >&2 + exit 1 +} + +mkdir -p "$scripts_dir" + +if [[ -L "$target_dir" ]]; then + rm "$target_dir" +elif [[ -e "$target_dir" ]]; then + if [[ -e "$backup_dir" ]]; then + printf 'Refusing to replace %s; backup already exists at %s\n' \ + "$target_dir" "$backup_dir" >&2 + exit 1 + fi + mv "$target_dir" "$backup_dir" +fi + +ln -s "$source_dir" "$target_dir" + +# The server also rescans periodically, but an explicit reload makes a setup +# run deterministic. If Vicinae is not active yet, its startup scan is enough. +if command -v vicinae >/dev/null 2>&1 && vicinae ping >/dev/null 2>&1; then + vicinae cmd launch core:reload-scripts >/dev/null 2>&1 || true +fi diff --git a/tests/quickshell/osd-helper-contract.sh b/tests/quickshell/osd-helper-contract.sh index 7d1f329..3dca564 100755 --- a/tests/quickshell/osd-helper-contract.sh +++ b/tests/quickshell/osd-helper-contract.sh @@ -91,4 +91,8 @@ run_helper media next assert_line 'playerctl ' assert_line 'qs ' +: >"$log" +run_helper message notifications-disabled-symbolic 'Do Not Disturb On' +assert_line 'qs ' + printf 'osd helper contract: PASS\n' diff --git a/tests/quickshell/panama-action-contract.sh b/tests/quickshell/panama-action-contract.sh new file mode 100755 index 0000000..c671b75 --- /dev/null +++ b/tests/quickshell/panama-action-contract.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +dispatcher="$repo_dir/config/dot/quickshell/scripts/panama-action" +work="$(mktemp -d /tmp/panama-action.XXXXXX)" +fake_bin="$work/bin" +command_log="$work/commands.log" + +fail() { + printf 'Panama action contract: %s\n' "$1" >&2 + exit 1 +} + +cleanup() { + rm -rf "$work" +} +trap cleanup EXIT + +[[ -x "$dispatcher" ]] || fail 'dispatcher is missing or not executable' + +mkdir -p "$fake_bin" "$work/home/.config/quickshell/scripts" + +make_recorder() { + local command_name="$1" + cp /dev/stdin "$fake_bin/$command_name" + chmod +x "$fake_bin/$command_name" +} + +make_recorder qs <<'EOF' +#!/usr/bin/env bash +printf 'qs' >>"$PANAMA_ACTION_TEST_LOG" +printf ' <%s>' "$@" >>"$PANAMA_ACTION_TEST_LOG" +printf '\n' >>"$PANAMA_ACTION_TEST_LOG" +case "$*" in + 'ipc call notifications dnd'|'ipc call caffeine toggle'|'ipc call night-light toggle') + printf '%s\n' "${PANAMA_ACTION_TEST_STATE:-true}" + ;; +esac +[[ ${PANAMA_ACTION_TEST_FAIL_QS:-false} != true ]] +EOF + +make_recorder quickshell <<'EOF' +#!/usr/bin/env bash +printf 'quickshell' >>"$PANAMA_ACTION_TEST_LOG" +printf ' <%s>' "$@" >>"$PANAMA_ACTION_TEST_LOG" +printf '\n' >>"$PANAMA_ACTION_TEST_LOG" +EOF + +make_recorder panama-osd <<'EOF' +#!/usr/bin/env bash +printf 'panama-osd' >>"$PANAMA_ACTION_TEST_LOG" +printf ' <%s>' "$@" >>"$PANAMA_ACTION_TEST_LOG" +printf '\n' >>"$PANAMA_ACTION_TEST_LOG" +EOF + +make_recorder panama-prism-gallery <<'EOF' +#!/usr/bin/env bash +printf 'panama-prism-gallery' >>"$PANAMA_ACTION_TEST_LOG" +if (( $# > 0 )); then + printf ' <%s>' "$@" >>"$PANAMA_ACTION_TEST_LOG" +fi +printf '\n' >>"$PANAMA_ACTION_TEST_LOG" +EOF + +make_recorder notify-send <<'EOF' +#!/usr/bin/env bash +printf 'notify-send' >>"$PANAMA_ACTION_TEST_LOG" +printf ' <%s>' "$@" >>"$PANAMA_ACTION_TEST_LOG" +printf '\n' >>"$PANAMA_ACTION_TEST_LOG" +EOF + +run_action() { + : >"$command_log" + HOME="$work/home" PATH="$fake_bin:$PATH" \ + PANAMA_ACTION_TEST_LOG="$command_log" \ + "$dispatcher" "$1" +} + +assert_commands() { + local expected="$1" + local actual + actual="$(cat "$command_log")" + [[ "$actual" == "$expected" ]] \ + || fail "wrong command sequence; expected [$expected], got [$actual]" +} + +run_action control-center +assert_commands 'qs ' + +run_action notifications +assert_commands 'qs ' + +run_action calendar +assert_commands 'qs ' + +run_action clipboard +assert_commands 'qs ' + +run_action overview +assert_commands 'qs ' + +run_action settings +assert_commands 'qs ' + +run_action dnd +assert_commands $'qs \npanama-osd ' + +PANAMA_ACTION_TEST_STATE=false run_action caffeine +assert_commands $'qs \npanama-osd ' + +run_action night-light +assert_commands $'qs \npanama-osd ' + +run_action focus-start +assert_commands $'qs \npanama-osd ' + +run_action focus-end +assert_commands $'qs \npanama-osd ' + +run_action capture +assert_commands 'qs ' + +run_action intelligence +assert_commands 'qs ' + +run_action screenshot +assert_commands 'qs ' + +run_action microphone +assert_commands 'panama-osd ' + +run_action gallery +assert_commands 'panama-prism-gallery' + +run_action restart-shell +assert_commands $'qs \nquickshell <--daemonize>' + +if HOME="$work/home" PATH="$fake_bin:$PATH" PANAMA_ACTION_TEST_LOG="$command_log" \ + "$dispatcher" definitely-not-an-action >/dev/null 2>&1; then + fail 'unknown action was accepted' +fi + +: >"$command_log" +if PANAMA_ACTION_TEST_FAIL_QS=true HOME="$work/home" PATH="$fake_bin:$PATH" \ + PANAMA_ACTION_TEST_LOG="$command_log" "$dispatcher" control-center >/dev/null 2>&1; then + fail 'failed shell action returned success' +fi +assert_commands $'qs \nnotify-send <-a> <-i> ' + +printf 'Panama action contract: PASS\n' diff --git a/tests/quickshell/panama-action-ipc-contract.sh b/tests/quickshell/panama-action-ipc-contract.sh new file mode 100755 index 0000000..8cf8d53 --- /dev/null +++ b/tests/quickshell/panama-action-ipc-contract.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +set -euo pipefail + +fail() { + printf 'Panama action IPC contract: %s\n' "$1" >&2 + exit 1 +} + +active_target="" +original_state="" + +restore_target() { + local target="$1" expected="$2" current + current="$(qs ipc call "$target" status 2>/dev/null || true)" + if [[ $current != "$expected" && ( $expected == true || $expected == false ) ]]; then + qs ipc call "$target" toggle >/dev/null 2>&1 || true + fi +} + +cleanup() { + if [[ -n $active_target ]]; then + restore_target "$active_target" "$original_state" + fi +} +trap cleanup EXIT + +exercise_toggle() { + local target="$1" before after restored + before="$(qs ipc call "$target" status)" + active_target="$target" + original_state="$before" + + [[ $before == true || $before == false ]] \ + || fail "$target returned a non-boolean status: $before" + + after="$(qs ipc call "$target" toggle)" + [[ $after != "$before" ]] \ + || fail "$target did not change state: before=$before after=$after" + + restored="$(qs ipc call "$target" toggle)" + [[ $restored == "$before" ]] \ + || fail "$target did not restore its original state: before=$before restored=$restored" + + active_target="" + original_state="" +} + +ipc="$(qs ipc show)" +rg -q '^target caffeine$' <<<"$ipc" || fail 'caffeine IPC target is missing' +rg -q '^target night-light$' <<<"$ipc" || fail 'night-light IPC target is missing' + +exercise_toggle caffeine +exercise_toggle night-light + +printf 'Panama action IPC contract: PASS\n' diff --git a/tests/quickshell/panama-command-install-contract.sh b/tests/quickshell/panama-command-install-contract.sh new file mode 100755 index 0000000..f81b00c --- /dev/null +++ b/tests/quickshell/panama-command-install-contract.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +installer="$repo_dir/setup/scripts/link-vicinae-scripts" +work="$(mktemp -d /tmp/panama-command-install.XXXXXX)" +data_dir="$work/share/vicinae" +fake_bin="$work/bin" +vicinae_log="$work/vicinae.log" + +fail() { + printf 'Panama command install contract: %s\n' "$1" >&2 + exit 1 +} + +cleanup() { + rm -rf "$work" +} +trap cleanup EXIT + +[[ -x "$installer" ]] || fail 'Vicinae script installer is missing or not executable' + +mkdir -p "$data_dir/scripts/panama" "$fake_bin" +printf 'user-owned\n' >"$data_dir/scripts/panama/keep.sh" + +cat >"$fake_bin/vicinae" <<'EOF' +#!/usr/bin/env bash +printf 'vicinae' >>"$PANAMA_VICINAE_TEST_LOG" +printf ' <%s>' "$@" >>"$PANAMA_VICINAE_TEST_LOG" +printf '\n' >>"$PANAMA_VICINAE_TEST_LOG" +[[ ${1:-} == ping || ${1:-} == cmd ]] +EOF +chmod +x "$fake_bin/vicinae" + +PATH="$fake_bin:$PATH" PANAMA_PATH="$repo_dir" VICINAE_DATA_DIR="$data_dir" \ + PANAMA_VICINAE_TEST_LOG="$vicinae_log" "$installer" >/dev/null + +target="$data_dir/scripts/panama" +backup="$data_dir/scripts/panama.pre-panama" +[[ -L "$target" ]] || fail 'Panama command directory was not linked' +[[ "$(readlink -f "$target")" == "$(readlink -f "$repo_dir/config/local/share/vicinae/scripts")" ]] \ + || fail 'runtime command link points outside the tracked source' +[[ -f "$backup/keep.sh" ]] || fail 'pre-existing user command directory was not preserved' +[[ "$(cat "$backup/keep.sh")" == user-owned ]] || fail 'preserved user command changed' + +expected_calls=$'vicinae \nvicinae ' +[[ "$(cat "$vicinae_log")" == "$expected_calls" ]] \ + || fail "Vicinae reload sequence was wrong: $(cat "$vicinae_log")" + +# A second setup pass must update the same narrow link without creating more +# backups or disturbing the preserved directory. +: >"$vicinae_log" +PATH="$fake_bin:$PATH" PANAMA_PATH="$repo_dir" VICINAE_DATA_DIR="$data_dir" \ + PANAMA_VICINAE_TEST_LOG="$vicinae_log" "$installer" >/dev/null +[[ -L "$target" ]] || fail 'second install did not leave the command link intact' +[[ -f "$backup/keep.sh" ]] || fail 'second install disturbed the original backup' +[[ ! -e "$data_dir/scripts/panama.pre-panama.pre-panama" ]] \ + || fail 'second install created a duplicate backup' + +printf 'Panama command install contract: PASS\n' diff --git a/tests/quickshell/panama-commands-contract.sh b/tests/quickshell/panama-commands-contract.sh new file mode 100755 index 0000000..bc02e50 --- /dev/null +++ b/tests/quickshell/panama-commands-contract.sh @@ -0,0 +1,82 @@ +#!/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.sh]=control-center + [open-notifications.sh]=notifications + [open-calendar.sh]=calendar + [open-clipboard.sh]=clipboard + [open-mission-control.sh]=overview + [open-settings.sh]=settings + [toggle-dnd.sh]=dnd + [toggle-caffeine.sh]=caffeine + [toggle-night-light.sh]=night-light + [start-focus.sh]=focus-start + [end-focus.sh]=focus-end + [capture.sh]=capture + [screen-intelligence.sh]=intelligence + [quick-screenshot.sh]=screenshot + [toggle-microphone.sh]=microphone + [open-prism-gallery.sh]=gallery + [restart-shell.sh]=restart-shell +) + +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' + +mapfile -t actual_files < <(find "$commands_dir" -maxdepth 1 -type f -name '*.sh' -printf '%f\n' | sort) +[[ ${#actual_files[@]} -eq ${#expected[@]} ]] \ + || fail "expected ${#expected[@]} commands, found ${#actual_files[@]}" + +declare -A seen_titles=() +for script_name in "${!expected[@]}"; 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")" + [[ $title == 'Panama: '* ]] || fail "$script_name has an ungrouped title: $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" + + : >"$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' "${#expected[@]}"