Let plugins declare keyboard shortcuts; have the shell bind them with confirmation and conflict handling #9631
falser101
started this conversation in
Suggestions
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Summary
Plugins should be able to declare default keyboard shortcuts in
manifest.json. The shell (andomarchy plugin enable) should own binding, confirmation, conflict detection, and rebinding. Plugins should not edit~/.config/hypr/bindings.lua.This is a plugin-contract request, not a request for another settings overlay.
Problem
Global hotkeys belong to Hyprland (
o.bind→hl.bind). First-party panels already have hardcoded defaults:Third-party plugins have no equivalent. Today they either:
bindings.luathemselves (Todoist, Keysmith, and similar).That leaks lifecycle into every plugin: disable/remove can leave dead binds, updates can clobber user remaps, Super+K only sees whatever happened to land in Hyprland, and conflict checking is duplicated poorly.
Two related gaps in the current contract:
shortcutsfield.schemaVersion: 1has kinds, entry points, andbarWidget.schema/settingsForm, but nothing for global hotkeys.~/.config/omarchy/extensions/omarchy-menu.jsonc, which the plugin manager cannot write. Modifier Keys has to ship a separateinstall.shfor that.omarchy plugin addalready warns that plugins are unsandboxed and lands them disabled.omarchy plugin enableis a silent IPC call (enablePlugin). That is the right moment to confirm shortcuts, and it currently does not.Proposal
Keep the existing split:
Manifest
Unknown extra fields already survive validation, so this can stay on
schemaVersion: 1with a new optional object:{ "schemaVersion": 1, "id": "io.github.example.todoist", "kinds": ["bar-widget"], "entryPoints": { "barWidget": "BarWidget.qml" }, "shortcuts": [ { "id": "toggle", "label": "Open Todoist", "default": "SUPER + CTRL + Y", "action": "toggle" } ] }actionshould be a closed set that maps to existing shell IPC, for exampletoggle,summon,hide,call:<method>. Do not let a plugin’s default shortcut run an arbitrary shell command.omarchy plugin validateshould check: unique ids,o.bind-compatible key syntax, and a legal action.This is for global hotkeys only. In-panel keys (Escape, j/k, Enter) stay inside the plugin’s
PanelKeyCatcher.When to confirm
Confirm on enable, not add. Add already asks “do you trust this unsandboxed repo?” and leaves the plugin off so the user can read it. Binding a key is a second, separate grant.
Interactive
omarchy plugin enable(and Setup → Plugins → Enable) would:SUPER + CTRL + Yis already Clipboard manager.hl.unbindfirst, then bind — same rule as editingbindings.luatoday), or skip.enablePlugin.--yes/ non-interactive must not steal keys. Suggested policy: apply defaults only when free; skip conflicts and print them.Persistence and binding
User choices belong in
shell.json(or a shell-owned sidecar), never back in the plugin checkout:{ "plugins": [ { "id": "io.github.example.todoist", "shortcuts": { "toggle": "SUPER + CTRL + T" } } ] }Plugin updates may change
default. Confirmed or remapped combos stay put.The shell should generate a file it owns, for example
~/.local/state/omarchy/plugin-binds.lua, and have Hyprland load it. Do not append to the user’s~/.config/hypr/bindings.lua. That file is the user’s override surface; a generated table can be rewritten on enable/disable/remove.Apply live as well so a compositor restart is not required. Super+K then sees plugin shortcuts because they are real Hyprland binds.
Disable/remove unbinds and drops the record. Clone routing should follow today’s IPC identity (
resolveEnabledId): callers keep using the built-in id, the enabled clone receives the shortcut.Conflict detection
Compare against:
hyprctl binds/omarchy menu keybindings --print)Normalize before comparing:
SUPER + CTRL + YvsCTRL + SUPER + Y,code:vs keysym, and loop-generated binds such as workspaceSUPER + 1..9. #7627 is relevant:--printcurrently does not use the same syntax aso.bind/hl.unbind.Why this should not be a third-party plugin
A community “shortcut manager” can get about 80% of the way there: a
servicecan watchpluginRegistry, read extrashortcutsfields, show an overlay, and write a marked block inbindings.lua(Keysmith already does the last part).It cannot:
omarchy plugin enableor Setup → Enablerequirea generated file (hyprland.luaonly loads the stock user modules)omarchy-menu.jsoncSo this belongs in the plugin contract, next to
barWidget.schema, not as another overlay people have to install first.Related work
Closest existing proposals, none of which implement declarative shortcuts + confirm + conflict UI:
menuItemin the manifest; the shell merges a launcher and does not mutate the user’s menu file. Same ownership model, different surface.input.luaalone. Same persistence shape asplugin-binds.lua.--printvso.bindsyntax, needed for reliable conflict checks.Suggested first slice
shortcutson the manifest + validate.shell.json.plugin-binds.lua(or equivalent) loaded by Hyprland; unbind on disable/remove.--yesnever steals; print skipped conflicts.Menu contribution (
menuItem/ Setup rows) is useful and #7946 already covers it. Config widgets can keep usingbarWidget.schema. Neither should block shortcuts.Happy to adjust the manifest shape if a
hyprlandkind from #8396 is the preferred place for binds instead of a top-levelshortcutsarray.All reactions