Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 |
Expand Down
50 changes: 30 additions & 20 deletions src/shortcuts.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,35 @@ 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;
}

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) {
Expand All @@ -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
*/
Expand Down
54 changes: 54 additions & 0 deletions test/keyboard-accessibility.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

(() => {
Expand Down
2 changes: 1 addition & 1 deletion website/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ <h2>Web Component</h2>
</div>
<overtype-editor
id="wc-editor"
value="# Web Component Demo\n\nThis demo showcases the **native** `<overtype-editor>` custom element with full functionality!\n\n## Features\n\n- **Real-time** markdown editing\n- *Live* preview updates\n- `Keyboard shortcuts` support\n- Multiple themes (Solar/Cave)\n- Built-in toolbar\n- Auto-resize capability\n- Event handling\n\n## Try These Features\n\n1. **Bold text** with Cmd/Ctrl + B\n2. *Italic text* with Cmd/Ctrl + I\n3. `Inline code` with Cmd/Ctrl + K\n4. [Links](https://github.com) work great!\n5. Create lists with Cmd/Ctrl + Shift + 8\n\n### Code Blocks\n\n```javascript\n// JavaScript code with syntax highlighting\nfunction greet(name) {\n return `Hello, ${name}!`;\n}\n\nconsole.log(greet('OverType'));\n```\n\n### Blockquotes\n\n> This is a blockquote example.\n> It can span multiple lines\n> and looks great in the preview!\n\n---\n\n**Happy editing!** 🎉\n\n*The Web Component provides the same powerful features as the regular OverType editor, but as a native HTML element.*"
value="# Web Component Demo\n\nThis demo showcases the **native** `<overtype-editor>` custom element with full functionality!\n\n## Features\n\n- **Real-time** markdown editing\n- *Live* preview updates\n- `Keyboard shortcuts` support\n- Multiple themes (Solar/Cave)\n- Built-in toolbar\n- Auto-resize capability\n- Event handling\n\n## Try These Features\n\n1. **Bold text** with Cmd/Ctrl + B\n2. *Italic text* with Cmd/Ctrl + I\n3. `Inline code` with Cmd/Ctrl + E\n4. [Links](https://github.com) work great!\n5. Create lists with Cmd/Ctrl + Shift + 8\n6. Quote text with Cmd/Ctrl + Shift + .\n\n### Code Blocks\n\n```javascript\n// JavaScript code with syntax highlighting\nfunction greet(name) {\n return `Hello, ${name}!`;\n}\n\nconsole.log(greet('OverType'));\n```\n\n### Blockquotes\n\n> This is a blockquote example.\n> It can span multiple lines\n> and looks great in the preview!\n\n---\n\n**Happy editing!** 🎉\n\n*The Web Component provides the same powerful features as the regular OverType editor, but as a native HTML element.*"
theme="solar"
height="300px"
toolbar
Expand Down
4 changes: 2 additions & 2 deletions website/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -586,13 +586,13 @@ <h2>START BUILDING</h2>
## 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
\`\`\`
Expand Down