| 1 | /** |
| 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| 3 | * |
| 4 | * This source code is licensed under the MIT license found in the |
| 5 | * LICENSE file in the root directory of this source tree. |
| 6 | */ |
| 7 | |
| 8 | import {Kbd} from 'isl-components/Kbd'; |
| 9 | import {KeyCode, makeCommandDispatcher, Modifier} from 'isl-components/KeyboardShortcuts'; |
| 10 | import {isMac} from 'isl-components/OperatingSystem'; |
| 11 | import {useMemo} from 'react'; |
| 12 | import {TypedEventEmitter} from 'shared/TypedEventEmitter'; |
| 13 | import {t} from './i18n'; |
| 14 | import {useModal} from './useModal'; |
| 15 | |
| 16 | import './ISLShortcuts.css'; |
| 17 | |
| 18 | const CMD = isMac ? Modifier.CMD : Modifier.CTRL; |
| 19 | |
| 20 | /* eslint-disable no-bitwise */ |
| 21 | export const [ISLCommandContext, useCommand, dispatchCommand, allCommands] = makeCommandDispatcher({ |
| 22 | OpenShortcutHelp: [Modifier.SHIFT, KeyCode.QuestionMark], |
| 23 | ToggleSidebar: [CMD, KeyCode.Period], |
| 24 | OpenUncommittedChangesComparisonView: [CMD, KeyCode.SingleQuote], |
| 25 | OpenHeadChangesComparisonView: [[CMD, Modifier.SHIFT], KeyCode.SingleQuote], |
| 26 | Escape: [Modifier.NONE, KeyCode.Escape], |
| 27 | SelectUpwards: [Modifier.NONE, KeyCode.UpArrow], |
| 28 | SelectDownwards: [Modifier.NONE, KeyCode.DownArrow], |
| 29 | OpenDetails: [Modifier.NONE, KeyCode.RightArrow], |
| 30 | ContinueSelectionUpwards: [Modifier.SHIFT, KeyCode.UpArrow], |
| 31 | ContinueSelectionDownwards: [Modifier.SHIFT, KeyCode.DownArrow], |
| 32 | SelectAllCommits: [Modifier.ALT, KeyCode.A], |
| 33 | HideSelectedCommits: [Modifier.NONE, KeyCode.Backspace], |
| 34 | ZoomIn: [Modifier.ALT, KeyCode.Plus], |
| 35 | ZoomOut: [Modifier.ALT, KeyCode.Minus], |
| 36 | ToggleTheme: [Modifier.ALT, KeyCode.T], |
| 37 | ToggleShelvedChangesDropdown: [Modifier.ALT, KeyCode.S], |
| 38 | ToggleDownloadCommitsDropdown: [Modifier.ALT, KeyCode.D], |
| 39 | ToggleCwdDropdown: [Modifier.ALT, KeyCode.C], |
| 40 | ToggleBulkActionsDropdown: [Modifier.ALT, KeyCode.B], |
| 41 | ToggleFocusMode: [Modifier.ALT, KeyCode.F], |
| 42 | ToggleBookmarksManagerDropdown: [Modifier.ALT, KeyCode.M], |
| 43 | RebaseOntoCurrentStackBase: [Modifier.ALT, KeyCode.R], |
| 44 | }); |
| 45 | |
| 46 | export type ISLCommandName = Parameters<typeof useCommand>[0]; |
| 47 | |
| 48 | /** Like useCommand, but returns an eventEmitter you can subscribe to */ |
| 49 | export function useCommandEvent(commandName: ISLCommandName): TypedEventEmitter<'change', null> { |
| 50 | const emitter = useMemo(() => new TypedEventEmitter<'change', null>(), []); |
| 51 | useCommand(commandName, () => { |
| 52 | emitter.emit('change', null); |
| 53 | }); |
| 54 | return emitter; |
| 55 | } |
| 56 | |
| 57 | export const ISLShortcutLabels: Partial<Record<ISLCommandName, string>> = { |
| 58 | Escape: t('Dismiss Tooltips and Popups'), |
| 59 | OpenShortcutHelp: t('Open Shortcut Help'), |
| 60 | ToggleSidebar: t('Toggle Commit Info Sidebar'), |
| 61 | OpenUncommittedChangesComparisonView: t('Open Uncommitted Changes Comparison View'), |
| 62 | OpenHeadChangesComparisonView: t('Open Head Changes Comparison View'), |
| 63 | SelectAllCommits: t('Select All Commits'), |
| 64 | ToggleTheme: t('Toggle Light/Dark Theme'), |
| 65 | ZoomIn: t('Zoom In'), |
| 66 | ZoomOut: t('Zoom Out'), |
| 67 | ToggleShelvedChangesDropdown: t('Toggle Shelved Changes Dropdown'), |
| 68 | ToggleDownloadCommitsDropdown: t('Toggle Download Commits Dropdown'), |
| 69 | ToggleCwdDropdown: t('Toggle CWD Dropdown'), |
| 70 | ToggleBulkActionsDropdown: t('Toggle Bulk Actions Dropdown'), |
| 71 | ToggleFocusMode: t('Toggle Focus Mode'), |
| 72 | ToggleBookmarksManagerDropdown: t('Toggle Bookmarks Manager Dropdown'), |
| 73 | RebaseOntoCurrentStackBase: t('Rebase Selected Commits onto Current Stack Base'), |
| 74 | }; |
| 75 | |
| 76 | export function useShowKeyboardShortcutsHelp(): () => unknown { |
| 77 | const showModal = useModal(); |
| 78 | const showShortcutsModal = () => { |
| 79 | showModal({ |
| 80 | type: 'custom', |
| 81 | component: () => ( |
| 82 | <div className="keyboard-shortcuts-menu"> |
| 83 | <table> |
| 84 | {(Object.entries(ISLShortcutLabels) as Array<[ISLCommandName, string]>).map( |
| 85 | ([name, label]) => { |
| 86 | const [modifiers, keyCode] = allCommands[name]; |
| 87 | { |
| 88 | return ( |
| 89 | <tr key={name}> |
| 90 | <td>{label}</td> |
| 91 | <td> |
| 92 | <Kbd |
| 93 | modifiers={Array.isArray(modifiers) ? modifiers : [modifiers]} |
| 94 | keycode={keyCode} |
| 95 | /> |
| 96 | </td> |
| 97 | </tr> |
| 98 | ); |
| 99 | } |
| 100 | }, |
| 101 | )} |
| 102 | </table> |
| 103 | </div> |
| 104 | ), |
| 105 | icon: 'keyboard', |
| 106 | title: t('Keyboard Shortcuts'), |
| 107 | }); |
| 108 | }; |
| 109 | useCommand('OpenShortcutHelp', showShortcutsModal); |
| 110 | return showShortcutsModal; |
| 111 | } |
| 112 | |