// Copyright 2024, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 const KeyTypeCodeRegex = /c{(.*)}/; const KeyTypeKey = "key"; const KeyTypeCode = "code"; let PLATFORM: NodeJS.Platform = "darwin"; const PlatformMacOS = "darwin"; function setKeyUtilPlatform(platform: NodeJS.Platform) { PLATFORM = platform; } function parseKey(key: string): { key: string; type: string } { let regexMatch = key.match(KeyTypeCodeRegex); if (regexMatch != null && regexMatch.length > 1) { let code = regexMatch[1]; return { key: code, type: KeyTypeCode }; } else if (regexMatch != null) { console.log("error: regexMatch is not null yet there is no captured group: ", regexMatch, key); } return { key: key, type: KeyTypeKey }; } function parseKeyDescription(keyDescription: string): KeyPressDecl { let rtn = { key: "", mods: {} } as KeyPressDecl; let keys = keyDescription.replace(/[()]/g, "").split(":"); for (let key of keys) { if (key == "Cmd") { rtn.mods.Cmd = true; } else if (key == "Shift") { rtn.mods.Shift = true; } else if (key == "Ctrl") { rtn.mods.Ctrl = true; } else if (key == "Option") { rtn.mods.Option = true; } else if (key == "Alt") { rtn.mods.Alt = true; } else if (key == "Meta") { rtn.mods.Meta = true; } else { let { key: parsedKey, type: keyType } = parseKey(key); rtn.key = parsedKey; rtn.keyType = keyType; if (rtn.keyType == KeyTypeKey && key.length == 1) { // check for if key is upper case // TODO what about unicode upper case? if (/[A-Z]/.test(key.charAt(0))) { // this key is an upper case A - Z - we should apply the shift key, even if it wasn't specified rtn.mods.Shift = true; } else if (key == " ") { rtn.key = "Space"; // we allow " " and "Space" to be mapped to Space key } } } } return rtn; } function notMod(keyPressMod: boolean, eventMod: boolean) { return (keyPressMod && !eventMod) || (eventMod && !keyPressMod); } function checkKeyPressed(event: WaveKeyboardEvent, keyDescription: string): boolean { let keyPress = parseKeyDescription(keyDescription); if (!keyPress.mods.Alt && notMod(keyPress.mods.Option, event.option)) { return false; } if (!keyPress.mods.Meta && notMod(keyPress.mods.Cmd, event.cmd)) { return false; } if (notMod(keyPress.mods.Shift, event.shift)) { return false; } if (notMod(keyPress.mods.Ctrl, event.control)) { return false; } if (keyPress.mods.Alt && !event.alt) { return false; } if (keyPress.mods.Meta && !event.meta) { return false; } let eventKey = ""; let descKey = keyPress.key; if (keyPress.keyType == KeyTypeCode) { eventKey = event.code; } if (keyPress.keyType == KeyTypeKey) { eventKey = event.key; if (eventKey.length == 1 && /[A-Z]/.test(eventKey.charAt(0))) { // key is upper case A-Z, this means shift is applied, we want to allow // "Shift:e" as well as "Shift:E" or "E" eventKey = eventKey.toLocaleLowerCase(); descKey = descKey.toLocaleLowerCase(); } else if (eventKey == " ") { eventKey = "Space"; // a space key is shown as " ", we want users to be able to set space key as "Space" or " ", whichever they prefer } } if (descKey != eventKey) { return false; } return true; } function adaptFromReactOrNativeKeyEvent(event: React.KeyboardEvent | KeyboardEvent): WaveKeyboardEvent { let rtn: WaveKeyboardEvent = {} as WaveKeyboardEvent; rtn.control = event.ctrlKey; rtn.shift = event.shiftKey; rtn.cmd = PLATFORM == PlatformMacOS ? event.metaKey : event.altKey; rtn.option = PLATFORM == PlatformMacOS ? event.altKey : event.metaKey; rtn.meta = event.metaKey; rtn.alt = event.altKey; rtn.code = event.code; rtn.key = event.key; rtn.location = event.location; rtn.type = event.type; rtn.repeat = event.repeat; return rtn; } function adaptFromElectronKeyEvent(event: any): WaveKeyboardEvent { let rtn: WaveKeyboardEvent = {} as WaveKeyboardEvent; rtn.type = event.type; rtn.control = event.control; rtn.cmd = PLATFORM == PlatformMacOS ? event.meta : event.alt; rtn.option = PLATFORM == PlatformMacOS ? event.alt : event.meta; rtn.meta = event.meta; rtn.alt = event.alt; rtn.shift = event.shift; rtn.repeat = event.isAutoRepeat; rtn.location = event.location; rtn.code = event.code; rtn.key = event.key; return rtn; } export { adaptFromElectronKeyEvent, adaptFromReactOrNativeKeyEvent, checkKeyPressed, parseKeyDescription, setKeyUtilPlatform, };