Mod-* keymap binding on non-english layout in Windows

Hi @marijn,

I didn’t found a similar bug so created a new one. The issue is specific to Windows environment (I tested it on chrome in Mac vs Windows only)

WHEN there is a Mod-* keymap binding (e.g. default Mod-b to apply strong mark)

AND user switch to non-english keyboard layout (e.g. russian in my case)

AND tries to use the shortcut (e.g. default Mod-b to apply strong mark to some text)

RESULT: Nothing happens (binding doesn’t work)

It is reproducible on Windows Chrome even on the basic example: https://prosemirror.net/examples/basic/

I’ve made some investigation and found that possible root cause of the issue is missing check for event.ctrlKey here: https://github.com/ProseMirror/prosemirror-keymap/blob/469840b1e50e2a8ba1463313f05f8aa1a9133539/src/keymap.js#L86

Do you think we can fix this quickly? I can help with a pull request.

Thank you in advance

That’s intentionally not there—I’m not aware of any keyboard layout where holding ctrl is used to create different characters.

Is it different for a Russian layout? What keys are you pressing when typing ctrl-b?

It is not specific to russian layout, but to windows environment. The “b” key is associated with russian “и” and they both have event.keyCode = 66 and different event.key

When I check it on Mac - i press “command” key + “и”. Once event is fired it has metaKey=true. It passes IF statement and is able to match keymap binding by keyCode. This case works brilliant.

When on windows I press “ctrl” key + “и”. The event.ctrlKey=true, but metaKey, shiftKey and altKey properties all set to false. That’s why algorithm can’t match keymap binding by keyCode=66 and skip this branch.

Do I understand correctly that you’re actually pressing ctrl-и (which happens to involve the “b” key, but your layout assigns “и” to it) but in most apps that also toggles bold?

What prosemirror-keymap is trying to do is to use the key meaning that the keyboard layout assigns, as opposed to the physical key label. For example, in a querz layout, z and y are switched compared to querty, and when the user uses such a layout, we want to undo when they press ctrl + the key their layout assigns to z, regardless of whether that has a y on it on the keyboard.

Is it correct that in this case, your expectation is different, and you’d want и to act like b when it comes to ctrl combos?

Yes, in most editors I used such a combination toggles bold. I can rephrase the issue that on windows keyboard user has to switch language to english to be able to use Ctrl + * shortcuts in prosemirror.

Yes. I want a consistent behavior. On Mac “COMMAND + b” and “COMMAND + и” do the same. While on Win keyboard user don’t have COMMAND. Using of CTRL is a standard pattern and “CTRL + b” works fine while “CTRL + и” is ignored.

I think adding separate bindings for ctrl-и is the most reasonable solution then. Are you using your own setup, or a keymap provided by someone else?

The editor is not specific to english and russian audience only. How to scale this approach? What about other languages? What I need is just a binding by keyCode.

I just tried to create a binding for Ctrl + b (instead of Mod + b). And again - it works consistently on Mac for russian and english languages. And on windows notebook - only for english.

Most of keymap bindings are default (https://github.com/ProseMirror/prosemirror-example-setup#documentation). As I mentioned above the issue is reproduced on the basic example https://prosemirror.net/examples/basic/

See below photos of keyboards

That’s the problem—it seems different languages have different conventions for how these things should work. I haven’t encountered the expectation you describe for Russian layouts before yet. The prosemirror-keymap plugin works by mapping based on event.key (the parts that look at .keyCode are just workarounds for browser limitations). It’d be possible to add a feature that tells it to look at key codes, but using that for the default ctrl-b binding would break things for people who use a layout that doesn’t correspond to their physical keyboard.

@marijn thank you very much for your time and help.

I’ve just realized I interpreted the algorithm wrong and finally got your point.

Here are 2 screenshots of event on Mac and Windows when switched to russian and press “Mod-b”. They have different “key”.

Mac:

Windows:

My workaround is to use for specific bindings custom keymap with the following dirty hack:

import {keydownHandler} from "prosemirror-keymap";
import {Plugin} from "prosemirror-state";

export function customKeymap(bindings) {
  const keydownHandlerFunc = keydownHandler(bindings);
  return new Plugin({
    props: {
      handleKeyDown: (view, event) => {
        let result = keydownHandlerFunc(view, event);
        if (result === false && event.code.toLowerCase().indexOf("key") === 0 && event.code.length === 4) {
          const customEvent = {
            key: event.code.toLowerCase().substr(3),
            keyCode: event.keyCode,
            shiftKey: event.shiftKey,
            altKey: event.altKey,
            metaKey: event.metaKey,
            ctrlKey: event.ctrlKey,
          };

          result = keydownHandlerFunc(view, customEvent);
        }
        return result;
      },
    },
  });
}

Thanks again for your patience and help.

One possible other idea would be to add a kludge to the keymap plugin that, for certain whitelisted character ranges (starting with only Cyrillic), also uses the key code (basically adding another || clause to the (event.shiftKey || event.altKey || event.metaKey) condition). I’m not sure if there’d be false positives (language layouts that also produce Cyrillic characters but where users don’t expect this behavior), but even if, they might be too obscure and rare to impact anyone.

Does that sound like it’d help?

I think whitelists - is a very hard way where developer quickly sink in an ocean of particular details. General solution here would be a way to create binding by keyCode i believe.

But anyway your help and patience are invaluable. My company definitely make a donation to ProseMirror project once we start make a money :slight_smile: