native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
9.3 kB · 299 lines
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300import AppKitimport Carbonimport ProwlCLIShared
/// Maps canonical CLI key tokens to macOS NSEvent parameters.struct CLIKeySpec { let keyCode: UInt16 let characters: String let charactersIgnoringModifiers: String let modifiers: NSEvent.ModifierFlags
static func from(token: String) -> CLIKeySpec? { guard let descriptor = KeyTokens.descriptor(for: token), let base = baseSpec(for: descriptor.baseToken) else { return nil }
var modifierFlags = eventModifiers(from: descriptor.modifiers) if base.usesFunctionModifier { modifierFlags.insert(.function) }
let charactersIgnoringModifiers = base.charactersIgnoringModifiers let characters: String if usesControlCharacter(for: descriptor.modifiers), let controlCharacter = controlCharacter(for: descriptor.baseToken) { characters = controlCharacter } else if descriptor.modifiers.contains(.shift) { characters = shiftedCharacters(for: descriptor.baseToken) ?? charactersIgnoringModifiers } else { characters = charactersIgnoringModifiers }
return CLIKeySpec( keyCode: base.keyCode, characters: characters, charactersIgnoringModifiers: charactersIgnoringModifiers, modifiers: modifierFlags ) }
private struct BaseSpec { let keyCode: UInt16 let charactersIgnoringModifiers: String let usesFunctionModifier: Bool }
private static let namedBaseSpecs: [String: BaseSpec] = [ "enter": BaseSpec( keyCode: UInt16(kVK_Return), charactersIgnoringModifiers: "\r", usesFunctionModifier: false ), "esc": BaseSpec( keyCode: UInt16(kVK_Escape), charactersIgnoringModifiers: "\u{1B}", usesFunctionModifier: false ), "tab": BaseSpec( keyCode: UInt16(kVK_Tab), charactersIgnoringModifiers: "\t", usesFunctionModifier: false ), "backspace": BaseSpec( keyCode: UInt16(kVK_Delete), charactersIgnoringModifiers: "\u{7F}", usesFunctionModifier: false ), "delete-forward": BaseSpec( keyCode: UInt16(kVK_ForwardDelete), charactersIgnoringModifiers: String(UnicodeScalar(NSDeleteFunctionKey)!), usesFunctionModifier: true ), "insert": BaseSpec( keyCode: UInt16(kVK_Help), charactersIgnoringModifiers: String(UnicodeScalar(NSInsertFunctionKey)!), usesFunctionModifier: true ), "up": BaseSpec( keyCode: UInt16(kVK_UpArrow), charactersIgnoringModifiers: String(UnicodeScalar(NSUpArrowFunctionKey)!), usesFunctionModifier: true ), "down": BaseSpec( keyCode: UInt16(kVK_DownArrow), charactersIgnoringModifiers: String(UnicodeScalar(NSDownArrowFunctionKey)!), usesFunctionModifier: true ), "left": BaseSpec( keyCode: UInt16(kVK_LeftArrow), charactersIgnoringModifiers: String(UnicodeScalar(NSLeftArrowFunctionKey)!), usesFunctionModifier: true ), "right": BaseSpec( keyCode: UInt16(kVK_RightArrow), charactersIgnoringModifiers: String(UnicodeScalar(NSRightArrowFunctionKey)!), usesFunctionModifier: true ), "pageup": BaseSpec( keyCode: UInt16(kVK_PageUp), charactersIgnoringModifiers: String(UnicodeScalar(NSPageUpFunctionKey)!), usesFunctionModifier: true ), "pagedown": BaseSpec( keyCode: UInt16(kVK_PageDown), charactersIgnoringModifiers: String(UnicodeScalar(NSPageDownFunctionKey)!), usesFunctionModifier: true ), "home": BaseSpec( keyCode: UInt16(kVK_Home), charactersIgnoringModifiers: String(UnicodeScalar(NSHomeFunctionKey)!), usesFunctionModifier: true ), "end": BaseSpec( keyCode: UInt16(kVK_End), charactersIgnoringModifiers: String(UnicodeScalar(NSEndFunctionKey)!), usesFunctionModifier: true ), ]
private static let functionKeyMap: [String: (UInt16, Int)] = [ "f1": (UInt16(kVK_F1), NSF1FunctionKey), "f2": (UInt16(kVK_F2), NSF2FunctionKey), "f3": (UInt16(kVK_F3), NSF3FunctionKey), "f4": (UInt16(kVK_F4), NSF4FunctionKey), "f5": (UInt16(kVK_F5), NSF5FunctionKey), "f6": (UInt16(kVK_F6), NSF6FunctionKey), "f7": (UInt16(kVK_F7), NSF7FunctionKey), "f8": (UInt16(kVK_F8), NSF8FunctionKey), "f9": (UInt16(kVK_F9), NSF9FunctionKey), "f10": (UInt16(kVK_F10), NSF10FunctionKey), "f11": (UInt16(kVK_F11), NSF11FunctionKey), "f12": (UInt16(kVK_F12), NSF12FunctionKey), ]
private static let printableKeyCodes: [String: UInt16] = [ "a": UInt16(kVK_ANSI_A), "b": UInt16(kVK_ANSI_B), "c": UInt16(kVK_ANSI_C), "d": UInt16(kVK_ANSI_D), "e": UInt16(kVK_ANSI_E), "f": UInt16(kVK_ANSI_F), "g": UInt16(kVK_ANSI_G), "h": UInt16(kVK_ANSI_H), "i": UInt16(kVK_ANSI_I), "j": UInt16(kVK_ANSI_J), "k": UInt16(kVK_ANSI_K), "l": UInt16(kVK_ANSI_L), "m": UInt16(kVK_ANSI_M), "n": UInt16(kVK_ANSI_N), "o": UInt16(kVK_ANSI_O), "p": UInt16(kVK_ANSI_P), "q": UInt16(kVK_ANSI_Q), "r": UInt16(kVK_ANSI_R), "s": UInt16(kVK_ANSI_S), "t": UInt16(kVK_ANSI_T), "u": UInt16(kVK_ANSI_U), "v": UInt16(kVK_ANSI_V), "w": UInt16(kVK_ANSI_W), "x": UInt16(kVK_ANSI_X), "y": UInt16(kVK_ANSI_Y), "z": UInt16(kVK_ANSI_Z), "0": UInt16(kVK_ANSI_0), "1": UInt16(kVK_ANSI_1), "2": UInt16(kVK_ANSI_2), "3": UInt16(kVK_ANSI_3), "4": UInt16(kVK_ANSI_4), "5": UInt16(kVK_ANSI_5), "6": UInt16(kVK_ANSI_6), "7": UInt16(kVK_ANSI_7), "8": UInt16(kVK_ANSI_8), "9": UInt16(kVK_ANSI_9), "space": UInt16(kVK_Space), "minus": UInt16(kVK_ANSI_Minus), "equal": UInt16(kVK_ANSI_Equal), "comma": UInt16(kVK_ANSI_Comma), "period": UInt16(kVK_ANSI_Period), "slash": UInt16(kVK_ANSI_Slash), "backslash": UInt16(kVK_ANSI_Backslash), "semicolon": UInt16(kVK_ANSI_Semicolon), "quote": UInt16(kVK_ANSI_Quote), "grave": UInt16(kVK_ANSI_Grave), "left-bracket": UInt16(kVK_ANSI_LeftBracket), "right-bracket": UInt16(kVK_ANSI_RightBracket), ]
private static let shiftedCharacterMap: [String: String] = [ "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", "8": "*", "9": "(", "0": ")", "minus": "_", "equal": "+", "comma": "<", "period": ">", "slash": "?", "backslash": "|", "semicolon": ":", "quote": "\"", "grave": "~", "left-bracket": "{", "right-bracket": "}", ]
private static func eventModifiers(from modifiers: [KeyModifier]) -> NSEvent.ModifierFlags { modifiers.reduce(into: NSEvent.ModifierFlags()) { result, modifier in switch modifier { case .cmd: result.insert(.command) case .shift: result.insert(.shift) case .opt: result.insert(.option) case .ctrl: result.insert(.control) } } }
private static func baseSpec(for token: String) -> BaseSpec? { if let base = namedBaseSpecs[token] { return base } if let (keyCode, scalar) = functionKeyMap[token] { return BaseSpec( keyCode: keyCode, charactersIgnoringModifiers: String(UnicodeScalar(scalar)!), usesFunctionModifier: true ) } return printableBaseSpec(for: token) }
private static func printableBaseSpec(for token: String) -> BaseSpec? { guard let character = printableCharacter(for: token), let keyCode = printableKeyCode(for: token) else { return nil }
return BaseSpec( keyCode: keyCode, charactersIgnoringModifiers: String(character), usesFunctionModifier: false ) }
private static func printableCharacter(for token: String) -> Character? { switch token { case "space": return " " case "minus": return "-" case "equal": return "=" case "comma": return "," case "period": return "." case "slash": return "/" case "backslash": return "\\" case "semicolon": return ";" case "quote": return "'" case "grave": return "`" case "left-bracket": return "[" case "right-bracket": return "]" default: guard token.count == 1 else { return nil } return token.first } }
private static func printableKeyCode(for token: String) -> UInt16? { printableKeyCodes[token] }
private static func shiftedCharacters(for token: String) -> String? { if token.count == 1, let character = token.first, character.isLetter { return String(character).uppercased() }
return shiftedCharacterMap[token] }
private static func usesControlCharacter(for modifiers: [KeyModifier]) -> Bool { let modifierSet = Set(modifiers) return modifierSet.contains(.ctrl) && modifierSet.isSubset(of: [.ctrl, .shift]) }
private static func controlCharacter(for token: String) -> String? { if token.count == 1, let scalar = token.unicodeScalars.first, scalar.properties.isAlphabetic { return String(UnicodeScalar(scalar.value & 0x1F)!) }
switch token { case "left-bracket": return String(UnicodeScalar(27)!) case "backslash": return String(UnicodeScalar(28)!) case "right-bracket": return String(UnicodeScalar(29)!) case "6": return String(UnicodeScalar(30)!) case "minus": return String(UnicodeScalar(31)!) default: return nil } }}