diff --git a/src/common/keyboard/shortcuts.ts b/src/common/keyboard/shortcuts.ts index 9fbf6d7f5f..c265f7047b 100644 --- a/src/common/keyboard/shortcuts.ts +++ b/src/common/keyboard/shortcuts.ts @@ -1,8 +1,27 @@ import { tinykeys } from "tinykeys"; import { canOverrideAlphanumericInput } from "../dom/can-override-input"; +/** + * A function to handle a keyboard shortcut. + */ export type ShortcutHandler = (event: KeyboardEvent) => void; +/** + * Configuration for a keyboard shortcut. + */ +export interface ShortcutConfig { + /** + * The handler function to call when the shortcut is triggered. + */ + handler: ShortcutHandler; + /** + * If true, the shortcut will be triggered even when the user is selecting text. + * By default (false), shortcuts are blocked during text selection to avoid + * interrupting copy/paste operations. + */ + allowWhenTextSelected?: boolean; +} + interface ShortcutEntry { /** * The key that the shortcut is registered to. @@ -17,25 +36,26 @@ interface ShortcutEntry { /** * Register keyboard shortcuts using tinykeys. * - * @param shortcuts - Key combinations mapped to handler functions. + * @param shortcuts - Key combinations mapped to shortcut configurations. * @returns A function to remove the shortcuts. */ function registerShortcuts( - shortcuts: Record + shortcuts: Record ): () => void { const wrappedShortcuts: Record = {}; - Object.entries(shortcuts).forEach(([key, handler]) => { + Object.entries(shortcuts).forEach(([key, config]) => { wrappedShortcuts[key] = (event: KeyboardEvent) => { // Don't capture the event if the user is focused on an input field if (!canOverrideAlphanumericInput(event.composedPath())) { return; } - // Don't capture the event if the user is selecting text - if (window.getSelection()?.toString()) { + // Don't capture the event if the user is selecting text to avoid + // interrupting copy/paste operations or text manipulation + if (!config.allowWhenTextSelected && window.getSelection()?.toString()) { return; } - handler(event); + config.handler(event); }; }); @@ -51,12 +71,12 @@ export class ShortcutManager { /** * Add a group of keyboard shortcuts to the manager. * - * @param shortcuts - Key combinations mapped to handler functions. + * @param shortcuts - Key combinations mapped to shortcut configurations. * Uses tinykeys syntax. See https://github.com/jamiebuilds/tinykeys#usage. */ - public add(shortcuts: Record) { - Object.entries(shortcuts).forEach(([key, handler]) => { - const disposer = registerShortcuts({ [key]: handler }); + public add(shortcuts: Record) { + Object.entries(shortcuts).forEach(([key, config]) => { + const disposer = registerShortcuts({ [key]: config }); const entry: ShortcutEntry = { key, disposer }; this.shortcutEntries.push(entry); }); diff --git a/src/state/quick-bar-mixin.ts b/src/state/quick-bar-mixin.ts index 3b3437a15a..8461ce9592 100644 --- a/src/state/quick-bar-mixin.ts +++ b/src/state/quick-bar-mixin.ts @@ -64,19 +64,19 @@ export default >(superClass: T) => const shortcutManager = new ShortcutManager(); shortcutManager.add({ // Those are for latin keyboards that have e, c, m keys - e: (ev) => this._showQuickBar(ev), - c: (ev) => this._showQuickBar(ev, QuickBarMode.Command), - m: (ev) => this._createMyLink(ev), - a: (ev) => this._showVoiceCommandDialog(ev), - d: (ev) => this._showQuickBar(ev, QuickBarMode.Device), + e: { handler: (ev) => this._showQuickBar(ev) }, + c: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Command) }, + m: { handler: (ev) => this._createMyLink(ev) }, + a: { handler: (ev) => this._showVoiceCommandDialog(ev) }, + d: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Device) }, // Workaround see https://github.com/jamiebuilds/tinykeys/issues/130 - "Shift+?": (ev) => this._showShortcutDialog(ev), + "Shift+?": { handler: (ev) => this._showShortcutDialog(ev) }, // Those are fallbacks for non-latin keyboards that don't have e, c, m keys (qwerty-based shortcuts) - KeyE: (ev) => this._showQuickBar(ev), - KeyC: (ev) => this._showQuickBar(ev, QuickBarMode.Command), - KeyM: (ev) => this._createMyLink(ev), - KeyA: (ev) => this._showVoiceCommandDialog(ev), - KeyD: (ev) => this._showQuickBar(ev, QuickBarMode.Device), + KeyE: { handler: (ev) => this._showQuickBar(ev) }, + KeyC: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Command) }, + KeyM: { handler: (ev) => this._createMyLink(ev) }, + KeyA: { handler: (ev) => this._showVoiceCommandDialog(ev) }, + KeyD: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Device) }, }); }