import { Options, useHotkeys } from 'react-hotkeys-hook' import { useEffect } from 'react' import { codeManager } from './singletons' import { Platform } from './utils' // Hotkey wrapper wraps hotkeys for the app (outside of the editor) // with hotkeys inside the editor. // This way we can have hotkeys defined in one place and not have to worry about // conflicting hotkeys, or them only being implemented for the app but not // inside the editor. // TODO: would be nice if this didn't have to be a react hook. It's not needed // for the code mirror stuff but but it is needed for the useHotkeys hook. export default function useHotkeyWrapper( hotkey: string[], callback: () => void, additionalOptions?: Options ) { const defaultOptions = { preventDefault: true } const options = { ...defaultOptions, ...additionalOptions } useHotkeys(hotkey, callback, options) useEffect(() => { for (const key of hotkey) { const keybinding = mapHotkeyToCodeMirrorHotkey(key) codeManager.registerHotkey(keybinding, callback) } }) } // Convert hotkey to code mirror hotkey // See: https://codemirror.net/docs/ref/#view.KeyBinding function mapHotkeyToCodeMirrorHotkey(hotkey: string): string { return hotkey .replaceAll('+', '-') .replaceAll(' ', '') .replaceAll('mod', 'Mod') .replaceAll('meta', 'Meta') .replaceAll('ctrl', 'Ctrl') .replaceAll('shift', 'Shift') .replaceAll('alt', 'Alt') } const LOWER_CASE_LETTER = /[a-z]/ const WHITESPACE = /\s+/g // Convert hotkey to display text. export function hotkeyDisplay(hotkey: string, platform: Platform): string { const isMac = platform === 'macos' const isWindows = platform === 'windows' const meta = isWindows ? 'Win' : 'Meta' const outputSeparator = isMac ? '' : '+' const display = hotkey // Capitalize letters. We want Ctrl K, not Ctrl k, since Shift should be // shown as a separate modifier. .split('+') .map((word) => { if (word.length === 1 && LOWER_CASE_LETTER.test(word)) { return word.toUpperCase() } return word }) .join(outputSeparator) // Collapse multiple spaces into one. .replaceAll(WHITESPACE, ' ') .replaceAll('mod', isMac ? '⌘' : 'Ctrl') .replaceAll('meta', isMac ? '⌘' : meta) // Note: This is *not* the ASCII caret character. It's "UP ARROWHEAD". // Unicode: U+2303. .replaceAll('ctrl', isMac ? '⌃' : 'Ctrl') // Note: This is technically the wrong arrow for shift, but it's more // visible and recognizable. May want to change this in the future. // // The correct arrow is ⇧ "UPWARDS WHITE ARROW" Unicode: U+21E7 .replaceAll('shift', isMac ? '⬆' : 'Shift') .replaceAll('alt', isMac ? '⌥' : 'Alt') return display }