Route Super+I through the shell's input reader so unfocused choices stick

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
David Heinemeier HanssonandClaude Opus 5.5 committed 2026-10-10 23:13:38 +09:00
1 parent 1371ea7b5d
commit a494f21ab9
6 files changed
+53 -5

No files matched your search

+5 -1
View File
@@ -7,7 +7,11 @@
set -euo pipefail
if [[ ${1:-} == "cycle" ]]; then
python "$OMARCHY_PATH/default/input-methods/indicator.py" cycle
# The shell's reader retains a choice made while nothing has focus. Without
# the shell, switch directly.
if [[ $(omarchy-shell shell cycleInput 2>/dev/null) != "ok" ]]; then
python "$OMARCHY_PATH/default/input-methods/indicator.py" cycle
fi
else
python "$OMARCHY_PATH/default/input-methods/configure.py" "$@"
fi
+9 -3
View File
@@ -112,12 +112,18 @@ class Indicator:
self.entries = {}
def select_next(self):
state = snapshot(self.bus)
devices = subprocess.run(["hyprctl", "-j", "devices"], check=True, text=True, capture_output=True)
keyboards = json.loads(devices.stdout).get("keyboards", [])
try:
state = snapshot(self.bus)
except GLib.Error:
# Without Fcitx, Super+I still cycles the compositor layouts.
for command in layout_switches(keyboards):
subprocess.run(command, check=True, capture_output=True)
return
methods = state["methods"]
current = self.pending or state["current"] or self.last
state["current"] = current if current in methods else next(iter(methods), "")
devices = subprocess.run(["hyprctl", "-j", "devices"], check=True, text=True, capture_output=True)
keyboards = json.loads(devices.stdout).get("keyboards", [])
following, index = cycle_choice(state, keyboards)
if following is None:
return
+1
View File
@@ -122,6 +122,7 @@ or `omarchy.power`. There is no `bar` target.
| `reloadConfig` | reload shell.json |
| `applyTheme <colorsB64> <shellB64>` | push theme colors + shell.toml |
| `toggleBarTransparency` | flip the bar background between solid and transparent |
| `cycleInput` | switch to the next input method or layout (Super+I); `not running` without the input reader |
| `setPluginEnabled <id> <"true"\|…>` | flip enabled bit (`ok` / `unknown`) |
| `enablePlugin <id> <placementJson>` | enable and place in one mutation |
| `putBarWidget <id> <placementJson>` | place a widget only where absent (`omarchy bar put`) |
+3 -1
View File
@@ -8,7 +8,9 @@ Singleton {
property var state: ({})
function cycle() {
if (watcher.running) watcher.write("cycle\n")
if (!watcher.running) return false
watcher.write("cycle\n")
return true
}
// One reader and pending selection for every monitor's keyboard widget.
+6
View File
@@ -1779,6 +1779,12 @@ ShellRoot {
return "ok"
}
// Super+I goes through the bar's reader, which keeps a choice made before
// any text field has focus until one does.
function cycleInput(): string {
return Commons.InputMethodState.cycle() ? "ok" : "not running"
}
function rescanPlugins(): void {
shell.reloadPlugins()
}
+29
View File
@@ -170,4 +170,33 @@ with patch.object(indicator.GLib, "MainLoop", return_value=loop), patch.object(i
assert call.call_args.args[1] == "SetCurrentIM"
print("ok - a failed compositor query leaves the bar click reader alive for the next request")
keyboards = [{"name": "physical", "layout": "us,dk", "active_layout_index": 0}]
with patch.object(indicator, "snapshot", side_effect=indicator.GLib.Error("no fcitx")), patch.object(indicator.subprocess, "run", side_effect=fake_process):
indicator.Indicator(None).select_next()
assert keyboards[0]["active_layout_index"] == 1
print("ok - the shortcut reader cycles compositor layouts when Fcitx is unavailable")
PY
stubs=$(mktemp -d)
trap 'rm -rf "$stubs"' EXIT
calls="$stubs/calls"
cat >"$stubs/omarchy-shell" <<'SH'
#!/bin/bash
echo "shell $*" >>"$CALLS"
echo "$SHELL_REPLY"
SH
cat >"$stubs/python" <<'SH'
#!/bin/bash
echo "python ${*##*/}" >>"$CALLS"
SH
chmod +x "$stubs/omarchy-shell" "$stubs/python"
CALLS=$calls SHELL_REPLY=ok PATH="$stubs:$PATH" OMARCHY_PATH="$ROOT" "$ROOT/bin/omarchy-input-method" cycle
[[ $(<"$calls") == "shell shell cycleInput" ]] || fail "Super+I goes through the shell's reader" "$(<"$calls")"
pass "Super+I goes through the shell's reader, which retains a choice made without focus"
: >"$calls"
CALLS=$calls SHELL_REPLY="" PATH="$stubs:$PATH" OMARCHY_PATH="$ROOT" "$ROOT/bin/omarchy-input-method" cycle
[[ $(<"$calls") == $'shell shell cycleInput\npython indicator.py cycle' ]] || fail "Super+I switches directly without the shell" "$(<"$calls")"
pass "Super+I switches directly when the shell does not answer"