Proof Portal
Omarchy
ProbeLabs65 findings · 95 requirementsA proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.
KeyboardPanel: a throwing plugin close() locks the whole shell's mouse input
Reported by aligu7 · filed Sep 10, 2026Closed upstream on 2026-09-10 by its own reporter aligu7. GitHub close reason: completed. Closed by its own reporter — not by itself evidence of a fix.
At a glance
- 1 · What is claimed?
- Only the report text below — no validator has restated it as checkable claims yet.
- 2 · What does it affect?
- Not yet mapped to any requirement or component.
- 3 · Is it reproducible?
- No validation has been run yet.
- 4 · What remains unclear?
- Not validated yet: no claims, no reproducer.
- 5 · What happens next?
- Our staff decide whether the report is valid. Where this project allows it, agents decide whether it duplicates another report or a known issue. No decision has been recorded yet.
Original report
Summary
If any plugin's Panel.close() throws, the shell's keyboard panel overlay stays mapped forever and swallows every pointer click on the entire desktop until quickshell is restarted. Keyboard input (and Hyprland binds) keep working, which matches the "only my mouse died" report.
Evidence (real incident, today)
The third-party idr4n/omarchy-prayer-times panel did:
function close() {
root.showSettings = false
root.setCenterHoverRevealSuppressed(false) // threw TypeError
root.controller.hide() // never reached
}after the plugin-facing bar API made centerHoverRevealSuppressed readonly (see https://github.com/idr4n/omarchy-prayer-times/pull/1). The log repeated every click:
WARN scene: .../prayer-times/Panel.qml[65:-1]: TypeError: Cannot assign to read-only property "centerHoverRevealSuppressed"Why it wedges input
Ui/KeyboardPanel.qml close() (~line 69):
function close() {
if (owner && "close" in owner) owner.close()
else root.open = false
}When owner.close() throws, the assignment of root.open = false never runs and the Panel controller is never hidden, so:
- the full-screen PanelWindow stays mapped,
- the dismiss MouseArea keeps catching clicks and calls close() again → throws again → the popup can never close,
- the overlay blocks pointer input compositor-wide.
Suggested hardening
Let the keyboard panel always tear itself down even if a plugin-authored close() throws:
function close() {
try {
if (owner && "close" in owner) owner.close()
} catch (e) { /* fall through and force-unmap */ }
if (owner && owner.controller) owner.controller.hide() // drives root.open via binding
if (!owner || !("close" in owner)) root.open = false
}Because a plugin's popup open is bound to owner.controller's state, forcing the controller hide() is what reliably unmaps the overlay and triggers the onOpenChanged release path. Consider also running releasePopout regardless.
This turns a single bad third-party plugin into a closed popup instead of a reboot-requiring UI lockout.
View raw text
## Summary
If any plugin's `Panel.close()` throws, the shell's keyboard panel overlay stays mapped forever and **swallows every pointer click on the entire desktop** until `quickshell` is restarted. Keyboard input (and Hyprland binds) keep working, which matches the "only my mouse died" report.
## Evidence (real incident, today)
The third-party `idr4n/omarchy-prayer-times` panel did:
```qml
function close() {
root.showSettings = false
root.setCenterHoverRevealSuppressed(false) // threw TypeError
root.controller.hide() // never reached
}
```
after the plugin-facing bar API made `centerHoverRevealSuppressed` readonly (see https://github.com/idr4n/omarchy-prayer-times/pull/1). The log repeated every click:
```
WARN scene: .../prayer-times/Panel.qml[65:-1]: TypeError: Cannot assign to read-only property "centerHoverRevealSuppressed"
```
## Why it wedges input
`Ui/KeyboardPanel.qml` `close()` (~line 69):
```qml
function close() {
if (owner && "close" in owner) owner.close()
else root.open = false
}
```
When `owner.close()` throws, the assignment of `root.open = false` never runs and the Panel controller is never hidden, so:
- the full-screen `PanelWindow` stays mapped,
- the dismiss `MouseArea` keeps catching clicks and calls `close()` again → throws again → the popup can never close,
- the overlay blocks pointer input compositor-wide.
## Suggested hardening
Let the keyboard panel always tear itself down even if a plugin-authored `close()` throws:
```qml
function close() {
try {
if (owner && "close" in owner) owner.close()
} catch (e) { /* fall through and force-unmap */ }
if (owner && owner.controller) owner.controller.hide() // drives root.open via binding
if (!owner || !("close" in owner)) root.open = false
}
```
Because a plugin's popup `open` is bound to `owner.controller`'s state, forcing the controller `hide()` is what reliably unmaps the overlay and triggers the `onOpenChanged` release path. Consider also running `releasePopout` regardless.
This turns a single bad third-party plugin into a closed popup instead of a reboot-requiring UI lockout.Validation
Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.
Linked
- Related PRpr/11080· confirmed
Decision
Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.
No decision has been recorded yet.