feat: Added "Keyboard shortcuts" help dialog. Can be opened by pressing Shift + ?
This commit is contained in:
@@ -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]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<KeyboardCommandHandler>()
|
||||
private commandMap = new Map<KeyboardCommand, KeyboardShortcut>()
|
||||
|
||||
private keyboardShortcutHelpItems = new Set<KeyboardShortcutHelpItem>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PlatformedKeyboardShortcut, 'command'> & {
|
||||
category: KeyboardShortcutCategory
|
||||
description: string
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
20
packages/ui-services/src/Keyboard/keyboardCharacterForKey.ts
Normal file
20
packages/ui-services/src/Keyboard/keyboardCharacterForKey.ts
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -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<Props> = ({ 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 (
|
||||
<>
|
||||
<div className="mb-1 mt-1 hidden items-center justify-between px-4 md:flex md:px-3">
|
||||
@@ -187,6 +193,19 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({ setMenuPane, closeMenu,
|
||||
</div>
|
||||
<span className="text-neutral">v{application.version}</span>
|
||||
</MenuItem>
|
||||
{!isMobilePlatform(application.platform) && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
application.keyboardService.triggerCommand(TOGGLE_KEYBOARD_SHORTCUTS_MODAL)
|
||||
}}
|
||||
>
|
||||
<Icon type="keyboard" className={iconClassName} />
|
||||
Keyboard shortcuts
|
||||
{keyboardShortcutsHelpShortcut && (
|
||||
<KeyboardShortcutIndicator shortcut={keyboardShortcutsHelpShortcut} className="ml-auto" />
|
||||
)}
|
||||
</MenuItem>
|
||||
)}
|
||||
</MenuSection>
|
||||
{user ? (
|
||||
<MenuSection>
|
||||
|
||||
@@ -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<Props> = ({ application, mainApplicatio
|
||||
<EditorWidthSelectionModalWrapper />
|
||||
<ConfirmDeleteAccountContainer application={application} />
|
||||
<ImportModal importModalController={application.importModalController} />
|
||||
<KeyboardShortcutsModal keyboardService={application.keyboardService} />
|
||||
</>
|
||||
{application.routeService.isDotOrg && <DotOrgNotice />}
|
||||
{isIOS() && <IosKeyboardClose />}
|
||||
|
||||
@@ -57,6 +57,8 @@ const ChangeEditorButton: FunctionComponent<Props> = ({ noteViewController, onCl
|
||||
useEffect(() => {
|
||||
return application.keyboardService.addCommandHandler({
|
||||
command: CHANGE_EDITOR_COMMAND,
|
||||
category: 'Current note',
|
||||
description: 'Change note type',
|
||||
onKeyDown: () => {
|
||||
void toggleMenu()
|
||||
},
|
||||
|
||||
@@ -178,6 +178,8 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
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<HTMLDivElement, Props>(
|
||||
},
|
||||
{
|
||||
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<HTMLDivElement, Props>(
|
||||
},
|
||||
{
|
||||
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<HTMLDivElement, Props>(
|
||||
},
|
||||
{
|
||||
command: SEARCH_KEYBOARD_COMMAND,
|
||||
category: 'General',
|
||||
description: 'Toggle global search',
|
||||
onKeyDown: (event) => {
|
||||
if (searchBarElement) {
|
||||
event.preventDefault()
|
||||
@@ -225,6 +233,8 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
},
|
||||
{
|
||||
command: SELECT_ALL_ITEMS_KEYBOARD_COMMAND,
|
||||
category: 'General',
|
||||
description: 'Select all items',
|
||||
onKeyDown: (event) => {
|
||||
const isTargetInsideContentList = (event.target as HTMLElement).closest(`#${ElementIds.ContentList}`)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()))
|
||||
},
|
||||
|
||||
@@ -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<PlatformedKeyboardShortcut, 'command'>
|
||||
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 (
|
||||
<div className={`keyboard-shortcut-indicator flex opacity-[0.35] ${className}`}>
|
||||
<div className={classNames('flex items-center gap-1', dimmed && 'opacity-70', className)}>
|
||||
{keys.map((key, index) => {
|
||||
return (
|
||||
<div className={index !== 0 ? spacingClass : ''} key={index}>
|
||||
<kbd
|
||||
className={classNames(
|
||||
'rounded border-[0.5px] border-passive-3 bg-default p-1 text-center font-sans capitalize leading-none text-text shadow-[var(--tw-shadow-color)_0px_2px_0px_0px] shadow-passive-3',
|
||||
small ? 'text-[length:0.65rem]' : 'text-xs',
|
||||
)}
|
||||
key={index}
|
||||
>
|
||||
{key}
|
||||
</div>
|
||||
</kbd>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -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 (
|
||||
<div className="flex items-center gap-2 px-4 py-2.5">
|
||||
<div>{item.description}</div>
|
||||
<KeyboardShortcutIndicator className="ml-auto" shortcut={item} small={false} dimmed={false} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<ModalOverlay isOpen={isOpen} close={close}>
|
||||
<Modal title="Keyboard shortcuts" close={close}>
|
||||
{Object.entries(items).map(
|
||||
([category, items]) =>
|
||||
items.length > 0 && (
|
||||
<div key={category}>
|
||||
<div className="p-4 pb-0.5 pt-4 text-base font-semibold capitalize">{category}</div>
|
||||
{items.map((item, index) => (
|
||||
<Item item={item} key={index} />
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</Modal>
|
||||
</ModalOverlay>
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(KeyboardShortcutsModal)
|
||||
@@ -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) {
|
||||
|
||||
@@ -15,6 +15,8 @@ const PreferencesViewWrapper: FunctionComponent<PreferencesViewWrapperProps> = (
|
||||
useEffect(() => {
|
||||
return commandService.addCommandHandler({
|
||||
command: OPEN_PREFERENCES_COMMAND,
|
||||
category: 'General',
|
||||
description: 'Open preferences',
|
||||
onKeyDown: () => application.preferencesController.openPreferences(),
|
||||
})
|
||||
}, [commandService, application])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Props> = ({
|
||||
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)
|
||||
}, [])
|
||||
|
||||
@@ -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()
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
},
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user