From 47ca6b9c33581724f19025394630c1adcd341ee9 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Sun, 26 Apr 2026 11:48:05 +0100 Subject: [PATCH] Lexical: Added support for keyCode-based fallback shortcut use Helps in cases where languages like cyrillic may have the relevant key to use but the actual text/.key value is the cyrillic key value instead of the shorcut key we expect. --- resources/js/wysiwyg/services/shortcuts.ts | 70 ++++++++++++++++++---- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/resources/js/wysiwyg/services/shortcuts.ts b/resources/js/wysiwyg/services/shortcuts.ts index 00abe0c6d..2702b5486 100644 --- a/resources/js/wysiwyg/services/shortcuts.ts +++ b/resources/js/wysiwyg/services/shortcuts.ts @@ -94,30 +94,78 @@ const extendedActionsByKeys: Record = { }; function createKeyDownListener(context: EditorUiContext, useExtended: boolean): (e: KeyboardEvent) => void { - const keySetToUse = useExtended ? extendedActionsByKeys : baseActionsByKeys; + const baseKeySetToUse = useExtended ? extendedActionsByKeys : baseActionsByKeys; + const keySetToUse = extendKeySetWithKeyCodes(baseKeySetToUse); return (event: KeyboardEvent) => { - const combo = keyboardEventToKeyComboString(event); - // console.log(`pressed: ${combo}`); - if (keySetToUse[combo]) { - const handled = keySetToUse[combo](context.editor, context); - if (handled) { - event.stopPropagation(); - event.preventDefault(); + const comboStrings = keyboardEventToKeyComboStrings(event); + // console.log(comboStrings, event, keySetToUse); + for (const combo of comboStrings) { + if (keySetToUse[combo]) { + const handled = keySetToUse[combo](context.editor, context); + if (handled) { + event.stopPropagation(); + event.preventDefault(); + } + break; } } }; } -function keyboardEventToKeyComboString(event: KeyboardEvent): string { +/** + * Takes a shortcut key set and returns a new set with added variations of shortcts where + * they can be sensibly represented as their key code instead of just key, which we can use + * for matching in scenarios where the physical key may be represented of the letter used + * in the shortcut, but produces a different 'key' value. + * Useful for Cyrillic scenarios where the keyboard key would show a latin character + * as an option, and therefore be expected for use for the relevant latin shortcut, but the main + * key output is a Cyrillic character. + */ +function extendKeySetWithKeyCodes(keySet: Record): Record { + const newKeys: Record = {}; + + const setKeys = Object.keys(keySet); + for (const keyCombo of setKeys) { + const action = keySet[keyCombo]; + newKeys[keyCombo] = action; + + const comboParts = keyCombo.split('+'); + const lastComboPart = comboParts.pop() || ''; + if (lastComboPart.match(/^[a-zA-Z]$/)) { + const keyCode = lastComboPart.toUpperCase().charCodeAt(0); + comboParts.push(String(keyCode)); + const newCombo = comboParts.join('+'); + newKeys[newCombo] = action; + } + } + + return newKeys; +} + +function keyboardEventToKeyComboStrings(event: KeyboardEvent): string[] { const metaKeyPressed = isMac() ? event.metaKey : event.ctrlKey; - const parts = [ + const mainParts = [ metaKeyPressed ? 'meta' : '', event.shiftKey ? 'shift' : '', event.key, ]; - return parts.filter(Boolean).join('+').toLowerCase(); + const toReturn = [ + mainParts.filter(Boolean).join('+').toLowerCase(), + ]; + + // If ending with a standard latin character, provide an alternative + // keyCode based option for scenarios of dual-language keyboard use. + const keyCode = event.keyCode || 0; + if (keyCode >= 65 && keyCode <= 90) { + const keyCodeParts = [...mainParts]; + keyCodeParts.pop(); + keyCodeParts.push(String(keyCode)); + toReturn.push(keyCodeParts.filter(Boolean).join('+').toLowerCase()); + } + + return toReturn; } function isMac(): boolean {