From 18dbf226e04455aa6ead6747a375de893c28d6b8 Mon Sep 17 00:00:00 2001 From: Rias Date: Fri, 19 Jun 2026 11:33:23 +0200 Subject: [PATCH] Add inline code and quote shortcuts, matching GitHub's markdown editor --- README.md | 4 +++ src/shortcuts.js | 50 +++++++++++++++----------- test/keyboard-accessibility.test.js | 54 +++++++++++++++++++++++++++++ website/demo.html | 2 +- website/index.html | 4 +-- 5 files changed, 91 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index d261b50..64ba4e9 100644 --- a/README.md +++ b/README.md @@ -255,9 +255,11 @@ The toolbar and keyboard shortcuts work together seamlessly: - **Cmd/Ctrl + B** - Bold - **Cmd/Ctrl + I** - Italic +- **Cmd/Ctrl + E** - Inline code - **Cmd/Ctrl + K** - Insert link - **Cmd/Ctrl + Shift + 7** - Numbered list - **Cmd/Ctrl + Shift + 8** - Bullet list +- **Cmd/Ctrl + Shift + .** - Quote - **Cmd/Ctrl + ]** - Indent current line or selection - **Cmd/Ctrl + [** - Outdent current line or selection - **Tab / Shift+Tab** - Indent or outdent selected lines @@ -686,9 +688,11 @@ OverType.themes.cave |----------|--------| | Cmd/Ctrl + B | Toggle bold | | Cmd/Ctrl + I | Toggle italic | +| Cmd/Ctrl + E | Toggle inline code | | Cmd/Ctrl + K | Insert link | | Cmd/Ctrl + Shift + 7 | Toggle numbered list | | Cmd/Ctrl + Shift + 8 | Toggle bullet list | +| Cmd/Ctrl + Shift + . | Toggle quote | | Cmd/Ctrl + ] | Indent current line or selection | | Cmd/Ctrl + [ | Outdent current line or selection | | Tab / Shift+Tab | Indent or outdent selected lines | diff --git a/src/shortcuts.js b/src/shortcuts.js index b9e7856..4e3f44d 100644 --- a/src/shortcuts.js +++ b/src/shortcuts.js @@ -17,18 +17,17 @@ export class ShortcutsManager { * @returns {boolean} Whether the event was handled */ handleKeydown(event) { - const isMac = navigator.platform.toLowerCase().includes('mac'); - const modKey = isMac ? event.metaKey : event.ctrlKey; + const { modKey, matchesKey } = this.getShortcutInfo(event); if (!modKey) return false; - if (event.key === ']') { + if (!event.shiftKey && matchesKey(']', 'bracketright')) { event.preventDefault(); this.editor.indentSelection(); return true; } - if (event.key === '[') { + if (!event.shiftKey && matchesKey('[', 'bracketleft')) { event.preventDefault(); this.editor.outdentSelection(); return true; @@ -36,22 +35,17 @@ export class ShortcutsManager { let actionId = null; - switch (event.key.toLowerCase()) { - case 'b': - if (!event.shiftKey) actionId = 'toggleBold'; - break; - case 'i': - if (!event.shiftKey) actionId = 'toggleItalic'; - break; - case 'k': - if (!event.shiftKey) actionId = 'insertLink'; - break; - case '7': - if (event.shiftKey) actionId = 'toggleNumberedList'; - break; - case '8': - if (event.shiftKey) actionId = 'toggleBulletList'; - break; + if (!event.shiftKey) { + if (matchesKey('b', 'keyb')) actionId = 'toggleBold'; + if (matchesKey('e', 'keye')) actionId = 'toggleCode'; + if (matchesKey('i', 'keyi')) actionId = 'toggleItalic'; + if (matchesKey('k', 'keyk')) actionId = 'insertLink'; + } + + if (event.shiftKey) { + if (matchesKey('7', '&', 'digit7')) actionId = 'toggleNumberedList'; + if (matchesKey('8', '*', 'digit8')) actionId = 'toggleBulletList'; + if (matchesKey('.', '>', 'period')) actionId = 'toggleQuote'; } if (actionId) { @@ -63,6 +57,22 @@ export class ShortcutsManager { return false; } + /** + * Get normalized shortcut state for cross-platform matching. + * @param {KeyboardEvent} event - The keyboard event + * @returns {{modKey: boolean, matchesKey: Function}} + */ + getShortcutInfo(event) { + const isMac = navigator.platform.toLowerCase().includes('mac'); + const modKey = isMac ? event.metaKey : event.ctrlKey; + + const key = event.key.toLowerCase(); + const code = (event.code || '').toLowerCase(); + const matchesKey = (...values) => values.includes(key) || values.includes(code); + + return { modKey, matchesKey }; + } + /** * Cleanup */ diff --git a/test/keyboard-accessibility.test.js b/test/keyboard-accessibility.test.js index 11f8cc6..8a5c900 100644 --- a/test/keyboard-accessibility.test.js +++ b/test/keyboard-accessibility.test.js @@ -171,6 +171,60 @@ console.log('\n⌘ Explicit indent shortcuts\n'); assert(selectedText(editor) === ' one\n two', 'Ctrl+] preserves selected lines', 'Indented selected lines should remain selected'); })(); +console.log('\n⌘ GitHub markdown shortcuts\n'); + +(() => { + const editor = createEditor(); + let actionId = null; + editor.performAction = (id) => { + actionId = id; + }; + + const event = dispatchKey(editor, 'e', { ctrlKey: true, code: 'KeyE' }); + + assert(event.defaultPrevented, 'Ctrl+E is prevented', 'Ctrl+E should become a markdown command'); + assert(actionId === 'toggleCode', 'Ctrl+E toggles inline code', 'Ctrl+E should dispatch toggleCode'); +})(); + +(() => { + const editor = createEditor(); + let actionId = null; + editor.performAction = (id) => { + actionId = id; + }; + + const event = dispatchKey(editor, '>', { ctrlKey: true, shiftKey: true, code: 'Period' }); + + assert(event.defaultPrevented, 'Ctrl+Shift+. is prevented', 'Ctrl+Shift+. should become a markdown command'); + assert(actionId === 'toggleQuote', 'Ctrl+Shift+. toggles quote', 'Ctrl+Shift+. should dispatch toggleQuote'); +})(); + +(() => { + const editor = createEditor(); + let actionId = null; + editor.performAction = (id) => { + actionId = id; + }; + + const event = dispatchKey(editor, '&', { ctrlKey: true, shiftKey: true, code: 'Digit7' }); + + assert(event.defaultPrevented, 'Ctrl+Shift+7 is prevented with shifted key', 'Ctrl+Shift+7 should stay handled'); + assert(actionId === 'toggleNumberedList', 'Ctrl+Shift+7 toggles numbered list', 'Ctrl+Shift+7 should dispatch toggleNumberedList'); +})(); + +(() => { + const editor = createEditor(); + let actionId = null; + editor.performAction = (id) => { + actionId = id; + }; + + const event = dispatchKey(editor, '*', { ctrlKey: true, shiftKey: true, code: 'Digit8' }); + + assert(event.defaultPrevented, 'Ctrl+Shift+8 is prevented with shifted key', 'Ctrl+Shift+8 should stay handled'); + assert(actionId === 'toggleBulletList', 'Ctrl+Shift+8 toggles bullet list', 'Ctrl+Shift+8 should dispatch toggleBulletList'); +})(); + console.log('\nšŸ”’ Readonly and disabled no-op behavior\n'); (() => { diff --git a/website/demo.html b/website/demo.html index cb812fb..39fb85f 100644 --- a/website/demo.html +++ b/website/demo.html @@ -245,7 +245,7 @@

Web Component

START BUILDING ## Text Formatting - **Bold** text (Cmd/Ctrl + B) - *Italic* text (Cmd/Ctrl + I) -- \`inline code\` (Cmd/Ctrl + \`) +- \`inline code\` (Cmd/Ctrl + E) ## Lists & Structure 1. Numbered lists 2. Bullet points 3. Task lists -4. Block quotes +4. Block quotes (Cmd/Ctrl + Shift + .) ## Code Blocks \`\`\`