Add selectable Voxtype and Superwhisper dictation (#14552)

* Add selectable dictation backends

* Move dictation migration after repository priority update

* Extract backend-specific dictation installers

* Keep dictation installer dispatcher executable

* Call dictation backend installers directly

* Drop legacy Superwhisper migration and runtime cleanup

* Separate dictation defaults from generic recording protocol

* Add backend-specific dictation removers

* Preserve existing Voxtype selection during upgrade

* Make dictation backend selection safe and retryable

* Show the dictation indicator only for selected Voxtype

* Allow switching away from stopped Superwhisper

* Remove the built-in bar dictation indicator

* Store default dictation selection under defaults

* Rename dictation backend selector to use

* Move dictation removers into their own menu category
This commit is contained in:
David Heinemeier Hansson authored and GitHub committed 2026-10-08 20:37:26 +02:00
1 parent 1554a522cc
commit c24889096e
34 files changed
+639 -141

No files matched your search

+1
View File
@@ -46,6 +46,7 @@ GROUP_DESCRIPTIONS[finalize]="Finalize user setup"
GROUP_DESCRIPTIONS[default]="Default application selection"
GROUP_DESCRIPTIONS[dev]="Omarchy development tools"
GROUP_DESCRIPTIONS[disk]="Disk performance helpers"
GROUP_DESCRIPTIONS[dictation]="Dictation recording"
GROUP_DESCRIPTIONS[display]="Display and text scaling"
GROUP_DESCRIPTIONS[dns]="DNS resolver configuration"
GROUP_DESCRIPTIONS[drive]="Drive selection and encryption"
+37
View File
@@ -0,0 +1,37 @@
#!/bin/bash
# omarchy:summary=Get or set the default dictation backend
# omarchy:args=[backend]
# omarchy:examples=omarchy default dictation | omarchy default dictation superwhisper
if (($# > 1)); then
echo "Usage: omarchy default dictation [backend]" >&2
exit 1
fi
if (($# == 0)); then
config="${XDG_CONFIG_HOME:-$HOME/.config}/omarchy/defaults/dictation"
if [[ ! -s $config ]]; then
echo "No dictation backend configured. Select one in Setup > Defaults > Dictation." >&2
exit 1
fi
read -r backend < "$config"
else
backend=$1
fi
if [[ ! $backend =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
echo "Invalid dictation backend: $backend" >&2
exit 1
fi
if (($# == 0)); then
echo "$backend"
else
installer="omarchy-install-dictation-$backend"
if omarchy-cmd-missing "$installer"; then
echo "No installer available for dictation backend: $backend" >&2
exit 1
fi
omarchy-launch-floating-terminal-with-presentation "$installer"
fi
+26
View File
@@ -0,0 +1,26 @@
#!/bin/bash
# omarchy:summary=Record dictation with the configured backend
# omarchy:args=<start|stop|toggle>
# omarchy:examples=omarchy dictation start | omarchy dictation toggle
if (($# != 1)) || [[ $1 != "start" && $1 != "stop" && $1 != "toggle" ]]; then
echo "Usage: omarchy dictation <start|stop|toggle>" >&2
exit 1
fi
config="${XDG_CONFIG_HOME:-$HOME/.config}/omarchy/defaults/dictation"
mkdir -p "${config%/*}"
exec 9> "$config.lock"
if ! flock -w 5 9; then
echo "Dictation is busy; try again." >&2
exit 1
fi
backend=$(omarchy-default-dictation) || exit 1
adapter="omarchy-dictation-$backend"
if omarchy-cmd-missing "$adapter"; then
echo "The selected dictation backend ($backend) is not installed." >&2
exit 1
fi
"$adapter" "$1" 9>&-
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
# omarchy:summary=Record dictation using Superwhisper
# omarchy:args=<start|stop|toggle>
case "${1:-}" in
start) superwhisper start ;;
stop)
if omarchy-cmd-missing superwhisper || ! systemctl --user is-active --quiet superwhisper.service; then
exit 0
else
superwhisper stop
fi
;;
toggle) superwhisper record ;;
*) echo "Usage: omarchy dictation superwhisper <start|stop|toggle>" >&2; exit 1 ;;
esac
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
# omarchy:hidden=true
# omarchy:summary=Save a configured dictation backend after stopping the previous backend
# omarchy:args=<backend>
set -e
if (($# != 1)) || [[ ! $1 =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
echo "Usage: omarchy-dictation-use <backend>" >&2
exit 1
fi
config="${XDG_CONFIG_HOME:-$HOME/.config}/omarchy/defaults/dictation"
mkdir -p "${config%/*}"
exec 9> "$config.lock"
if ! flock -w 5 9; then
echo "Dictation is busy; try again." >&2
exit 1
fi
previous=$(omarchy-default-dictation 2>/dev/null) || previous=""
if [[ -n $previous && $previous != "$1" ]] && omarchy-cmd-present "omarchy-dictation-$previous"; then
if ! "omarchy-dictation-$previous" stop 9>&-; then
echo "Could not stop $previous; keeping the existing dictation backend." >&2
exit 1
fi
fi
temporary=$(mktemp "$config.XXXXXX")
trap 'rm -f "$temporary"' EXIT
printf '%s\n' "$1" > "$temporary"
mv -f "$temporary" "$config"
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
# omarchy:summary=Record dictation using Voxtype
# omarchy:args=<start|stop|toggle>
case "${1:-}" in
start|toggle) voxtype record "$1" ;;
stop)
if omarchy-cmd-missing voxtype || [[ $(voxtype status) == "stopped" ]]; then
exit 0
else
voxtype record stop
fi
;;
*) echo "Usage: omarchy dictation voxtype <start|stop|toggle>" >&2; exit 1 ;;
esac
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
# omarchy:summary=Install and configure Superwhisper dictation
# omarchy:requires-sudo=true
set -e
omarchy-pkg-add superwhisper-bin
bash /usr/share/superwhisper/setup-user
# Superwhisper requires a native recording shortcut; keep it separate from
# Omarchy's push-to-talk keys.
superwhisper shortcuts set toggle "Alt+Space" >/dev/null
superwhisper shortcuts set hold none >/dev/null
omarchy-dictation-use superwhisper
hyprctl reload >/dev/null
omarchy-notification-send -g  "Superwhisper Dictation Ready" "Hold Right Alt or F9 to dictate (or toggle with Super + Ctrl + X)." -t 10000
superwhisper settings
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
# omarchy:summary=Install and configure Voxtype dictation
# omarchy:requires-sudo=true
set -e
if omarchy-cmd-missing voxtype; then
if gum confirm "Install Voxtype + AI model (~150MB) to enable dictation?"; then
omarchy-pkg-add wtype voxtype-bin
else
exit 0
fi
fi
config_home="${XDG_CONFIG_HOME:-$HOME/.config}"
mkdir -p "$config_home/voxtype"
if [[ ! -e $config_home/voxtype/config.toml ]]; then
cp "$OMARCHY_PATH/default/voxtype/config.toml" "$config_home/voxtype/"
if omarchy-hw-vulkan; then
voxtype setup gpu --enable || true
fi
fi
# Setup reuses the configured model and downloads it only when missing.
# Retry these steps even when an earlier attempt already installed the binary.
voxtype setup --download --no-post-install
voxtype setup systemd
systemctl --user enable --now voxtype.service
omarchy-dictation-use voxtype
hyprctl reload >/dev/null
omarchy-notification-send -g  "Voxtype Dictation Ready" "Hold Right Alt or F9 to dictate (or toggle with Super + Ctrl + X)." -t 10000
+40
View File
@@ -0,0 +1,40 @@
#!/bin/bash
# omarchy:summary=Remove Superwhisper dictation and its desktop integration
# omarchy:requires-sudo=true
set -e
config_home="${XDG_CONFIG_HOME:-$HOME/.config}"
data_home="${XDG_DATA_HOME:-$HOME/.local/share}"
systemctl --user disable --now superwhisper.service 2>/dev/null || true
omarchy-shell -q shell setPluginEnabled superwhisper.panel false >/dev/null
omarchy-pkg-drop superwhisper-bin
systemctl --user daemon-reload
# Remove the package setup's links, preserving independently installed copies
# and the user's preferences, history, and models.
unlink_package_link() {
if [[ -L $1 && $(readlink "$1") == "$2" ]]; then
rm "$1"
fi
}
unlink_package_link "$HOME/.local/bin/superwhisper" /usr/bin/superwhisper
unlink_package_link "$data_home/superwhisper/app/current" /opt/superwhisper
unlink_package_link "$config_home/omarchy/plugins/superwhisper-panel" /opt/superwhisper/assets/omarchy-plugin/superwhisper-panel
for agent in .agents .claude .codex .pi/agent; do
unlink_package_link "$HOME/$agent/skills/superwhisper" /opt/superwhisper/assets/agent-skill/superwhisper
done
backend_config="$config_home/omarchy/defaults/dictation"
if [[ -f $backend_config && $(< "$backend_config") == "superwhisper" ]]; then
rm "$backend_config"
fi
omarchy-shell -q shell rescanPlugins
hyprctl reload >/dev/null
if systemctl --user -q is-active omarchy-fcitx5.service; then
systemctl --user restart omarchy-fcitx5.service
fi
@@ -18,6 +18,10 @@ if omarchy-cmd-present voxtype; then
omarchy-pkg-drop voxtype-bin
rm -rf ~/.config/voxtype
rm -rf ~/.local/share/voxtype
backend_config="${XDG_CONFIG_HOME:-$HOME/.config}/omarchy/defaults/dictation"
if [[ -f $backend_config && $(< "$backend_config") == "voxtype" ]]; then
rm "$backend_config"
fi
hyprctl reload >/dev/null
else
echo "Voxtype was not installed."
-26
View File
@@ -1,26 +0,0 @@
#!/bin/bash
# omarchy:summary=Install and configure Voxtype dictation
# omarchy:requires-sudo=true
set -e
# Install voxtype and configure it for use.
if gum confirm "Install Voxtype + AI model (~150MB) to enable dictation?"; then
omarchy-pkg-add wtype voxtype-bin
# Setup voxtype
mkdir -p ~/.config/voxtype
cp "$OMARCHY_PATH/default/voxtype/config.toml" ~/.config/voxtype/
voxtype setup --download --no-post-install
if omarchy-hw-vulkan; then
voxtype setup gpu --enable || true
fi
voxtype setup systemd
hyprctl reload >/dev/null
omarchy-restart-shell
omarchy-notification-send -g  "Voxtype Dictation Ready" "Hold F9 to dictate (or toggle with Super + Ctrl + X)." -t 10000
fi
-13
View File
@@ -1,13 +0,0 @@
#!/bin/bash
# omarchy:summary=Stream voxtype --follow status as bar-friendly JSON
if omarchy-cmd-missing voxtype; then
echo '{"alt": "", "class": "idle", "tooltip": ""}'
exit 0
fi
# Keep the status follower as the direct child of the bar. Quickshell
# clean up their direct child when they restart; a shell pipeline/coproc would
# leave `voxtype status --follow` orphaned under the user systemd instance.
exec setpriv --pdeathsig TERM voxtype status --follow --extended --format json
+9
View File
@@ -0,0 +1,9 @@
if o.shell_succeeds("omarchy-default-dictation") then
o.bind("SUPER + CTRL + X", "Toggle dictation", "omarchy-dictation toggle")
o.bind("F9", "Start dictation (push-to-talk)", "omarchy-dictation start")
o.bind("F9", "Stop dictation (push-to-talk)", "omarchy-dictation stop", { release = true })
-- A modifier's mask changes between its press and release. Match the keysym
-- independently of that mask; AltGr layouts use a different keysym.
o.bind("ALT + Alt_R", "Start dictation (push-to-talk)", "omarchy-dictation start", { ignore_mods = true })
o.bind("ALT + Alt_R", "Stop dictation (push-to-talk)", "omarchy-dictation stop", { release = true, ignore_mods = true })
end
-5
View File
@@ -1,5 +0,0 @@
if o.cmd_present("voxtype") then
o.bind("SUPER + CTRL + X", "Toggle dictation", "voxtype record toggle")
o.bind("F9", "Start dictation (push-to-talk)", "voxtype record start")
o.bind("F9", "Stop dictation (push-to-talk)", "voxtype record stop", { release = true })
end
+24
View File
@@ -0,0 +1,24 @@
-- Backends may supply their own desktop integration without editing the
-- user's Hyprland config. Recording bindings remain owned by Omarchy.
local paths = require("default.hypr.paths")
local pipe = io.popen("omarchy-default-dictation 2>/dev/null", "r")
if not pipe then
return
end
local backend = pipe:read("*l")
pipe:close()
if not backend or not backend:match("^[%w%-]+$") or o.cmd_missing("omarchy-dictation-" .. backend) then
return
end
local integration = paths.config_home .. "/" .. backend .. "/shortcuts.lua"
local file = io.open(integration, "r")
if file then
file:close()
local ok, error = pcall(dofile, integration)
if not ok then
print("Could not load dictation backend " .. backend .. ": " .. tostring(error))
end
end
+2 -1
View File
@@ -10,7 +10,7 @@ if _G.omarchy_default_bindings ~= false then
require("default.hypr.bindings.clipboard")
require("default.hypr.bindings.tiling")
require("default.hypr.bindings.utilities")
require("default.hypr.bindings.voxtype")
require("default.hypr.bindings.dictation")
require_optional.module("default.hypr.bindings.applications")
end
require("default.hypr.envs")
@@ -18,6 +18,7 @@ require("default.hypr.looknfeel")
require("default.hypr.qconsole")
require("default.hypr.input")
require("default.hypr.windows")
require("default.hypr.dictation-backend")
-- Current theme overrides.
require_optional.module("omarchy.current.theme.hyprland")
+6 -2
View File
@@ -159,6 +159,9 @@
"setup.default.browser.edge": {"icon":"󰇩","label":"Edge","checked":"[[ \"$(omarchy-default-browser)\" == \"edge\" ]]","action":"omarchy-default-browser edge"},
"setup.default.browser.firefox": {"icon":"","label":"Firefox","checked":"[[ \"$(omarchy-default-browser)\" == \"firefox\" ]]","action":"omarchy-default-browser firefox"},
"setup.default.browser.zen": {"icon":"󰖟","label":"Zen","checked":"[[ \"$(omarchy-default-browser)\" == \"zen\" ]]","action":"omarchy-default-browser zen"},
"setup.default.dictation": {"icon":"","label":"Dictation","title":"Default Dictation"},
"setup.default.dictation.voxtype": {"icon":"","label":"Voxtype","checked":"[[ \"$(omarchy-default-dictation)\" == \"voxtype\" ]]","action":"omarchy-default-dictation voxtype"},
"setup.default.dictation.superwhisper": {"icon":"","label":"Superwhisper","checked":"[[ \"$(omarchy-default-dictation)\" == \"superwhisper\" ]]","action":"omarchy-default-dictation superwhisper"},
"setup.default.terminal": {"icon":"","label":"Terminal","title":"Default Terminal"},
"setup.default.terminal.alacritty": {"icon":"","label":"Alacritty","checked":"[[ \"$(omarchy-default-terminal)\" == \"alacritty\" ]]","action":"omarchy-default-terminal alacritty"},
"setup.default.terminal.foot": {"icon":"","label":"Foot","checked":"[[ \"$(omarchy-default-terminal)\" == \"foot\" ]]","action":"omarchy-default-terminal foot"},
@@ -247,7 +250,6 @@
"install.terminal.kitty": {"icon":"","label":"Kitty","disabled":"omarchy-pkg-present kitty","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal kitty'"},
"install.ai.chatgpt": {"icon":"","iconFont":"omarchy","label":"ChatGPT Desktop","disabled":"omarchy-pkg-present openai-codex-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-chatgpt"},
"install.ai.claude": {"icon":"","iconFont":"omarchy","label":"Claude Desktop","disabled":"omarchy-pkg-present claude-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-claude"},
"install.ai.dictation": {"icon":"","label":"Dictation","disabled":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install"},
"install.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","disabled":"omarchy-pkg-present grok-bot","action":"omarchy-install-and-launch 'Grok Bot' grok-bot grok-bot"},
"install.ai.hermes": {"icon":"","iconFont":"omarchy","label":"Hermes Desktop","disabled":"omarchy-pkg-present hermes-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-hermes"},
"install.ai.lm-studio": {"icon":"","iconFont":"omarchy","label":"LM Studio","disabled":"omarchy-pkg-present lmstudio-bin","action":"omarchy-install-app 'LM Studio' lmstudio-bin"},
@@ -291,6 +293,7 @@
// Remove
"remove.package": {"icon":"󰣇","label":"Package","action":"xdg-terminal-exec --app-id=org.omarchy.terminal omarchy-pkg-remove"},
"remove.ai": {"icon":"󱚤","label":"AI","title":"Remove"},
"remove.dictation": {"icon":"","label":"Dictation","when":"omarchy-pkg-present voxtype-bin || omarchy-pkg-present superwhisper-bin"},
"remove.service": {"icon":"","label":"Services","title":"Remove"},
"remove.development": {"icon":"󰵮","label":"Development","title":"Remove"},
"remove.theme": {"icon":"󰸌","label":"Theme","action":"omarchy-theme-remove"},
@@ -318,7 +321,8 @@
"remove.service.ssh-agent": {"icon":"󰣀","label":"SSH Agent","when":"systemctl --user -q is-enabled gcr-ssh-agent.socket","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-service-ssh-agent"},
"remove.ai.chatgpt": {"icon":"","iconFont":"omarchy","label":"ChatGPT Desktop","when":"omarchy-pkg-present openai-codex-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-chatgpt"},
"remove.ai.claude": {"icon":"","iconFont":"omarchy","label":"Claude Desktop","when":"omarchy-pkg-present claude-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-claude"},
"remove.ai.dictation": {"icon":"","label":"Dictation","when":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-remove"},
"remove.dictation.voxtype": {"icon":"","label":"Voxtype","when":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-dictation-voxtype"},
"remove.dictation.superwhisper": {"icon":"","label":"Superwhisper","when":"omarchy-pkg-present superwhisper-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-dictation-superwhisper"},
"remove.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","when":"omarchy-pkg-present grok-bot","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-grok-bot"},
"remove.ai.lm-studio": {"icon":"","iconFont":"omarchy","label":"LM Studio","when":"omarchy-pkg-present lmstudio-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-lm-studio"},
"remove.ai.ollama": {"icon":"","iconFont":"omarchy","label":"Ollama","when":"omarchy-pkg-present ollama","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-ollama"},
+1 -1
View File
@@ -11,7 +11,7 @@
state_file = "auto"
[hotkey]
# Hotkey is configured in Hyprland. Default is Super + Ctrl + X
# Hotkeys are configured in Hyprland: hold Right Alt or F9, or toggle with Super + Ctrl + X.
enabled = false
[audio]
+2 -2
View File
@@ -3,7 +3,7 @@
set -e
if omarchy-done ensure voxtype-install-invitation; then
omarchy-notification-send -u critical -g  "Install Dictation with Voxtype" \
omarchy-notification-send -u critical -g  "Install Dictation" \
"Click to install voice dictation for Omarchy." \
--exec omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install
--exec omarchy-menu summon setup.default.dictation
fi
+2 -2
View File
@@ -150,8 +150,8 @@ Usually on Linux, you need `Ctrl + Shift + C/V` to copy'n'paste in the terminal
| `Super + Alt + ]` | Make webcam overlay larger while recording |
| `Alt + Shift + L` | Copy current URL from webapp or Chromium |
| `Alt + Shift + D` | Download the video on the current page to `~/Videos` |
| `Super + Ctrl + X` | Start/stop dictation (requires _Install > AI > Dictation_) |
| `F9` | Push-to-talk dictation (requires _Install > AI > Dictation_) |
| `Super + Ctrl + X` | Start/stop dictation (requires _Setup > Defaults > Dictation_) |
| `Right Alt` / `F9` | Push-to-talk dictation (requires Voxtype or Superwhisper) |
With screenrecordings, the hotkey first asks which audio you want, then starts recording. Hit it again to stop. See [screenshots and recording](12-screenshots-recording.md) for the details.
+10 -2
View File
@@ -10,6 +10,14 @@ This is very helpful for grabbing addresses out of image footers or phone number
### Dictation
Omarchy offers AI dictation via [Voxtype](https://voxtype.io/). You install it via _Install > AI > Dictation_ through the Omarchy menu. By default, it'll load a base English model that takes up 150MB. But you can tweak which model you'd like to use by running `voxtype setup model` in the terminal. And you can tweak all the settings via `~/.config/voxtype/config.toml`.
Install [Voxtype](https://voxtype.io/) or [Superwhisper](https://superwhisper.com/) through _Setup > Defaults > Dictation_ in the Omarchy menu. Selecting a backend installs it if needed and makes it the default for dictation. Voxtype loads a base English model that takes up 150MB; change the model with `voxtype setup model` and other settings through `~/.config/voxtype/config.toml`. Superwhisper opens its settings to choose cloud or local processing, and OPR keeps the application updated.
Once installed, you dictate by holding down `F9` or by toggling with `Super + Ctrl + X`, and the dictated text will appear in the focused input area.
Once installed, you dictate by holding down `Right Alt` or `F9`, or by toggling with `Super + Ctrl + X`, and the dictated text will appear in the focused input area. These shortcuts are enabled only after selecting a backend. Right Alt also starts dictation when used in a modifier chord; use Left Alt for those chords. On keyboard layouts where Right Alt is AltGr, use `F9` to keep AltGr available for typing.
The same shortcuts can use an installed Superwhisper instead. Select your backend through Setup > Defaults > Dictation. The backend installer configures it and saves the selection. Run `omarchy default dictation` to see which one is selected. The selection is saved in `~/.config/omarchy/defaults/dictation`. Dictation requires an explicit selection.
Omarchy loads the selected backend's desktop integration automatically. No Superwhisper configuration is needed in `~/.config/hypr/`.
Custom bindings and scripts can use `omarchy dictation start`, `omarchy dictation stop`, and `omarchy dictation toggle`. The selected backend handles the recording and transcription. Selecting Superwhisper disables its native hold shortcut and sets its native toggle to `Alt + Space`, keeping it separate from Omarchy's recording keys. Superwhisper requires at least one native recording shortcut, and its cancellation shortcut stays available.
Additional backends can integrate without changing Omarchy. Provide an executable named `omarchy-dictation-<backend>` on PATH that accepts `start`, `stop`, and `toggle` and returns promptly, with a nonzero status on failure. Its installer configures the provider and calls `omarchy-dictation-use <backend>` after successful setup. This stops the previous backend and saves the selection atomically; `stop` must succeed when the provider is idle or unavailable. Backend names use lowercase letters, digits, and hyphens. Provide `omarchy-install-dictation-<backend>` for setup; `omarchy default dictation <backend>` launches it. Add a Defaults menu entry through an Omarchy menu extension to call that selector. Optional desktop integration can live in `${XDG_CONFIG_HOME:-$HOME/.config}/<backend>/shortcuts.lua`.
+9
View File
@@ -0,0 +1,9 @@
echo "Preserve Voxtype as the default for existing dictation users"
backend_config="${XDG_CONFIG_HOME:-$HOME/.config}/omarchy/defaults/dictation"
if [[ ! -e $backend_config ]] && omarchy-pkg-present voxtype-bin; then
mkdir -p "${backend_config%/*}"
temporary=$(mktemp "$backend_config.XXXXXX")
printf '%s\n' voxtype > "$temporary"
mv -f "$temporary" "$backend_config"
fi
@@ -1,38 +0,0 @@
import QtQuick
import Quickshell.Io
import qs.Ui
BarIndicator {
id: root
property string state: "idle"
property string icon: ""
active: state === "recording"
activeText: icon
inactiveText: "󰍬"
activeTooltipText: state
inactiveTooltipText: "Dictate"
function update(raw) {
var data = extractData(raw)
state = String(data.alt || data.class || "idle")
if (state === "recording") icon = "󰍬"
else if (state === "transcribing") icon = "󰔟"
else icon = ""
}
Process {
command: ["bash", "-c", "omarchy-voxtype-status"]
running: true
stdout: SplitParser {
onRead: function(data) { root.update(data) }
}
}
onPressed: function() {
if (!root.bar) return
root.bar.run("omarchy-voxtype-config")
}
}
@@ -31,11 +31,6 @@
"label": "Passwordless sudo",
"description": "Warning while passwordless root access is enabled"
},
{
"value": "Dictation",
"label": "Dictation",
"description": "Voice typing status"
},
{
"value": "ScreenRecording",
"label": "Screen recording",
+1 -1
View File
@@ -8,7 +8,7 @@ BarWidget {
id: root
moduleName: "omarchy.indicators"
readonly property var defaultIndicatorEntries: [ "PasswordlessSudo", "Dictation", "ScreenRecording", "Reminder", "NightLight", "Dnd", "StayAwake" ]
readonly property var defaultIndicatorEntries: [ "PasswordlessSudo", "ScreenRecording", "Reminder", "NightLight", "Dnd", "StayAwake" ]
readonly property var indicatorEntries: indicatorEntriesFromSettings(settings)
property var activeIndicatorIds: []
property var indicatorActiveStates: ({})
+1
View File
@@ -398,6 +398,7 @@ var GUARD_READERS = [
"omarchy-default-browser",
"omarchy-default-editor",
"omarchy-default-terminal",
"omarchy-default-dictation",
"omarchy-dns"
]
+318
View File
@@ -0,0 +1,318 @@
#!/bin/bash
set -euo pipefail
source "$(dirname "${BASH_SOURCE[0]}")/base-test.sh"
test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT
export HOME="$test_tmp/home" XDG_CONFIG_HOME="$test_tmp/config" OMARCHY_PATH="$ROOT"
export DICTATION_LOG="$test_tmp/calls" DICTATION_INSTALLED="voxtype superwhisper"
mkdir -p "$test_tmp/bin" "$XDG_CONFIG_HOME/omarchy/defaults"
export PATH="$test_tmp/bin:$ROOT/bin:$PATH"
config="$XDG_CONFIG_HOME/omarchy/defaults/dictation"
cat > "$test_tmp/bin/omarchy-cmd-present" <<'SH'
#!/bin/bash
case "$1" in
omarchy-dictation-*|omarchy-install-dictation-*) command -v "$1" >/dev/null ;;
*) [[ " $DICTATION_INSTALLED " == *" $1 "* ]] ;;
esac
SH
cat > "$test_tmp/bin/omarchy-cmd-missing" <<'SH'
#!/bin/bash
! omarchy-cmd-present "$1"
SH
cat > "$test_tmp/bin/omarchy-launch-floating-terminal-with-presentation" <<'SH'
#!/bin/bash
printf '%s\n' "$*" > "$DICTATION_INSTALL_LOG"
SH
export DICTATION_INSTALL_LOG="$test_tmp/install"
for backend in voxtype superwhisper; do
cat > "$test_tmp/bin/$backend" <<'SH'
#!/bin/bash
printf '%s %s\n' "${0##*/}" "$*" >> "$DICTATION_LOG"
if [[ $1 == "status" ]]; then echo "${VOXTYPE_STATUS:-idle}"; exit 0; fi
if [[ -n ${FAIL_SETUP_STEP:-} && $* == "$FAIL_SETUP_STEP" ]]; then exit 7; fi
exit "${DICTATION_EXIT:-0}"
SH
done
for command in omarchy-pkg-add omarchy-notification-send hyprctl omarchy-restart-shell; do
cat > "$test_tmp/bin/$command" <<'SH'
#!/bin/bash
printf '%s %s\n' "${0##*/}" "$*" >> "$DICTATION_LOG"
exit "${SETUP_EXIT:-0}"
SH
done
cat > "$test_tmp/bin/systemctl" <<'SH'
#!/bin/bash
if [[ $* == "--user is-active --quiet superwhisper.service" ]]; then
[[ ${DICTATION_SERVICE_ACTIVE:-1} == 1 ]]
else
printf '%s %s\n' "${0##*/}" "$*" >> "$DICTATION_LOG"
exit "${SERVICE_EXIT:-0}"
fi
SH
cat > "$test_tmp/bin/omarchy-hw-vulkan" <<'SH'
#!/bin/bash
exit 1
SH
cat > "$test_tmp/bin/gum" <<'SH'
#!/bin/bash
exit "${CONFIRM_EXIT:-0}"
SH
cat > "$test_tmp/bin/bash" <<'SH'
#!/bin/bash
if [[ $1 == "/usr/share/superwhisper/setup-user" ]]; then
printf '%s\n' setup-user >> "$DICTATION_LOG"
exit "${SETUP_EXIT:-0}"
else
exec /bin/bash "$@"
fi
SH
chmod +x "$test_tmp/bin/"*
if omarchy-dictation start 2> "$test_tmp/error"; then
fail "unset backend must fail even when providers are installed"
fi
[[ ! -e $DICTATION_LOG ]] || fail "recording never configures or installs a backend"
pass "dictation requires explicit configuration"
omarchy-default-dictation superwhisper
[[ $(cat "$DICTATION_INSTALL_LOG") == "omarchy-install-dictation-superwhisper" ]] || fail "Defaults launches backend installer"
[[ ! -e $config ]] || fail "Defaults does not select before setup succeeds"
if omarchy-default-dictation unknown > "$test_tmp/output" 2>&1; then fail "missing installer must fail"; fi
if omarchy-default-dictation '../invalid' > "$test_tmp/output" 2>&1; then fail "invalid backend must fail"; fi
pass "Defaults delegates setup without prematurely selecting a backend"
for backend in voxtype superwhisper; do
printf '%s\n' "$backend" > "$config"
[[ $(omarchy-default-dictation) == "$backend" ]] || fail "configured backend is readable"
: > "$DICTATION_LOG"
for action in start stop toggle; do omarchy dictation "$action"; done
if [[ $backend == "voxtype" ]]; then
expected=$'voxtype record start\nvoxtype status\nvoxtype record stop\nvoxtype record toggle'
else
expected=$'superwhisper start\nsuperwhisper stop\nsuperwhisper record'
fi
[[ $(cat "$DICTATION_LOG") == "$expected" ]] || fail "adapter dispatches recording for $backend"
done
pass "built-in adapters implement start stop and toggle"
cat > "$test_tmp/bin/omarchy-dictation-future-backend" <<'SH'
#!/bin/bash
[[ ! -e /proc/$$/fd/9 ]] || exit 90
printf '%s\n' "$1" >> "$DICTATION_LOG"
exit "${DICTATION_EXIT:-0}"
SH
chmod +x "$test_tmp/bin/omarchy-dictation-future-backend"
cat > "$test_tmp/bin/omarchy-install-dictation-future-backend" <<'SH'
#!/bin/bash
exit 0
SH
chmod +x "$test_tmp/bin/omarchy-install-dictation-future-backend"
omarchy-default-dictation future-backend
[[ $(cat "$DICTATION_INSTALL_LOG") == "omarchy-install-dictation-future-backend" ]] || fail "third backend installer requires no selector changes"
printf '%s\n' future-backend > "$config"
: > "$DICTATION_LOG"
for action in start stop toggle; do omarchy-dictation "$action"; done
[[ $(cat "$DICTATION_LOG") == $'start\nstop\ntoggle' ]] || fail "third backend requires no dispatcher changes"
result=0
DICTATION_EXIT=7 omarchy-dictation start || result=$?
(( result == 7 )) || fail "backend failures propagate"
rm "$test_tmp/bin/omarchy-dictation-future-backend"
if omarchy-dictation start 2> "$test_tmp/error"; then fail "missing adapter must fail"; fi
printf '%s\n' '../invalid' > "$config"
if omarchy-dictation start 2> "$test_tmp/error"; then fail "invalid backend must fail"; fi
pass "external adapters follow the same protocol and errors propagate"
printf '%s\n' voxtype > "$config"
if SETUP_EXIT=1 omarchy-install-dictation-superwhisper > "$test_tmp/output" 2>&1; then fail "failed installation must fail"; fi
[[ $(cat "$config") == "voxtype" ]] || fail "failed setup must preserve selection"
if DICTATION_EXIT=7 omarchy-install-dictation-superwhisper > "$test_tmp/output" 2>&1; then fail "failed shortcut setup must fail"; fi
[[ $(cat "$config") == "voxtype" ]] || fail "failed shortcuts must preserve selection"
omarchy-install-dictation-superwhisper
[[ $(omarchy-default-dictation) == "superwhisper" ]] || fail "installer selects Superwhisper"
pass "Superwhisper installer owns setup and saves selection only after success"
DICTATION_INSTALLED=superwhisper
CONFIRM_EXIT=1 omarchy-install-dictation-voxtype
[[ $(cat "$config") == "superwhisper" ]] || fail "cancelled installation preserves selection"
omarchy-install-dictation-voxtype
[[ $(omarchy-default-dictation) == "voxtype" ]] || fail "installer selects Voxtype"
DICTATION_INSTALLED="voxtype superwhisper"
: > "$DICTATION_LOG"
omarchy-install-dictation-voxtype
grep -q 'voxtype setup --download --no-post-install' "$DICTATION_LOG" || fail "installed Voxtype retries model setup"
printf '%s\n' 'custom model configuration' > "$XDG_CONFIG_HOME/voxtype/config.toml"
printf '%s\n' superwhisper > "$config"
for step in 'setup --download --no-post-install' 'setup systemd'; do
if FAIL_SETUP_STEP="$step" omarchy-install-dictation-voxtype > "$test_tmp/output" 2>&1; then fail "incomplete setup must fail"; fi
[[ $(cat "$config") == "superwhisper" ]] || fail "incomplete setup preserves selection"
done
if SERVICE_EXIT=1 omarchy-install-dictation-voxtype > "$test_tmp/output" 2>&1; then fail "service failure must fail"; fi
[[ $(cat "$config") == "superwhisper" ]] || fail "service failure preserves selection"
omarchy-install-dictation-voxtype
[[ $(cat "$config") == "voxtype" ]] || fail "retry finishes installed Voxtype setup"
[[ $(cat "$XDG_CONFIG_HOME/voxtype/config.toml") == "custom model configuration" ]] || fail "retry preserves custom configuration"
pass "Voxtype retries incomplete model and service setup without replacing configuration"
: > "$DICTATION_LOG"
if DICTATION_EXIT=7 omarchy-dictation-use superwhisper > "$test_tmp/output" 2>&1; then fail "failed stop must prevent switching"; fi
[[ $(cat "$config") == "voxtype" ]] || fail "failed stop preserves selection"
omarchy-dictation-use superwhisper
[[ $(cat "$DICTATION_LOG") == $'voxtype status\nvoxtype record stop\nvoxtype status\nvoxtype record stop' ]] || fail "switch stops the previous backend"
[[ $(cat "$config") == "superwhisper" ]] || fail "successful stop permits switching"
: > "$DICTATION_LOG"
omarchy-dictation-use superwhisper
[[ ! -s $DICTATION_LOG ]] || fail "reselecting does not interrupt recording"
printf '%s\n' voxtype > "$config"
VOXTYPE_STATUS=stopped omarchy-dictation-use superwhisper
[[ $(cat "$config") == "superwhisper" ]] || fail "a stopped Voxtype daemon permits switching"
printf '%s\n' superwhisper > "$config"
DICTATION_SERVICE_ACTIVE=0 omarchy-dictation-use voxtype
[[ $(cat "$config") == "voxtype" ]] || fail "a stopped Superwhisper service permits switching"
printf '%s\n' removed-backend > "$config"
omarchy-dictation-use voxtype
[[ $(cat "$config") == "voxtype" ]] || fail "a removed adapter permits recovery"
pass "switching stops recordings, preserves failed stops, and recovers unavailable providers"
# Readers may run throughout a switch, but must never observe a truncated file.
(
for ((i = 0; i < 200; i++)); do
selection=$(cat "$config")
[[ $selection == "voxtype" || $selection == "superwhisper" ]] || exit 1
done
) &
reader=$!
for ((i = 0; i < 20; i++)); do
omarchy-dictation-use voxtype
omarchy-dictation-use superwhisper
done
wait "$reader" || fail "concurrent readers always see a complete selection"
printf '%s\n' voxtype > "$config"
pass "selection updates are atomic for concurrent readers"
lua <<'LUA'
local root = os.getenv("ROOT")
local configured, bindings = false, {}
o = {
shell_succeeds = function(command)
assert(command == "omarchy-default-dictation")
return configured
end,
bind = function(...) table.insert(bindings, {...}) end,
}
dofile(root .. "/default/hypr/bindings/dictation.lua")
assert(#bindings == 0, "unconfigured dictation leaves application keys available")
configured = true
dofile(root .. "/default/hypr/bindings/dictation.lua")
assert(#bindings == 5, "any configured backend receives the same bindings")
LUA
pass "dictation shortcuts require a selection without limiting backend names"
lua <<'LUA'
local root = os.getenv("ROOT")
package.path = root .. "/?.lua;" .. package.path
package.loaded["default.hypr.paths"] = { config_home = "/config" }
local selected, available, loaded = "superwhisper", true, {}
local missing = false
o = { cmd_missing = function() return missing end }
local real_open, real_popen, real_dofile = io.open, io.popen, dofile
io.popen = function(command)
assert(command == "omarchy-default-dictation 2>/dev/null")
return { read = function() return selected end, close = function() end }
end
io.open = function(path)
assert(path == "/config/" .. selected .. "/shortcuts.lua")
if available then return { close = function() end } end
end
dofile = function(path) table.insert(loaded, path) end
local function load_backend()
package.loaded["default.hypr.dictation-backend"] = nil
require("default.hypr.dictation-backend")
end
load_backend()
assert(loaded[1] == "/config/superwhisper/shortcuts.lua")
selected, available = "voxtype", false
load_backend()
assert(#loaded == 1, "a backend with no integration should need no config")
selected, available = "future-backend", true
load_backend()
assert(loaded[2] == "/config/future-backend/shortcuts.lua", "integration must not be limited to named providers")
missing = true
load_backend()
assert(#loaded == 2, "an uninstalled backend must not load a stale bridge")
missing = false
dofile = function() error("broken generated bridge") end
load_backend()
assert(#loaded == 2, "a broken bridge must not abort the desktop configuration")
dofile = function(path) table.insert(loaded, path) end
selected = "../../outside"
load_backend()
assert(#loaded == 2, "a backend must not escape the config directory")
selected = nil
load_backend()
assert(#loaded == 2, "no installed backend must leave configuration usable")
io.open, io.popen, dofile = real_open, real_popen, real_dofile
LUA
pass "desktop integration follows the selected backend without personal Hyprland config"
cat > "$test_tmp/bin/systemctl" <<'SH'
#!/bin/bash
exit 0
SH
cat > "$test_tmp/bin/omarchy-pkg-drop" <<'SH'
#!/bin/bash
printf '%s\n' "$*" >> "$DICTATION_LOG"
exit "${REMOVE_EXIT:-0}"
SH
chmod +x "$test_tmp/bin/systemctl" "$test_tmp/bin/omarchy-pkg-drop"
omarchy-remove-dictation-voxtype
[[ ! -e $config ]] || fail "removing Voxtype clears selection"
if omarchy-default-dictation 2> "$test_tmp/error"; then fail "removal must not auto-select another backend"; fi
pass "removal clears selection without automatic fallback"
cat > "$test_tmp/bin/omarchy-shell" <<'SH'
#!/bin/bash
printf '%s\n' "$*" >> "$DICTATION_LOG"
SH
chmod +x "$test_tmp/bin/omarchy-shell"
export XDG_DATA_HOME="$test_tmp/data"
mkdir -p "$HOME/.local/bin" "$XDG_DATA_HOME/superwhisper/app" "$XDG_DATA_HOME/superwhisper/models" "$XDG_CONFIG_HOME/omarchy/plugins" "$HOME/.agents/skills" "$HOME/.claude/skills"
ln -s /usr/bin/superwhisper "$HOME/.local/bin/superwhisper"
ln -s /opt/superwhisper "$XDG_DATA_HOME/superwhisper/app/current"
ln -s /opt/superwhisper/assets/omarchy-plugin/superwhisper-panel "$XDG_CONFIG_HOME/omarchy/plugins/superwhisper-panel"
ln -s /opt/superwhisper/assets/agent-skill/superwhisper "$HOME/.agents/skills/superwhisper"
ln -s /custom/skill "$HOME/.claude/skills/superwhisper"
printf '%s\n' retained > "$XDG_DATA_HOME/superwhisper/models/model"
printf '%s\n' superwhisper > "$config"
if REMOVE_EXIT=1 omarchy-remove-dictation-superwhisper > "$test_tmp/output" 2>&1; then
fail "failed package removal must fail"
fi
[[ -L $HOME/.local/bin/superwhisper && $(cat "$config") == "superwhisper" ]] || fail "failed removal preserves selection and links"
omarchy-remove-dictation-superwhisper
[[ ! -e $config ]] || fail "Superwhisper removal clears its selection"
for link in "$HOME/.local/bin/superwhisper" "$XDG_DATA_HOME/superwhisper/app/current" "$XDG_CONFIG_HOME/omarchy/plugins/superwhisper-panel" "$HOME/.agents/skills/superwhisper"; do
[[ ! -L $link ]] || fail "Superwhisper removal unlinks packaged integration"
done
[[ $(readlink "$HOME/.claude/skills/superwhisper") == "/custom/skill" ]] || fail "custom skills are preserved"
[[ -f $XDG_DATA_HOME/superwhisper/models/model ]] || fail "downloaded models are retained"
printf '%s\n' voxtype > "$config"
omarchy-remove-dictation-superwhisper
[[ $(cat "$config") == "voxtype" ]] || fail "removing Superwhisper preserves another selected backend"
pass "Superwhisper removal clears its selection and owned links while preserving user data and other backends"
cat > "$test_tmp/bin/omarchy-pkg-present" <<'SH'
#!/bin/bash
[[ ${VOXTYPE_PACKAGE:-0} == 1 && $1 == "voxtype-bin" ]]
SH
chmod +x "$test_tmp/bin/omarchy-pkg-present"
rm "$config"
VOXTYPE_PACKAGE=0 /bin/bash -euo pipefail "$ROOT/migrations/1791479273.sh"
[[ ! -e $config ]] || fail "migration does not select an uninstalled backend"
VOXTYPE_PACKAGE=1 /bin/bash -euo pipefail "$ROOT/migrations/1791479273.sh"
[[ $(cat "$config") == "voxtype" ]] || fail "existing Voxtype users keep dictation after upgrade"
printf '%s\n' future-backend > "$config"
VOXTYPE_PACKAGE=1 /bin/bash -euo pipefail "$ROOT/migrations/1791479273.sh"
[[ $(cat "$config") == "future-backend" ]] || fail "upgrade preserves explicit backend selections"
pass "one-time upgrade preserves existing Voxtype without runtime autodetection"
@@ -198,16 +198,6 @@ ShellRoot {
root.assertTrue(root.commandCount("omarchy-capture-screenrecording --stop-recording") === 1, "Screen Recording left click stops active recording")
}
var dictation = root.createIndicator("Dictation")
if (dictation) {
dictation.moduleName = "Dictation"
root.injectBar(dictation)
dictation.triggerPress(Qt.LeftButton)
dictation.triggerPress(Qt.RightButton)
root.assertTrue(root.commandCount("omarchy-voxtype-config") === 2, "Dictation clicks run config command")
root.assertTrue(root.commandCount("omarchy-voxtype-model") === 0, "Dictation clicks do not run model command")
}
var stayAwake = root.createIndicator("StayAwake")
if (stayAwake) {
stayAwake.moduleName = "StayAwake"
@@ -147,8 +147,11 @@ trap 'rm -rf "$tmpdir"' EXIT
home="$tmpdir/home"
stub_bin="$tmpdir/bin"
mkdir -p "$home" "$stub_bin"
touch "$stub_bin/voxtype"
chmod +x "$stub_bin/voxtype"
cat > "$stub_bin/omarchy-default-dictation" <<'SH'
#!/bin/bash
echo voxtype
SH
chmod +x "$stub_bin/omarchy-default-dictation"
bindings=$(PATH="$stub_bin:$PATH" list_bindings "$home")
[[ -n $bindings ]] || fail "default bindings load for the conflict check"
+19 -26
View File
@@ -144,36 +144,29 @@ no_bindings_output=$(run_omarchy_bindings "$no_bindings_home" 'omarchy_default_b
[[ -z $no_bindings_output ]] || fail "default binding variable disables all Omarchy bindings" "$no_bindings_output"
pass "default binding variable disables all Omarchy bindings"
voxtype_home="$tmpdir/voxtype-home"
voxtype_bin="$tmpdir/voxtype-bin"
mkdir -p "$voxtype_home" "$voxtype_bin"
touch "$voxtype_bin/voxtype"
chmod +x "$voxtype_bin/voxtype"
voxtype_output=$(PATH="$voxtype_bin:$PATH" run_omarchy_bindings "$voxtype_home")
grep -Fq $'SUPER + CTRL + X Toggle dictation' <<<"$voxtype_output" ||
fail "installed Voxtype enables its toggle binding"
grep -Fq $'F9 Start dictation (push-to-talk)' <<<"$voxtype_output" ||
fail "installed Voxtype enables its push-to-talk binding"
grep -Fq $'F9 Stop dictation (push-to-talk)' <<<"$voxtype_output" ||
fail "installed Voxtype enables its release binding"
pass "installed Voxtype conditionally enables dictation bindings"
voxtype_without_execute_output=$(PATH="$voxtype_bin:$PATH" run_omarchy_bindings \
"$voxtype_home" 'os.execute = function() return nil, "No child processes", 10 end')
grep -Fq $'SUPER + CTRL + X Toggle dictation' <<<"$voxtype_without_execute_output" ||
fail "Voxtype detection does not require spawning a subprocess"
pass "installed Voxtype detection works without os.execute"
dictation_home="$tmpdir/dictation-home"
missing_bin="$tmpdir/missing-bin"
mkdir -p "$missing_bin"
mkdir -p "$dictation_home" "$missing_bin"
ln -s "$(command -v lua)" "$missing_bin/lua"
ln -s "$(command -v lspci)" "$missing_bin/lspci"
ln -s "$(command -v sort)" "$missing_bin/sort"
missing_voxtype_output=$(PATH="$missing_bin" run_omarchy_bindings "$voxtype_home")
if grep -Fq $'SUPER + CTRL + X Toggle dictation' <<<"$missing_voxtype_output"; then
fail "missing Voxtype skips its bindings"
fi
pass "missing Voxtype skips dictation bindings"
dictation_output=$(PATH="$missing_bin" run_omarchy_bindings "$dictation_home")
if grep -Fq 'dictation' <<<"$dictation_output"; then fail "unconfigured dictation leaves application shortcuts available"; fi
cat > "$missing_bin/omarchy-default-dictation" <<'SH'
#!/bin/bash
echo future-backend
SH
chmod +x "$missing_bin/omarchy-default-dictation"
dictation_output=$(PATH="$missing_bin" run_omarchy_bindings "$dictation_home")
for binding in \
$'SUPER + CTRL + X\tToggle dictation' \
$'F9\tStart dictation (push-to-talk)' \
$'F9\tStop dictation (push-to-talk)' \
$'ALT + Alt_R\tStart dictation (push-to-talk)' \
$'ALT + Alt_R\tStop dictation (push-to-talk)'; do
grep -Fq "$binding" <<<"$dictation_output" || fail "dictation bindings load for any selected backend" "$binding"
done
pass "dictation bindings require a selection without hardcoding backends"
# Lazydocker is optional on new installs, while existing installs keep the hotkey.
lazydocker_bin="$tmpdir/lazydocker-bin"
+1 -1
View File
@@ -71,7 +71,7 @@ const fs = require('fs')
const defaultItems = menu.parseMenuJsonc(fs.readFileSync(path.join(root, 'default/omarchy/omarchy-menu.jsonc'), 'utf8'))
const guardText = defaultItems.map(item => `${item.when}\n${item.checked}\n${item.disabled}`).join('\n')
const repeated = [...new Set(
(guardText.match(/\$\((omarchy-[a-z0-9-]+)\)/g) || []).map(match => match.slice(2, -1))
(guardText.match(/\$\((omarchy-[a-z0-9-]+(?: [a-z0-9-]+)*)\)/g) || []).map(match => match.slice(2, -1))
)].filter(command => guardText.split(`$(${command})`).length > 2)
assertDeepEqual(
repeated.filter(command => !menu.guardReaders.includes(command)),
+2 -1
View File
@@ -325,6 +325,7 @@ assertDeepEqual(
[
'remove.package',
'remove.ai',
'remove.dictation',
'remove.service',
'remove.development',
'remove.theme',
@@ -336,7 +337,7 @@ assertDeepEqual(
'remove.preinstalls',
'remove.security'
],
'menu orders Remove categories like their Install counterparts, followed by Remove-only categories'
'menu keeps the Remove category order'
)
assert(
defaultById['setup.security.passwordless-sudo'].action.includes('omarchy-sudo-passwordless'),
@@ -9,7 +9,7 @@ const read = name => fs.readFileSync(path.join(root, name), 'utf8')
const indicator = read('shell/plugins/bar/indicators/PasswordlessSudo.qml')
const widget = read('shell/plugins/bar/widgets/Indicators.qml')
const manifest = JSON.parse(read('shell/plugins/bar/widgets/Indicators.manifest.json'))
assert(widget.match(/defaultIndicatorEntries: \[ "PasswordlessSudo", "Dictation"/), 'passwordless sudo is included in the default indicator tray')
assert(widget.match(/defaultIndicatorEntries: \[ "PasswordlessSudo", "ScreenRecording"/), 'passwordless sudo is included in the default indicator tray')
assert(manifest.barWidget.schema.find(field => field.key === 'items').options.some(option => option.value === 'PasswordlessSudo'), 'passwordless sudo is configurable alongside Night Light')
assert(indicator.includes('useActiveColor: true') && indicator.includes('activeColor: Commons.Color.urgent'), 'active passwordless sudo uses the theme danger color')
assertEqual(indicator.match(/activeText: "([^"]+)"/)[1], indicator.match(/inactiveText: "([^"]+)"/)[1], 'sudo keeps the same icon in both states')
+2 -2
View File
@@ -45,8 +45,8 @@ run_invitation_hook
[[ -f $test_home/.local/state/omarchy/done/voxtype-install-invitation ]] || fail "Voxtype invitation records completion"
[[ -f $hook_path ]] || fail "Voxtype invitation keeps its hook installed"
[[ $(grep -c '^notification$' "$log_file") -eq 1 ]] || fail "Voxtype invitation sends one notification"
grep -qx 'exec:omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install' "$log_file" ||
fail "Voxtype invitation attaches the installer to the notification"
grep -qx 'exec:omarchy-menu summon setup.default.dictation' "$log_file" ||
fail "dictation invitation opens the backend installation choices"
grep -q '^systemd-run:' "$log_file" && fail "Voxtype invitation needs no unit to hold an unanswered toast"
HOME="$test_home" PATH="$test_bin:$ROOT/bin:$PATH" TEST_LOG="$log_file" bash "$hook_path"