From ff3c45ba359fb53520310c2c44bd368255e31023 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Sat, 27 Jan 2024 15:25:49 +0530 Subject: [PATCH] feat: Added "Keyboard shortcuts" help dialog. Can be opened by pressing Shift + ? --- packages/snjs/lib/Application/Platforms.ts | 2 +- .../src/Keyboard/KeyboardCommandHandler.ts | 3 + .../src/Keyboard/KeyboardCommands.ts | 2 + .../src/Keyboard/KeyboardModifier.ts | 7 ++ .../src/Keyboard/KeyboardService.ts | 56 ++++++++++++- .../src/Keyboard/KeyboardShortcut.ts | 7 ++ .../src/Keyboard/getKeyboardShortcuts.ts | 14 ++-- .../src/Keyboard/keyboardCharacterForKey.ts | 20 +++++ packages/ui-services/src/index.ts | 1 + .../AccountMenu/GeneralAccountMenu.tsx | 19 +++++ .../ApplicationView/ApplicationView.tsx | 2 + .../ChangeEditor/ChangeEditorButton.tsx | 2 + .../ContentListView/ContentListView.tsx | 10 +++ .../EditorWidthSelectionModal.tsx | 2 + .../Components/Footer/QuickSettingsButton.tsx | 2 + .../KeyboardShortcutIndicator.tsx | 38 +++++---- .../KeyboardShortcutsHelpModal.tsx | 78 +++++++++++++++++++ .../LinkedItemBubblesContainer.tsx | 2 + .../Preferences/PreferencesViewWrapper.tsx | 2 + .../Plugins/SearchPlugin/SearchPlugin.tsx | 8 ++ .../Plugins/ToolbarPlugin/ToolbarPlugin.tsx | 2 + .../Components/SuperEditor/SuperEditor.tsx | 40 +++++++++- .../Navigation/NavigationController.ts | 2 + .../NoteHistory/HistoryModalController.ts | 2 + .../NotesController/NotesController.ts | 4 + .../PaneController/PaneController.ts | 6 ++ packages/web/src/javascripts/Utils/Utils.ts | 6 +- 27 files changed, 312 insertions(+), 27 deletions(-) create mode 100644 packages/ui-services/src/Keyboard/keyboardCharacterForKey.ts create mode 100644 packages/web/src/javascripts/Components/KeyboardShortcutsHelpModal/KeyboardShortcutsHelpModal.tsx diff --git a/packages/snjs/lib/Application/Platforms.ts b/packages/snjs/lib/Application/Platforms.ts index 07a86e6a5..8cd8e8f32 100644 --- a/packages/snjs/lib/Application/Platforms.ts +++ b/packages/snjs/lib/Application/Platforms.ts @@ -9,7 +9,7 @@ export function platformFromString(string: string) { 'windows-web': Platform.WindowsWeb, 'windows-desktop': Platform.WindowsDesktop, 'ios-web': Platform.Ios, - android: Platform.Android, + 'android-web': Platform.Android, } return map[string] } diff --git a/packages/ui-services/src/Keyboard/KeyboardCommandHandler.ts b/packages/ui-services/src/Keyboard/KeyboardCommandHandler.ts index d40a541ff..c4e6bf20f 100644 --- a/packages/ui-services/src/Keyboard/KeyboardCommandHandler.ts +++ b/packages/ui-services/src/Keyboard/KeyboardCommandHandler.ts @@ -1,7 +1,10 @@ import { KeyboardCommand } from './KeyboardCommands' +import { KeyboardShortcutCategory } from './KeyboardShortcut' export type KeyboardCommandHandler = { command: KeyboardCommand + category?: KeyboardShortcutCategory + description?: string onKeyDown?: (event: KeyboardEvent, data?: unknown) => boolean | void onKeyUp?: (event: KeyboardEvent, data?: unknown) => boolean | void element?: HTMLElement diff --git a/packages/ui-services/src/Keyboard/KeyboardCommands.ts b/packages/ui-services/src/Keyboard/KeyboardCommands.ts index 356782c1e..4ecd457b3 100644 --- a/packages/ui-services/src/Keyboard/KeyboardCommands.ts +++ b/packages/ui-services/src/Keyboard/KeyboardCommands.ts @@ -39,3 +39,5 @@ export const SUPER_EXPORT_MARKDOWN = createKeyboardCommand('SUPER_EXPORT_MARKDOW export const OPEN_PREFERENCES_COMMAND = createKeyboardCommand('OPEN_PREFERENCES_COMMAND') export const CHANGE_EDITOR_WIDTH_COMMAND = createKeyboardCommand('CHANGE_EDITOR_WIDTH_COMMAND') + +export const TOGGLE_KEYBOARD_SHORTCUTS_MODAL = createKeyboardCommand('TOGGLE_KEYBOARD_SHORTCUTS_MODAL') diff --git a/packages/ui-services/src/Keyboard/KeyboardModifier.ts b/packages/ui-services/src/Keyboard/KeyboardModifier.ts index 1390e0504..c641b02c9 100644 --- a/packages/ui-services/src/Keyboard/KeyboardModifier.ts +++ b/packages/ui-services/src/Keyboard/KeyboardModifier.ts @@ -1,3 +1,6 @@ +import { Platform } from '@standardnotes/models' +import { isMacPlatform } from './platformCheck' + export enum KeyboardModifier { Shift = 'Shift', Ctrl = 'Control', @@ -5,3 +8,7 @@ export enum KeyboardModifier { Meta = 'Meta', Alt = 'Alt', } + +export function getPrimaryModifier(platform: Platform): KeyboardModifier { + return isMacPlatform(platform) ? KeyboardModifier.Meta : KeyboardModifier.Ctrl +} diff --git a/packages/ui-services/src/Keyboard/KeyboardService.ts b/packages/ui-services/src/Keyboard/KeyboardService.ts index 1371c9ef3..860612c83 100644 --- a/packages/ui-services/src/Keyboard/KeyboardService.ts +++ b/packages/ui-services/src/Keyboard/KeyboardService.ts @@ -4,7 +4,7 @@ import { KeyboardCommand } from './KeyboardCommands' import { KeyboardKeyEvent } from './KeyboardKeyEvent' import { KeyboardModifier } from './KeyboardModifier' import { KeyboardCommandHandler } from './KeyboardCommandHandler' -import { KeyboardShortcut, PlatformedKeyboardShortcut } from './KeyboardShortcut' +import { KeyboardShortcut, KeyboardShortcutHelpItem, PlatformedKeyboardShortcut } from './KeyboardShortcut' import { getKeyboardShortcuts } from './getKeyboardShortcuts' export class KeyboardService { @@ -12,6 +12,8 @@ export class KeyboardService { private commandHandlers = new Set() private commandMap = new Map() + private keyboardShortcutHelpItems = new Set() + constructor( private platform: Platform, environment: Environment, @@ -190,10 +192,18 @@ export class KeyboardService { addCommandHandler(observer: KeyboardCommandHandler): () => void { this.commandHandlers.add(observer) + const helpItem = this.getKeyboardShortcutHelpItemForHandler(observer) + if (helpItem) { + this.keyboardShortcutHelpItems.add(helpItem) + } + return () => { observer.onKeyDown = undefined observer.onKeyDown = undefined this.commandHandlers.delete(observer) + if (helpItem) { + this.keyboardShortcutHelpItems.delete(helpItem) + } } } @@ -217,4 +227,48 @@ export class KeyboardService { ...shortcut, } } + + getKeyboardShortcutHelpItemForHandler(handler: KeyboardCommandHandler): KeyboardShortcutHelpItem | undefined { + const shortcut = this.keyboardShortcutForCommand(handler.command) + + if (!shortcut || !handler.category || !handler.description) { + return undefined + } + + return { + ...shortcut, + category: handler.category, + description: handler.description, + } + } + + /** + * Register help item for a keyboard shortcut that is handled outside of the KeyboardService, + * for example by a library like Lexical. + */ + registerExternalKeyboardShortcutHelpItem(item: KeyboardShortcutHelpItem): () => void { + this.keyboardShortcutHelpItems.add(item) + + return () => { + this.keyboardShortcutHelpItems.delete(item) + } + } + + /** + * Register help item for a keyboard shortcut that is handled outside of the KeyboardService, + * for example by a library like Lexical. + */ + registerExternalKeyboardShortcutHelpItems(items: KeyboardShortcutHelpItem[]): () => void { + const disposers = items.map((item) => this.registerExternalKeyboardShortcutHelpItem(item)) + + return () => { + for (const disposer of disposers) { + disposer() + } + } + } + + getRegisteredKeyboardShorcutHelpItems(): KeyboardShortcutHelpItem[] { + return Array.from(this.keyboardShortcutHelpItems) + } } diff --git a/packages/ui-services/src/Keyboard/KeyboardShortcut.ts b/packages/ui-services/src/Keyboard/KeyboardShortcut.ts index 392a1f788..c04925a0d 100644 --- a/packages/ui-services/src/Keyboard/KeyboardShortcut.ts +++ b/packages/ui-services/src/Keyboard/KeyboardShortcut.ts @@ -18,3 +18,10 @@ export type KeyboardShortcut = { export type PlatformedKeyboardShortcut = KeyboardShortcut & { platform: Platform } + +export type KeyboardShortcutCategory = 'General' | 'Notes list' | 'Current note' | 'Super notes' | 'Formatting' + +export type KeyboardShortcutHelpItem = Omit & { + category: KeyboardShortcutCategory + description: string +} diff --git a/packages/ui-services/src/Keyboard/getKeyboardShortcuts.ts b/packages/ui-services/src/Keyboard/getKeyboardShortcuts.ts index d6ba3bb73..ee3bd27e1 100644 --- a/packages/ui-services/src/Keyboard/getKeyboardShortcuts.ts +++ b/packages/ui-services/src/Keyboard/getKeyboardShortcuts.ts @@ -1,5 +1,4 @@ import { Environment, Platform } from '@standardnotes/models' -import { isMacPlatform } from './platformCheck' import { CREATE_NEW_NOTE_KEYBOARD_COMMAND, TOGGLE_LIST_PANE_KEYBOARD_COMMAND, @@ -31,15 +30,14 @@ import { SUPER_SEARCH_TOGGLE_REPLACE_MODE, CHANGE_EDITOR_WIDTH_COMMAND, SUPER_TOGGLE_TOOLBAR, + TOGGLE_KEYBOARD_SHORTCUTS_MODAL, } from './KeyboardCommands' import { KeyboardKey } from './KeyboardKey' -import { KeyboardModifier } from './KeyboardModifier' +import { KeyboardModifier, getPrimaryModifier } from './KeyboardModifier' import { KeyboardShortcut } from './KeyboardShortcut' export function getKeyboardShortcuts(platform: Platform, _environment: Environment): KeyboardShortcut[] { - const isMac = isMacPlatform(platform) - - const primaryModifier = isMac ? KeyboardModifier.Meta : KeyboardModifier.Ctrl + const primaryModifier = getPrimaryModifier(platform) return [ { @@ -195,5 +193,11 @@ export function getKeyboardShortcuts(platform: Platform, _environment: Environme modifiers: [primaryModifier, KeyboardModifier.Shift], preventDefault: true, }, + { + command: TOGGLE_KEYBOARD_SHORTCUTS_MODAL, + key: '?', + preventDefault: true, + modifiers: [KeyboardModifier.Shift], + }, ] } diff --git a/packages/ui-services/src/Keyboard/keyboardCharacterForKey.ts b/packages/ui-services/src/Keyboard/keyboardCharacterForKey.ts new file mode 100644 index 000000000..7ce2f23fb --- /dev/null +++ b/packages/ui-services/src/Keyboard/keyboardCharacterForKey.ts @@ -0,0 +1,20 @@ +export function keyboardCharacterForKeyOrCode(keyOrCode: string) { + if (keyOrCode.startsWith('Digit')) { + return keyOrCode.replace('Digit', '') + } + if (keyOrCode.startsWith('Key')) { + return keyOrCode.replace('Key', '') + } + switch (keyOrCode) { + case 'ArrowDown': + return '↓' + case 'ArrowUp': + return '↑' + case 'ArrowLeft': + return '←' + case 'ArrowRight': + return '→' + default: + return keyOrCode + } +} diff --git a/packages/ui-services/src/index.ts b/packages/ui-services/src/index.ts index 15ab6bc7c..ed05b5614 100644 --- a/packages/ui-services/src/index.ts +++ b/packages/ui-services/src/index.ts @@ -10,6 +10,7 @@ export * from './Keyboard/KeyboardCommands' export * from './Keyboard/platformCheck' export * from './Keyboard/KeyboardKey' export * from './Keyboard/KeyboardModifier' +export * from './Keyboard/keyboardCharacterForKey' export * from './Keyboard/keyboardCharacterForModifier' export * from './Keyboard/keyboardStringForShortcut' export * from './Route/Params/DemoParams' diff --git a/packages/web/src/javascripts/Components/AccountMenu/GeneralAccountMenu.tsx b/packages/web/src/javascripts/Components/AccountMenu/GeneralAccountMenu.tsx index 6610360b5..0a7f55c94 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/GeneralAccountMenu.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/GeneralAccountMenu.tsx @@ -13,6 +13,8 @@ import Spinner from '@/Components/Spinner/Spinner' import { MenuItemIconSize } from '@/Constants/TailwindClassNames' import { useApplication } from '../ApplicationProvider' import MenuSection from '../Menu/MenuSection' +import { TOGGLE_KEYBOARD_SHORTCUTS_MODAL, isMobilePlatform } from '@standardnotes/ui-services' +import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator' type Props = { mainApplicationGroup: WebApplicationGroup @@ -90,6 +92,10 @@ const GeneralAccountMenu: FunctionComponent = ({ setMenuPane, closeMenu, const CREATE_ACCOUNT_INDEX = 1 const SWITCHER_INDEX = 0 + const keyboardShortcutsHelpShortcut = useMemo(() => { + return application.keyboardService.keyboardShortcutForCommand(TOGGLE_KEYBOARD_SHORTCUTS_MODAL) + }, [application.keyboardService]) + return ( <>
@@ -187,6 +193,19 @@ const GeneralAccountMenu: FunctionComponent = ({ setMenuPane, closeMenu,
v{application.version} + {!isMobilePlatform(application.platform) && ( + { + application.keyboardService.triggerCommand(TOGGLE_KEYBOARD_SHORTCUTS_MODAL) + }} + > + + Keyboard shortcuts + {keyboardShortcutsHelpShortcut && ( + + )} + + )} {user ? ( diff --git a/packages/web/src/javascripts/Components/ApplicationView/ApplicationView.tsx b/packages/web/src/javascripts/Components/ApplicationView/ApplicationView.tsx index e31c2547b..9bfabe577 100644 --- a/packages/web/src/javascripts/Components/ApplicationView/ApplicationView.tsx +++ b/packages/web/src/javascripts/Components/ApplicationView/ApplicationView.tsx @@ -31,6 +31,7 @@ import ImportModal from '../ImportModal/ImportModal' import IosKeyboardClose from '../IosKeyboardClose/IosKeyboardClose' import EditorWidthSelectionModalWrapper from '../EditorWidthSelectionModal/EditorWidthSelectionModal' import { ProtectionEvent } from '@standardnotes/services' +import KeyboardShortcutsModal from '../KeyboardShortcutsHelpModal/KeyboardShortcutsHelpModal' type Props = { application: WebApplication @@ -267,6 +268,7 @@ const ApplicationView: FunctionComponent = ({ application, mainApplicatio + {application.routeService.isDotOrg && } {isIOS() && } diff --git a/packages/web/src/javascripts/Components/ChangeEditor/ChangeEditorButton.tsx b/packages/web/src/javascripts/Components/ChangeEditor/ChangeEditorButton.tsx index 23e8443d3..c57c13e02 100644 --- a/packages/web/src/javascripts/Components/ChangeEditor/ChangeEditorButton.tsx +++ b/packages/web/src/javascripts/Components/ChangeEditor/ChangeEditorButton.tsx @@ -57,6 +57,8 @@ const ChangeEditorButton: FunctionComponent = ({ noteViewController, onCl useEffect(() => { return application.keyboardService.addCommandHandler({ command: CHANGE_EDITOR_COMMAND, + category: 'Current note', + description: 'Change note type', onKeyDown: () => { void toggleMenu() }, diff --git a/packages/web/src/javascripts/Components/ContentListView/ContentListView.tsx b/packages/web/src/javascripts/Components/ContentListView/ContentListView.tsx index 75aa4018c..7fe604fd4 100644 --- a/packages/web/src/javascripts/Components/ContentListView/ContentListView.tsx +++ b/packages/web/src/javascripts/Components/ContentListView/ContentListView.tsx @@ -178,6 +178,8 @@ const ContentListView = forwardRef( return application.keyboardService.addCommandHandlers([ { command: CREATE_NEW_NOTE_KEYBOARD_COMMAND, + category: 'General', + description: 'Create new note', onKeyDown: (event) => { event.preventDefault() void addNewItem() @@ -185,6 +187,8 @@ const ContentListView = forwardRef( }, { command: NEXT_LIST_ITEM_KEYBOARD_COMMAND, + category: 'Notes list', + description: 'Go to next item', elements: [document.body, ...(searchBarElement ? [searchBarElement] : [])], onKeyDown: () => { if (searchBarElement === document.activeElement) { @@ -198,6 +202,8 @@ const ContentListView = forwardRef( }, { command: PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND, + category: 'Notes list', + description: 'Go to previous item', element: document.body, onKeyDown: () => { if (shouldUseTableView) { @@ -208,6 +214,8 @@ const ContentListView = forwardRef( }, { command: SEARCH_KEYBOARD_COMMAND, + category: 'General', + description: 'Toggle global search', onKeyDown: (event) => { if (searchBarElement) { event.preventDefault() @@ -225,6 +233,8 @@ const ContentListView = forwardRef( }, { command: SELECT_ALL_ITEMS_KEYBOARD_COMMAND, + category: 'General', + description: 'Select all items', onKeyDown: (event) => { const isTargetInsideContentList = (event.target as HTMLElement).closest(`#${ElementIds.ContentList}`) diff --git a/packages/web/src/javascripts/Components/EditorWidthSelectionModal/EditorWidthSelectionModal.tsx b/packages/web/src/javascripts/Components/EditorWidthSelectionModal/EditorWidthSelectionModal.tsx index 4be657416..2ae70e676 100644 --- a/packages/web/src/javascripts/Components/EditorWidthSelectionModal/EditorWidthSelectionModal.tsx +++ b/packages/web/src/javascripts/Components/EditorWidthSelectionModal/EditorWidthSelectionModal.tsx @@ -180,6 +180,8 @@ const EditorWidthSelectionModalWrapper = () => { useEffect(() => { return application.keyboardService.addCommandHandler({ command: CHANGE_EDITOR_WIDTH_COMMAND, + category: 'Current note', + description: 'Change editor width', onKeyDown: (_, data) => { if (typeof data === 'boolean' && data) { setIsGlobal(data) diff --git a/packages/web/src/javascripts/Components/Footer/QuickSettingsButton.tsx b/packages/web/src/javascripts/Components/Footer/QuickSettingsButton.tsx index cacaad449..10c857f40 100644 --- a/packages/web/src/javascripts/Components/Footer/QuickSettingsButton.tsx +++ b/packages/web/src/javascripts/Components/Footer/QuickSettingsButton.tsx @@ -25,6 +25,8 @@ const QuickSettingsButton = ({ application, isMobileNavigation = false }: Props) useEffect(() => { return commandService.addCommandHandler({ command: TOGGLE_DARK_MODE_COMMAND, + category: 'General', + description: 'Toggle dark mode', onKeyDown: () => { void application.componentManager.toggleTheme(new UIFeature(GetDarkThemeFeature())) }, diff --git a/packages/web/src/javascripts/Components/KeyboardShortcutIndicator/KeyboardShortcutIndicator.tsx b/packages/web/src/javascripts/Components/KeyboardShortcutIndicator/KeyboardShortcutIndicator.tsx index 4ab426267..3b7962979 100644 --- a/packages/web/src/javascripts/Components/KeyboardShortcutIndicator/KeyboardShortcutIndicator.tsx +++ b/packages/web/src/javascripts/Components/KeyboardShortcutIndicator/KeyboardShortcutIndicator.tsx @@ -1,31 +1,31 @@ +import { classNames } from '@standardnotes/snjs' import { PlatformedKeyboardShortcut, keyboardCharacterForModifier, - isMacPlatform, isMobilePlatform, + keyboardCharacterForKeyOrCode, } from '@standardnotes/ui-services' import { useMemo } from 'react' type Props = { - shortcut: PlatformedKeyboardShortcut + shortcut: Omit + small?: boolean + dimmed?: boolean className?: string } -export const KeyboardShortcutIndicator = ({ shortcut, className }: Props) => { - const addPluses = !isMacPlatform(shortcut.platform) - const spacingClass = addPluses ? '' : 'ml-0.5' - +export const KeyboardShortcutIndicator = ({ shortcut, small = true, dimmed = true, className }: Props) => { const keys = useMemo(() => { const modifiers = shortcut.modifiers || [] - const primaryKey = (shortcut.key || '').toUpperCase() + const primaryKey = shortcut.key + ? keyboardCharacterForKeyOrCode(shortcut.key) + : shortcut.code + ? keyboardCharacterForKeyOrCode(shortcut.code) + : undefined const results: string[] = [] - modifiers.forEach((modifier, index) => { + modifiers.forEach((modifier) => { results.push(keyboardCharacterForModifier(modifier, shortcut.platform)) - - if (addPluses && (primaryKey || index !== modifiers.length - 1)) { - results.push('+') - } }) if (primaryKey) { @@ -33,19 +33,25 @@ export const KeyboardShortcutIndicator = ({ shortcut, className }: Props) => { } return results - }, [shortcut, addPluses]) + }, [shortcut]) if (isMobilePlatform(shortcut.platform)) { return null } return ( -
+
{keys.map((key, index) => { return ( -
+ {key} -
+ ) })}
diff --git a/packages/web/src/javascripts/Components/KeyboardShortcutsHelpModal/KeyboardShortcutsHelpModal.tsx b/packages/web/src/javascripts/Components/KeyboardShortcutsHelpModal/KeyboardShortcutsHelpModal.tsx new file mode 100644 index 000000000..a4ed42cb4 --- /dev/null +++ b/packages/web/src/javascripts/Components/KeyboardShortcutsHelpModal/KeyboardShortcutsHelpModal.tsx @@ -0,0 +1,78 @@ +import { useCallback, useEffect, useState } from 'react' +import Modal from '../Modal/Modal' +import ModalOverlay from '../Modal/ModalOverlay' +import { + KeyboardService, + KeyboardShortcutCategory, + KeyboardShortcutHelpItem, + TOGGLE_KEYBOARD_SHORTCUTS_MODAL, +} from '@standardnotes/ui-services' +import { observer } from 'mobx-react-lite' +import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator' + +type GroupedItems = { + [category in KeyboardShortcutCategory]: KeyboardShortcutHelpItem[] +} + +const createGroupedItems = (items: KeyboardShortcutHelpItem[]): GroupedItems => { + const groupedItems: GroupedItems = { + 'Current note': [], + Formatting: [], + 'Super notes': [], + 'Notes list': [], + General: [], + } + return items.reduce((acc, item) => { + acc[item.category].push(item) + return acc + }, groupedItems) +} + +const Item = ({ item }: { item: KeyboardShortcutHelpItem }) => { + return ( +
+
{item.description}
+ +
+ ) +} + +const KeyboardShortcutsModal = ({ keyboardService }: { keyboardService: KeyboardService }) => { + const [isOpen, setIsOpen] = useState(false) + const [items, setItems] = useState(() => createGroupedItems(keyboardService.getRegisteredKeyboardShorcutHelpItems())) + + const close = useCallback(() => { + setIsOpen(false) + }, []) + + useEffect(() => { + return keyboardService.addCommandHandler({ + command: TOGGLE_KEYBOARD_SHORTCUTS_MODAL, + description: 'Toggle keyboard shortcuts help', + onKeyDown: () => { + setItems(createGroupedItems(keyboardService.getRegisteredKeyboardShorcutHelpItems())) + setIsOpen((open) => !open) + }, + }) + }, [keyboardService]) + + return ( + + + {Object.entries(items).map( + ([category, items]) => + items.length > 0 && ( +
+
{category}
+ {items.map((item, index) => ( + + ))} +
+ ), + )} +
+
+ ) +} + +export default observer(KeyboardShortcutsModal) diff --git a/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx b/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx index c21ee8b1e..4fd3f17d3 100644 --- a/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx +++ b/packages/web/src/javascripts/Components/LinkedItems/LinkedItemBubblesContainer.tsx @@ -60,6 +60,8 @@ const LinkedItemBubblesContainer = ({ useEffect(() => { return commandService.addCommandHandler({ command: FOCUS_TAGS_INPUT_COMMAND, + category: 'Current note', + description: 'Link tags, notes, files', onKeyDown: () => { const input = document.getElementById(ElementIds.ItemLinkAutocompleteInput) if (input) { diff --git a/packages/web/src/javascripts/Components/Preferences/PreferencesViewWrapper.tsx b/packages/web/src/javascripts/Components/Preferences/PreferencesViewWrapper.tsx index eba410c86..cc00e741a 100644 --- a/packages/web/src/javascripts/Components/Preferences/PreferencesViewWrapper.tsx +++ b/packages/web/src/javascripts/Components/Preferences/PreferencesViewWrapper.tsx @@ -15,6 +15,8 @@ const PreferencesViewWrapper: FunctionComponent = ( useEffect(() => { return commandService.addCommandHandler({ command: OPEN_PREFERENCES_COMMAND, + category: 'General', + description: 'Open preferences', onKeyDown: () => application.preferencesController.openPreferences(), }) }, [commandService, application]) diff --git a/packages/web/src/javascripts/Components/SuperEditor/Plugins/SearchPlugin/SearchPlugin.tsx b/packages/web/src/javascripts/Components/SuperEditor/Plugins/SearchPlugin/SearchPlugin.tsx index d68d482bc..823c694b4 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Plugins/SearchPlugin/SearchPlugin.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Plugins/SearchPlugin/SearchPlugin.tsx @@ -38,6 +38,8 @@ export const SearchPlugin = () => { return application.keyboardService.addCommandHandlers([ { command: SUPER_TOGGLE_SEARCH, + category: 'Super notes', + description: 'Search in current note', onKeyDown: (event) => { if (!isFocusInEditor()) { return @@ -50,6 +52,8 @@ export const SearchPlugin = () => { }, { command: SUPER_SEARCH_TOGGLE_REPLACE_MODE, + category: 'Super notes', + description: 'Search and replace in current note', onKeyDown: (event) => { if (!isFocusInEditor()) { return @@ -72,6 +76,8 @@ export const SearchPlugin = () => { }, { command: SUPER_SEARCH_NEXT_RESULT, + category: 'Super notes', + description: 'Go to next search result', onKeyDown(event) { if (!isFocusInEditor()) { return @@ -85,6 +91,8 @@ export const SearchPlugin = () => { }, { command: SUPER_SEARCH_PREVIOUS_RESULT, + category: 'Super notes', + description: 'Go to previous search result', onKeyDown(event) { if (!isFocusInEditor()) { return diff --git a/packages/web/src/javascripts/Components/SuperEditor/Plugins/ToolbarPlugin/ToolbarPlugin.tsx b/packages/web/src/javascripts/Components/SuperEditor/Plugins/ToolbarPlugin/ToolbarPlugin.tsx index 544293026..d9af1083e 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Plugins/ToolbarPlugin/ToolbarPlugin.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Plugins/ToolbarPlugin/ToolbarPlugin.tsx @@ -553,6 +553,8 @@ const ToolbarPlugin = () => { useEffect(() => { return application.keyboardService.addCommandHandler({ command: SUPER_TOGGLE_TOOLBAR, + category: 'Super notes', + description: 'Toggle Super note toolbar', onKeyDown(event) { if (isMobile) { return diff --git a/packages/web/src/javascripts/Components/SuperEditor/SuperEditor.tsx b/packages/web/src/javascripts/Components/SuperEditor/SuperEditor.tsx index 2706ca50a..95f5d252d 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/SuperEditor.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/SuperEditor.tsx @@ -28,7 +28,7 @@ import { ChangeEditorFunction, } from './Plugins/ChangeContentCallback/ChangeContentCallback' import { useCommandService } from '@/Components/CommandProvider' -import { SUPER_SHOW_MARKDOWN_PREVIEW } from '@standardnotes/ui-services' +import { SUPER_SHOW_MARKDOWN_PREVIEW, getPrimaryModifier } from '@standardnotes/ui-services' import { SuperNoteMarkdownPreview } from './SuperNoteMarkdownPreview' import GetMarkdownPlugin, { GetMarkdownPluginInterface } from './Plugins/GetMarkdownPlugin/GetMarkdownPlugin' import { useResponsiveEditorFontSize } from '@/Utils/getPlaintextFontSize' @@ -83,10 +83,48 @@ export const SuperEditor: FunctionComponent = ({ useEffect(() => { return commandService.addCommandHandler({ command: SUPER_SHOW_MARKDOWN_PREVIEW, + category: 'Super notes', + description: 'Show markdown preview for current note', onKeyDown: () => setShowMarkdownPreview(true), }) }, [commandService]) + useEffect(() => { + const platform = application.platform + const primaryModifier = getPrimaryModifier(application.platform) + + return commandService.registerExternalKeyboardShortcutHelpItems([ + { + key: 'b', + modifiers: [primaryModifier], + description: 'Bold', + category: 'Formatting', + platform: platform, + }, + { + key: 'i', + modifiers: [primaryModifier], + description: 'Italic', + category: 'Formatting', + platform: platform, + }, + { + key: 'u', + modifiers: [primaryModifier], + description: 'Underline', + category: 'Formatting', + platform: platform, + }, + { + key: 'k', + modifiers: [primaryModifier], + description: 'Link', + category: 'Formatting', + platform: platform, + }, + ]) + }, [application.platform, commandService]) + const closeMarkdownPreview = useCallback(() => { setShowMarkdownPreview(false) }, []) diff --git a/packages/web/src/javascripts/Controllers/Navigation/NavigationController.ts b/packages/web/src/javascripts/Controllers/Navigation/NavigationController.ts index a9678ebcc..809f09b54 100644 --- a/packages/web/src/javascripts/Controllers/Navigation/NavigationController.ts +++ b/packages/web/src/javascripts/Controllers/Navigation/NavigationController.ts @@ -189,6 +189,8 @@ export class NavigationController this.disposers.push( this.keyboardService.addCommandHandler({ command: CREATE_NEW_TAG_COMMAND, + category: 'General', + description: 'Create new tag', onKeyDown: () => { this.createNewTemplate() }, diff --git a/packages/web/src/javascripts/Controllers/NoteHistory/HistoryModalController.ts b/packages/web/src/javascripts/Controllers/NoteHistory/HistoryModalController.ts index 1e37e0e6f..5992e7f6a 100644 --- a/packages/web/src/javascripts/Controllers/NoteHistory/HistoryModalController.ts +++ b/packages/web/src/javascripts/Controllers/NoteHistory/HistoryModalController.ts @@ -27,6 +27,8 @@ export class HistoryModalController extends AbstractViewController { this.disposers.push( keyboardService.addCommandHandler({ command: OPEN_NOTE_HISTORY_COMMAND, + category: 'Current note', + description: 'Open note history', onKeyDown: () => { this.openModal(notesController.firstSelectedNote) return true diff --git a/packages/web/src/javascripts/Controllers/NotesController/NotesController.ts b/packages/web/src/javascripts/Controllers/NotesController/NotesController.ts index df952652f..9823a58c5 100644 --- a/packages/web/src/javascripts/Controllers/NotesController/NotesController.ts +++ b/packages/web/src/javascripts/Controllers/NotesController/NotesController.ts @@ -90,12 +90,16 @@ export class NotesController this.disposers.push( this.keyboardService.addCommandHandler({ command: PIN_NOTE_COMMAND, + category: 'Current note', + description: 'Pin current note', onKeyDown: () => { this.togglePinSelectedNotes() }, }), this.keyboardService.addCommandHandler({ command: STAR_NOTE_COMMAND, + category: 'Current note', + description: 'Star current note', onKeyDown: () => { this.toggleStarSelectedNotes() }, diff --git a/packages/web/src/javascripts/Controllers/PaneController/PaneController.ts b/packages/web/src/javascripts/Controllers/PaneController/PaneController.ts index 281048d36..3d8cfaaf9 100644 --- a/packages/web/src/javascripts/Controllers/PaneController/PaneController.ts +++ b/packages/web/src/javascripts/Controllers/PaneController/PaneController.ts @@ -102,6 +102,8 @@ export class PaneController extends AbstractViewController implements InternalEv this.disposers.push( keyboardService.addCommandHandler({ command: TOGGLE_FOCUS_MODE_COMMAND, + category: 'General', + description: 'Toggle focus mode', onKeyDown: (event) => { event.preventDefault() this.setFocusModeEnabled(!this.focusModeEnabled) @@ -110,6 +112,8 @@ export class PaneController extends AbstractViewController implements InternalEv }), keyboardService.addCommandHandler({ command: TOGGLE_LIST_PANE_KEYBOARD_COMMAND, + category: 'General', + description: 'Toggle notes panel', onKeyDown: (event) => { event.preventDefault() this.toggleListPane() @@ -117,6 +121,8 @@ export class PaneController extends AbstractViewController implements InternalEv }), keyboardService.addCommandHandler({ command: TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND, + category: 'General', + description: 'Toggle tags panel', onKeyDown: (event) => { event.preventDefault() this.toggleNavigationPane() diff --git a/packages/web/src/javascripts/Utils/Utils.ts b/packages/web/src/javascripts/Utils/Utils.ts index 282cab9c6..a65a604cc 100644 --- a/packages/web/src/javascripts/Utils/Utils.ts +++ b/packages/web/src/javascripts/Utils/Utils.ts @@ -2,7 +2,7 @@ import { DeviceInterface, MobileDeviceInterface, Platform, platformFromString } import { IsDesktopPlatform, IsWebPlatform } from '@/Constants/Version' import { EMAIL_REGEX } from '../Constants/Constants' import { MutuallyExclusiveMediaQueryBreakpoints } from '@/Hooks/useMediaQuery' -import { isIOS } from '@standardnotes/ui-services' +import { isAndroid, isIOS } from '@standardnotes/ui-services' declare const process: { env: { @@ -16,9 +16,9 @@ export function getPlatformString() { try { const platform = navigator.platform.toLowerCase() let trimmed = '' - if (platform.includes('iphone')) { + if (platform.includes('iphone') || isIOS()) { trimmed = 'ios' - } else if (platform.includes('android')) { + } else if (platform.includes('android') || isAndroid()) { trimmed = 'android' } else if (platform.includes('mac')) { trimmed = 'mac'