From b27dd3fc96638f7b288a5aca7dd2119a689f42e4 Mon Sep 17 00:00:00 2001 From: Bauke Date: Wed, 13 Dec 2023 14:03:45 +0100 Subject: [PATCH] Add keyboard shortcut functionality for Markdown snippets. --- .../features/markdown-toolbar.tsx | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/source/content-scripts/features/markdown-toolbar.tsx b/source/content-scripts/features/markdown-toolbar.tsx index 8ff4157..e75c5d2 100644 --- a/source/content-scripts/features/markdown-toolbar.tsx +++ b/source/content-scripts/features/markdown-toolbar.tsx @@ -3,14 +3,47 @@ import {render} from "preact"; import {log, querySelectorAll} from "../../utilities/exports.js"; import { type MarkdownSnippet, + type ProcessedSnippetShortcut, MarkdownSnippetMarker, + processSnippetShortcut, } from "../../storage/exports.js"; +/** Type shorthand for a snippet with its processed shortcut. */ +type ProcessedSnippetTuple = [Value, ProcessedSnippetShortcut]; + export function runMarkdownToolbarFeature( snippets: Array>, ) { const count = addToolbarsToTextareas(snippets); if (count > 0) { + // Process all the snippet shortcuts outside of the keydown handler so we + // don't have to process them on every keydown event. + const snippetsWithProcessedShortcuts: ProcessedSnippetTuple[] = snippets + // Exclude snippets that don't have shortcuts defined. + .filter((snippet) => snippet.value.shortcut !== undefined) + // Map the result as a tuple of the snippet and the processed shortcut. + .map( + (snippet) => + [ + snippet, + processSnippetShortcut(snippet.value.shortcut!), + ] satisfies ProcessedSnippetTuple, + ); + + // Only add the keydown listener if it hasn't already been added and if + // there are any snippets with shortcuts to listen for. + if ( + !document.body.dataset.trxMarkdownToolbarKeydownListening && + snippetsWithProcessedShortcuts.length > 0 + ) { + document.addEventListener("keydown", async (event: KeyboardEvent) => { + await keyDownHandler(event, snippetsWithProcessedShortcuts); + }); + + document.body.dataset.trxMarkdownToolbarKeydownListening = "true"; + log("Markdown Toolbar: Listening for keyboard shortcuts."); + } + log(`Markdown Toolbar: Initialized for ${count} textareas.`); } } @@ -188,3 +221,48 @@ function insertSnippet(props: Omit, "allSnippets">) { textarea.selectionEnd = selectionEnd + cursorPosition; } + +/** + * The global handler for the `keydown` event. + * + * Keydown is chosen over keypress or keyup because it can be used to prevent + * the browser's keyboard shortcuts using `event.preventDefault()`. + */ +async function keyDownHandler( + event: KeyboardEvent, + snippets: ProcessedSnippetTuple[], +): Promise { + const textarea = event.target; + + // Markdown toolbars are only ever added for `