A compositor resolves a bind's keysym on the first level of the active
layout. Layouts that put punctuation higher up therefore cannot reach it
at all: de:neo has bracketleft on layer 4 and plain de on AltGr, so
Mod+bracketleft is a bind that can never fire. The editor recorded and
stored it anyway and gave no sign that anything was wrong (#3585).
`dms keybinds keymap` now reports what the seat's xkb keymap puts on the
first level of every physical key, and the editor says so instead of
letting a dead bind through.
The keymap a compositor hands out writes its keysyms as hex, so only the
range the keymap package can name back is usable for the comparison.
That is the ASCII range, which is where the problem lives, and the
command reports the vocabulary alongside the keys so the caller can tell
"the layout cannot reach this" from "we could not name it" and stay
quiet in the second case.
* feat(keybinds): redesign cheatsheet with category navigation, M3 styling, and click-to-execute
- Add category sidebar with Material Symbols icons and badge counts
- Move search field to top of sidebar with Ctrl+F shortcut
- Implement 2-column masonry layout for keybind items
- Deduplicate duplicate actions and stack multi-key combos vertically
- Collapse consecutive workspace keybindings (1-9) into range entries
- Add media & hardware icons for volume, mic, brightness, and PrtSc keycaps
- Preserve Niri window filter properties and distinguish same-app Alt-Tab binds
- Enable click-to-execute for keybind actions across supported compositors
* fix(keybinds): preserve niri action properties and disable execution on range rows
* fix(keybinds): convert niri action properties to CLI flags and disable IPC execution for window switcher binds
* fix(keybinds): pass skip-confirmation as boolean switch in niri executeAction
* fix(keybinds): handle niri spawn-sh actions in executeAction
* fix(keybinds): record numpad keys as KP_* keysyms
The shortcut recorder passed only the Qt key code to xkbKeyFromQtKey and
dropped Qt.KeypadModifier. Since Qt reuses the same Qt::Key_* values for the
numpad and the main row / nav cluster, numpad presses collapsed onto their
twins: numpad-7 became "7" (NumLock on) or "Home" (NumLock off) instead of
"KP_7"/"KP_Home", numpad-+ became "Equal", numpad-* became "8", numpad Enter
became "Return". numpad-5 with NumLock off (Qt.Key_Clear) was missing from the
map entirely, so the capture was silently dropped.
niri and the other providers bind against the xkb KP_* keysym names, so these
tokens never matched the physical key.
Pass the keypad flag through to xkbKeyFromQtKey and map keypad presses to the
KP_* keysyms: KP_0..KP_9 for the NumLock-on digit codes, the navigation names
(KP_Home, KP_End, KP_Up, ...) for the NumLock-off codes, plus the operators
and KP_Enter. Main-row keys are unaffected because they never carry the keypad
modifier.
* fix(keybinds): ignore lock keys while capturing a shortcut
NumLock/CapsLock/ScrollLock are toggles, not useful bind targets. Pressing
NumLock to switch the numpad between its digit and navigation keysyms
(KP_7 vs KP_Home) was captured as the bind itself (e.g. "Super+Num_Lock").
Skip them in the recorder like the other pure modifier keys already are.