Skip to content

Record punctuation, keypad and right-side modifier keys under evdev names - #158

Draft
JRufer wants to merge 1 commit into
masterfrom
claude/funny-clarke-q00g34
Draft

JRufer wants to merge 1 commit into
masterfrom
claude/funny-clarke-q00g34

Conversation

@JRufer

@JRufer JRufer commented Sep 25, 2026

Copy link
Copy Markdown
Owner

Problem

The shortcut recorders named any key they had no rule for after the character it typed. Punctuation was saved as KEY_., KEY_,, KEY_/, KEY_; and so on. No hotkey backend reports those names, so a binding or TTS stop key that included one was saved but could never fire.

The same mapping had two more bugs:

  • Right-side modifiers: right Ctrl, Alt, Shift and Super were saved under the left-side names. KeyMatcher compares names exactly, so the binding fired only from the left key.
  • Keypad: Numpad1 was saved as KEY_1 (the top-row key) and NumpadEnter as KEY_NUMPADENTER.

There were also three copies of mapBrowserKeyToEvdev: in wizard-data.ts, in HotkeysTab.svelte and in TtsTab.svelte. The TTS copy was missing the Key[A-Z] rule, so a letter stop key was saved as KEYV.

Changes

  • src/lib/keys.ts (new): one mapBrowserKeyToEvdev, used by the wizard (through the existing re-export in wizard-data.ts), Settings → Hotkeys and the TTS stop key. It maps by KeyboardEvent.code, which is layout-independent:

    • punctuation: Period→KEY_DOT, Comma→KEY_COMMA, Slash, Semicolon, Quote→KEY_APOSTROPHE, Backquote→KEY_GRAVE, Minus, Equal, BracketLeft/BracketRight→KEY_LEFTBRACE/KEY_RIGHTBRACE, Backslash, IntlBackslash→KEY_102ND
    • right-side modifiers → KEY_RIGHTCTRL, KEY_RIGHTALT, KEY_RIGHTSHIFT, KEY_RIGHTMETA
    • keypad → KEY_KP0…KEY_KP9, KEY_KPDOT, KEY_KPENTER, KEY_KPPLUS, KEY_KPMINUS, KEY_KPASTERISK, KEY_KPSLASH
    • PrintScreen→KEY_SYSRQ, ContextMenu→KEY_COMPOSE, plus navigation, lock and media keys

    When an event has no code, it falls back to key for modifiers, letters and digits.

  • tests/svelte/keys.test.ts (new): reads keymap::NAMES from crates/voxctrl-hotkeys/src/win_keys.rs and checks that every name the recorder emits is in it. The Linux backends take their names from the evdev crate, which uses the same vocabulary. Also covers Shift and non-US layouts, AZERTY, right-side modifiers, NumLock-off keypad keys, and events with no code. Cases are added to wizard-data.test.ts too.

  • Load-time migration: new voxctrl_config::migrate_legacy_key_name(s).

    • raw_to_binding repairs bindings.toml keys in memory, and the next save writes the corrected names back.
    • AppConfig::load repairs tts.stop_key and rewrites the config file. This replaces the separate KEY_ESCAPE → KEY_ESC migration.
    • Names it repairs: US-layout punctuation, shifted or not (KEY_./KEY_> → KEY_DOT, …), KEY_*, KEY_NUMPADENTER, KEY_PRINTSCREEN, KEY_CONTEXTMENU, KEYA…KEYZ, and KEY_ESCAPE.
  • Updated the win_keys.rs doc comment, which described the old left-side folding.

Notes and limits

  • The migration assumes a US layout. The old names record the character typed, not the key's position. On another layout, a repaired binding may move to a neighbouring key; before this change it never fired at all. Characters outside the US table, such as KEY_Ö, are left as they are and have to be recorded again.
  • Not repairable: KEY_1 could be the top-row 1 or keypad 1, and is also a valid name in its own right, so an old keypad-digit binding stays on the top row. KEY_RIGHTCTRL was never saved, so there is nothing to migrate for right-side modifiers.
  • Behaviour change: a combination recorded from now on with right Ctrl fires only from right Ctrl, matching evdev, X11 and Windows. Portal registration is unaffected, because the accelerator translation treats both sides as CTRL.
  • Portal gaps: in portal mode, the Rust keysym table (trigger.rs) has no entries for keypad keys other than Enter, or for KEY_SYSRQ, KEY_COMPOSE and KEY_102ND. The recorder will reject those there. That limitation already existed and is left for a follow-up.
  • F13–F24 are valid evdev names, but the Windows table stops at F12, so the cross-check test only covers F1–F12.

Testing

  • npm run test:unit: 253 passed
  • npm run check: 0 errors. The 453 warnings (Tailwind @apply) were already there.
  • cargo test -p voxctrl-config -p voxctrl-hotkeys -p voxctrl-routing: all pass, including the new migration tests
  • cargo clippy on those three crates: no warnings on the new code

🤖 Generated with Claude Code

https://claude.ai/code/session_01Nv8xwn4yhHm9dqmz6fuBtv


Generated by Claude Code

…ames

The key recorders named any key they had no rule for after the character
it typed, so punctuation was saved as KEY_., KEY_;, KEY_/ ... and a
shortcut or TTS stop key containing one could never fire. Right Ctrl/Alt/
Shift/Super were folded onto the left names, so they fired only from the
left key, and the keypad was saved as the top-row digits.

- Move the three copies of mapBrowserKeyToEvdev (wizard, Settings ->
  Hotkeys, TTS stop key) into src/lib/keys.ts and map by
  KeyboardEvent.code: punctuation, keypad, right-side modifiers, PrintScreen,
  ContextMenu, IntlBackslash and media keys. This also fixes the TTS stop
  key recorder, which saved letters as KEYV.
- Test every emitted name against keymap::NAMES in win_keys.rs.
- Migrate the broken names on load (bindings.toml and tts.stop_key),
  folding the existing KEY_ESCAPE -> KEY_ESC migration into it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nv8xwn4yhHm9dqmz6fuBtv
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants