Wrong shortcut for indentation on German keyboard still not fixed

I’m having the same issue with a german keyboard on Mac. “Cmd + ,” should open an unordered list. In some instances it opens the Anki preferences instead. I realised that this doesn’t happen as long as i avoid clicking on the Anki main-window when adding cards and instead only use the Anki Add-window. However if i click on the main-window once, “Cmd + ,” will open Anki preferences until i close the Add-window and open another Add-window by clicking on “Add” in the main-window. Sorry if this is confusing but i don’t know the correct terminology for the two windows.

1 Like

@nick5 & @Ankiuser7: Those are exactly the problems I mentioned above :+1:
@Anon_0000 Cheers! If you need any more information (e.g., effects of a shortcut pressed), or if you write a fix and it has to be tested on a Mac, I will happily do that. Just hit me up.

2 Likes

I got my problem sorted out and want to give a little bit more info here into what I did so far.

Details
  1. First, we know that not all keyboard shortcuts are working in the add cards dialog.
  2. E.g. on my machine (german nonstandard qwertz keyboard) the intendation shortcut doesn’t work.
  3. So I looked at the editor code ts/editor/editor-toolbar/BlockButtons.svelte, which defines the shortcut like so:
    const outdentKeyCombination = "Control+Shift+,";
    function outdentListItem() {
        if (getListItem(document.activeElement!.shadowRoot!)) {
            execCommand("outdent");
        } else {
            alert("Indent/unindent currently only works with lists.");
        }
    }

    const indentKeyCombination = "Control+Shift+.";
    function indentListItem() {
        if (getListItem(document.activeElement!.shadowRoot!)) {
            execCommand("indent");
        } else {
            alert("Indent/unindent currently only works with lists.");
        }
    }

    onMount(() => {
        registerShortcut((event: KeyboardEvent) => {
            preventDefault(event);
            indentListItem();
        }, indentKeyCombination);
        registerShortcut((event: KeyboardEvent) => {
            preventDefault(event);
            outdentListItem();
        }, outdentKeyCombination);
    });
  1. We can see it calls registerShortcut() which is defined in ts/lib/ts/tslib/shortcuts.ts.
  2. This then calls check(event) which in turn calls checkKey().
  3. This function initially looks like this:
function checkKey(event: KeyboardEvent, key: number): boolean {
    // avoid deprecation warning
    const which = event["which" + ""];
    return which === key;
}
  1. but I modified it to show me more info (if anki is run from the terminal and the add cards dialog is opened, pressing any key will log info):
function checkKey(event: KeyboardEvent, key: number): boolean {
    // avoid deprecation warning
    const which = event["which" + ""];
    console.log("############## which === key", which === key);
    console.log("############## which", which);
    console.log("############## key", key);
    console.log("############## event.key", event.key);
    console.log("############## event.ctrlKey", event.ctrlKey);
    console.log("############## event.shiftKey", event.shiftKey);
    console.log("##############");
    return which === key;
}
  1. Here’s example output of it (I pressed the Control key.):
JS info /_anki/js/editor.js:97041 ############## which === key false
JS info /_anki/js/editor.js:97042 ############## which 17
JS info /_anki/js/editor.js:97043 ############## key 84
JS info /_anki/js/editor.js:97044 ############## event.key Control
JS info /_anki/js/editor.js:97045 ############## event.ctrlKey true
JS info /_anki/js/editor.js:97046 ############## event.shiftKey false
JS info /_anki/js/editor.js:97047 ##############
  1. Altough event.which is deprecated it still correctly shows 17, which corresponds to the Control key. It doesn’t seem to work with Shift+. though (which on my keyboard produces a :):
JS info /_anki/js/editor.js:97041 ############## which === key false
JS info /_anki/js/editor.js:97042 ############## which 16
JS info /_anki/js/editor.js:97043 ############## key 84
JS info /_anki/js/editor.js:97044 ############## event.key Shift
JS info /_anki/js/editor.js:97045 ############## event.ctrlKey false
JS info /_anki/js/editor.js:97046 ############## event.shiftKey true
JS info /_anki/js/editor.js:97047 ##############
JS info /_anki/js/editor.js:97041 ############## which === key false
JS info /_anki/js/editor.js:97042 ############## which 186
JS info /_anki/js/editor.js:97043 ############## key 66
JS info /_anki/js/editor.js:97044 ############## event.key :
JS info /_anki/js/editor.js:97045 ############## event.ctrlKey false
JS info /_anki/js/editor.js:97046 ############## event.shiftKey true
  1. 186 corresponds to ; not :. But, those keys are on the same key on an us qwerty keyboard (also see this thorough keyboard info).

Conclusion

After searching a bit on the internet I found that that’s a common problem with web apps and that there’s a few workarounds:

  1. Don’t use special characters in shortcuts (like /).
  2. Provide alternative shortcuts if 1. cannot easily be changed (without upsetting users).
  3. Allow users to customize shortcuts.
  4. Maybe use a library like Mousetrap (in conjunction with the points above).

But apparently it’s not really possible to just make the current shortcuts work on all layouts.

Maybe we can convince dae to accept alternative shortcuts or to integrate the addon for every shortcut anki has.

2 Likes

sounds great

Thanks @Anon_0000 for all the work you did put into this!

Correct my if I’m wrong, but your debug tool proved that upon pressing Shift + ., which on our German QWERTZ keyboard should produce :, the Anki checkKey function recognizes the character code 186, which corresponds to ; instead of the : we want to have Anki recognize.

As you correctly pointed out, and we already stated earlier in this thread, the which property of KeyboardEvent is deprecated. We shouldn’t use this anyways.
Now, to have the keyboard shortcuts work like they are labelled, we could change the logic of the checkKey function to use the key property, which need recognizes the Shift + . as :, and change the KeyCombination variables accordingly.

I tried it with this dummy and switched keyboard layouts. Upon pressing Shift + . on my German QWERTZ keyboard, event.key recognizes :, but it returns > when I switch to an US keyboard.
This way the labels would all be correct and all keyboard shortcuts would work (but those that have system shortcuts assigned, see above), be it that a keyboard combination has a weird placement on a special layout.

In a next step, if desired, we could have the user set their keyboard layout in settings (maybe prompt upon setting up Anki initially), and adjust keyboard shortcuts and labels accordingly, to have meaningful keyboard shortcuts (i.e., have intend and outdent or superscript and subscript on two neighboring keys for every layout, which is not the case at the moment).

True. On an american leyout this is the same key, so it makes sense why it would work on us keyboards.

I think the issue that I faced is that “special” characters have different postitions based on different keyboard layouts (both, for other languages as well as when the layout itself is non standard for that language). So even if event.key recognizes the key, there will be shortcuts that just cannot be typed in depending on the keyboard layout. As far as my research and testing goes, the only way to solve that is to change the shortcuts to use mostly keys that every layout has or to allow shortcut customization.

Reading this now makes me think I should have changed the shortcut during testing. E.g. instead of Control+Shift+. I should have tryed Control+:. I know that this is essentially the same on my german layout. But it should be different on other layouts and might allow us to use event.key for every layout. I didn’t think about testing this but will do so once I’ve got a bit of free time.

I think being able to change keyboard shortcuts is still the better way, since then the user can be certain that every relevant shortcut works for their language and layout.

Besides: changing keyboard layouts should be the job of the OS. From a user perspective, they expect that the key they see on their physical keyboard is the one they actually type. If you were to change the layout e.g. from german to us english, then this wouldn’t be the case anymore (no matter if done from the OS level or theoretically within anki).

1 Like

I see your point. We should at least change them in a way that most keyboards have those keys.

This could be it! As far as I can tell, event.key won’t tell you Shift+. was pressed, but only that : was pressed. So if you were so kind to test this, as soon as time allows, and we’d be sure that event.key in a first step allows us to recognize the key that was actually pressed, we could use that for every layout to begin with.

Well, after reading this I thought “True”, yet again, I just tested and this doesn’t seem to be the case for macOS. Most prominent example: My system language is set to German, and my “main” keyboard layout is set to German QWERTZ. Now, pressing ⌘ Y in Safari brings up my search history, while ⌘ Z is the undo action. When I switch to US QWERTY keyboard, Safari still labels “Search History” as ⌘ Y in the menu bar. And the search history opens when I use ⌘ Z (i.e., the button in between T and U), as this acts like a Y when using the QWERTY layout.
This behavior is mostly* what Anki is doing right now, but macOS’ labels are labelled to a German keyboard layout (for me right now), and Anki’s labels are labelled to an US International keyboard layout.
(*But, as you can see in my Jan 17 post, this is not true for all shortcuts.)

That is, the app (in this case Safari) doesn’t seem to care what keyboard layout you have turned on right now, but rather what’s your standard layout. And this seems to be would be good starting point for Anki as well. In an ideal world, everything would work for people switching around keyboard layouts all the time (especially since Anki is widely used for language-learning). But for starters, we should allow people to use their standard layout without hassle.

1 Like

While we are at it: I just noticed that Anki provides a second shortcut for entering/exiting fullscreen mode. We should, for macOS, remove the custom one (first one in screenshot), as the latter (fn F) is a system standard for full screen on Mac.
I propose adding ⌘ + and ⌘ - for Zoom In and Zoom Out, respectively. I am using the mouse to scroll quite often, and would love to have those self-suggesting short cuts assigned as well.
I am happy to do write a commit myself as soon as I find the time for this after my tests in February. If you have any seconds thoughts regarding Full Screen and Zoom, let me know!

1 Like

Another one: When editing a note, F7 is supposed to be Apply Text Color, whereas the corresponding button displays the last used text color, e.g., orange, c.f.:

SCR-20250205-kmdc

I have a set of multiple custom colors. Pressing F7 for me always applies the first entry of a custom color, rather than the one last used, e.g. it will apply green even though the button displays orange. This does not seem to be the desired behavior.

1 Like
Request from @tommylenz

With a bit of a delay I tested it just now.

Event key recognizes event.key :, event.ctrlKey true and event.shiftKey true when pressing Control+Shift+. on my german layout. Anki uses which which recognizes 186 (;).

Test Code Snippet

I coded a little code snipped that should hopefully work on several layouts. Would be great if you all could test it.

How to Test

  1. Open Anki.
  2. Create a new Basic Card.
  3. Paste the js below into the front template.
  4. Select the input field and try the keys.

The Code

{{Front}}

<br>

<!--
  --  Create Input Field
  -->
<input placeholder="Click here, then press down a key." size="40" />

<!--
  --  Create Field that shows pressed Key
  -->
<p id="log"></p>




<script>
	/*
	 *  Setup logging
	 */
	var input = document.querySelector("input");
	var log = document.getElementById("log");

	input.addEventListener("keydown", log_key);

	function log_key(event) {
		/*
		 *  event.code gives a code value of the physical key. But if
		 *  the user presses "Z" on a german layout, "KeyY" is returned.
		 *  This can be solved by using .getLayoutMap().
		 *  Also see: https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event
		 *
		 *  The keys that do change meaning are called "writing system
		 *  keys". That means we only need to get the correct value for
		 *  those "writing system keys", as the other keys (like Tab)
		 *  will be the same on any layout.
		 *  Also see: https://www.w3.org/TR/uievents-code/#key-alphanumeric-writing-system
		 */
		var key_code = event.code;
		var keyboard = navigator.keyboard;
		var actual_key_pressed = "–– Not assigned. Did the code run? ––"
		var needs_conversion = false;
		
		/*
		 *  First check if the key is one that exists on all layouts.
		 *  If not, then mark the key for needs_conversion.
		 */
		switch (key_code) {
			case "Tab":
			case "CapsLock":
			case "ShiftLeft":
			case "ControlLeft":
			case "MetaLeft":
			case "AltLeft":
			case "Space":
			case "AltRight":
			case "MetaRight":
			case "ContextMenu":
			case "ControlRight":
			case "ShiftRight":
			case "Enter":
			case "Backspace":
				actual_key_pressed = key_code;
				break;
			default:
				actual_key_pressed = "Not a standard key. Maybe"
				                   + " a writing system key?";
				needs_conversion = true;
		}
		
		/*
		 *  Show the pressed key. This is the correct key for
		 *  the users keyboard layout.
		 */
		log.textContent = `${actual_key_pressed}`;
		
		if (needs_conversion) {
			keyboard.getLayoutMap().then((keyboardLayoutMap) => {
				actual_key_pressed = keyboardLayoutMap.get(key_code);
				
				/*
				 *  In case something still is not recognized,
				 *  this code will be used.
				 */
				if (actual_key_pressed == null) {
					actual_key_pressed = "Not a writing key?"
				}
				
				/*
				 *  Show the pressed key. This is the correct key for
				 *  the users keyboard layout.
				 */
				log.textContent = `${actual_key_pressed}`;
				
			});
		}
		
		/*
		 *  TODO: only use `log.textContent = `${actual_key_pressed}`;`
		 *        once to prevent flashing. Probably best to create
		 *        functions for the various steps above and just return
		 *        actual_key_pressed.
		 */
	}
</script>

Success

  • On my nonstandard german keyboard this properly prints < and >, which are accessible by pressing the key combo fn+L and fn+Shift+L.
  • Stuff like Tab, Space, CapsLock ect are recognized.
  • If I press z on a german keyboard, I actually get z instead of y.

Limitations

It uses an experimental function which exists in chrome and android webview, but e.g. not in firefox. This alone is probably a deal breaker, so needs some sort of solution.

3 Likes

Hey Anon_0000,

I’ve been very busy with my studies, but I just saw the incredible effort you put into this issue. Thank you so much for all your help!

Your script works on my Windows-Machine with only one key not working:
The “>”- Key (fn+Shift+L) is being displayed as “<”.
Everything else works exactly as you described.

I will post a second answer when I get to my Mac!

2 Likes

You’re right, same happens to me. I must have somehow missed it yesterday. I believe that that’s the correct behavior though because the > key actually is on the < key (just accessed while holding shift).

But maybe that’s not desired? The way I see it is like this:

  • The code needs to correctly identify the key pressed on the keyboard and is has to work for every layout. E.g. pressing kbd>z on a german layout should return z, not y.
  • The shortcuts in the UI might need to be adjusted or better yet they should be adjustable.
  • To be adjustable, the user needs to see the actual keys pressed on their layout, because everything else would be confusing.

I’d like to get the correct keys for every layout. And maybe we can implement it into anki in the future.

Also: @Danika_Dakika , I think you said you’re using a turkish layout. Could I trouble you to test the code too if you have some time on your hands? If the experimental function works, maybe I can figure out how it works and implement a solution that isn’t based on experimental functions that might disappear in the future.

1 Like

I can test it, but (1) I haven’t been keeping up with the thread, so you might need to point me directly at what you want me to test, and (2) it will be at least mid-week before I have a chance to work on it. :sweat_smile:

2 Likes

I’d like you to try the code I provided here: Wrong shortcut for indentation on German keyboard still not fixed - #30 by Anon_0000.

If you type a letter, number, other key like e.g. Space or Shift, then the card should show you the key that you pressed. E.g. I have the * key on my + key. So if I press * it should show + had been pressed (because that’s the actual key on my layout that had been pressed).

I’m still not convinced this is optimal – but if I at least know that the code works on other layouts too, then that gives me a good starting point.

No problem, take your time! Worst case I’ll ask you to reopen this topic when it closes. It’s not urgent for me as I do not even use ankis shortcuts most of the time – but this problem starts to captivate me, so now I want to try to solve it in the comming months.

(And I know I somewhat created a bit of pressure on your end pinging you like that – if you wanted to refuse that would be totally fine!)

1 Like

Hey @Anon_0000, thanks for the JS snipped, I tried this with German, English US International, and Unicode Hex layouts (the three that I am using), and it works fine. But as you pointed out, the JS Keyboard API is experimental and not supported by Firefox or Safari, which I guess is a deal-breaker. Should this be added and be made available on all major platforms, we could use this in the future.

In the next days, I will take a look at the Anki code and try to figure out whether we can at least make use of event.key. I have never before worked with the Anki code, and it looks like a complex repo to me, but I will see what I can do.

1 Like

Thanks for your tests!

Not really, actually. Ankiweb doesn’t share the code with Anki, so it doesn’t matter if non-chromium browsers would support it. The issue is that if anki implements their shortcut logic using experimental flags and chromium drops support for that flag, then keyboard shortcuts are broken on Anki.

I was hoping to find out how this experimental flag determines the actual keys and write custom code that anki could use instead of that experimental flag. I didn’t look into it yet, though.

1 Like

I see. I understand where you are coming from, maybe we can find out how to do it ourselves or use some third party library that does the same stuff the Keyboard API is doing. We should try to keep it as simple as possible.
If JS were to adapt the Keyboard API, switching to this seems like a natural option, as this will increase stability and maintainability.

True. I didn’t try 3rd party libs yet, as I don’t know what the requirements are for those (maybe dae has some sort of policy regarding 3rd party libs in Anki).

FYI: My workload increased quite heavily over the last few days and will likely stay that way for a few months – I won’t be able to test / develope much because of that.

But I’m looking forward to your insights and progression regarding this matter.

1 Like

@Anon_0000 Alright, I will keep you posted, trying to make it as easy to follow as possible. Feel free to chip in as much as you want.

Adjusting keyboard shortcuts for indent/outdent
For me, I have just adjusted this line of code for the outdent keyboard shortcut and the line 52 of the same file for the indent shortcut to Shift + Tab and Tab, respectively. It works like a charm on my system (see Jan 15 post for system info), on keyboard layouts German, English US and Unicode Hex. The previous shortcuts Control+Shift+, and Control+Shift+. did not work at all for German layout (see above in this thread for the explanation).
Same goes for the other keys, e.g. changing the key combination of superscript in this line to Control + O allows me to superscript something by pressing ⌘ + O.

It seems to me that we could at least change the intent and outdent shortcuts to Tab and Shift + Tab , respectively, as those are functional keys (as per W3 UI Events Proposed Recommendation), and as such common to just about all keyboard layouts. This would “repair” one shortcut that is currently displayed as a tooltip but is not working on macOS at all.
I wrote a commit for this: PR on GitHub.

Going forward
We’d need to decide how we want to address this issue. I tried to look for a solution that works as the Keyboard API does, but neither JS nor Qt seem to have a solution that works without any workarounds or pitfalls.
Using customisable shortcuts would be a better user experience, but will take time to implement and is more difficult to maintain. Even then, we’d have to agree to the default ones.

On the other hand, we could just use the Keyboard API. It seems to be on track to become part of JS. It is implement in full in Chromium since mid-2018, and I don’t see any reason it will be removed again. Yet, I am no expert in W3C stuff at all. I guess, if they were to remove the API from Chromium again, we’d need to patch it out, and revert back to some other solution. Sounds like an effort, albeit an unlikely one.

If this is not what we want to do, I’d say let the user select a keyboard layout in the settings, like they set a language right now. Based on this “main” keyboard layout, the tool-tipps would display shortcuts that are actually possible to type using the selected keyboard layout.
We’d need to create a list for every layout, falling back to the default list if no special shortcut is listed. But this would not make it worse than it is right now, that is, worst-case-scenario the shortcut is not possible to type. It would only improve the users situation if a faulty shortcut is reported and fixed by listing a special keyboard-layout-based shortcut. In my opinion, this would be highly maintainable, as upon a faulty shortcut being reported, one would just have to add an entry to a list for the specific keyboard layout (e.g., a JSON file).

What do you think?

1 Like

On the Turkish-Q layout, it shows me the character I typed in the input box. Below the input box –

  • It flashes up the “Not a standard key. Maybe a writing system key?” message whatever I press.
  • For “unaltered” keys – it shows me the same char as in the input box.
  • For “altered” keys (combined with Shift, Alt, etc.) – it shows me the altering key and then whatever Turkish-Q char it would have typed if it wasn’t altered. [In your example it sounds like you were expecting it to show me what was on the physical English keyboard, and it’s not that.]

Windows 11, if that helps to know!

2 Likes