From 3b7f27bf1ce9bb865a391e5dd7d62484a6592e23 Mon Sep 17 00:00:00 2001 From: rameel Date: Sat, 4 Apr 2026 20:56:21 +0500 Subject: [PATCH 1/5] Fix hotkey aliases - reverse mapping direction - remove invalid aliases - add missing key aliases --- src/hotkey.ts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/hotkey.ts b/src/hotkey.ts index 3c1f3c0..7b2efca 100644 --- a/src/hotkey.ts +++ b/src/hotkey.ts @@ -16,17 +16,15 @@ const aliases: Record = { "windows": "meta", "cmd": "meta", "command": "meta", - "comma": ",", - "period": ".", - "quote": "\"", - "singlequote": "'", - "colon": ":", - "semicolon": ";", - "plus": "+", - "minus": "-", - "tilde": "~", - "equal": "=", - "slash": "/" + ",": "comma", + ".": "period", + "/": "slash", + "\\": "backslash", + ";": "semicolon", + "=": "equal", + "`": "backquote", + "]": "bracketright", + "[": "bracketleft", }; const control_keys: string[] = [ From c5b58ff629fa64afbba3f2d388f5aedf5bfcaf1a Mon Sep 17 00:00:00 2001 From: rameel Date: Sat, 4 Apr 2026 21:00:56 +0500 Subject: [PATCH 2/5] Fix missing digit handling --- src/hotkey.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hotkey.ts b/src/hotkey.ts index 7b2efca..cd64c33 100644 --- a/src/hotkey.ts +++ b/src/hotkey.ts @@ -115,7 +115,9 @@ function describe(hotkey: string): Hotkey { k.length || error_invalid_key(hotkey); k = k.toUpperCase(); - data.code = k.length === 1 && k >= 'A' && k <= 'Z' ? `KEY${k}` : k; + data.code = /^[A-Z]$/.test(k) ? `KEY${k}` + : /^[0-9]$/.test(k) ? `DIGIT${k}` + : k; break; } return data; From 959b0190c558360fb70b659ab8d64bd7661f925e Mon Sep 17 00:00:00 2001 From: rameel Date: Sat, 4 Apr 2026 21:02:39 +0500 Subject: [PATCH 3/5] Add tests --- tests/aliases.spec.js | 200 ++++++++++++++++++++++++++++++++ tests/{ => assets}/generic.html | 2 +- tests/hotkey.spec.js | 31 ++++- 3 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 tests/aliases.spec.js rename tests/{ => assets}/generic.html (89%) diff --git a/tests/aliases.spec.js b/tests/aliases.spec.js new file mode 100644 index 0000000..db50b9d --- /dev/null +++ b/tests/aliases.spec.js @@ -0,0 +1,200 @@ +import process from "node:process"; +import { test, expect } from "@playwright/test"; + +test.beforeEach(async ({ page }) => { + await page.goto(`file://${process.cwd()}/tests/assets/generic.html`); + await page.evaluate(() => { + window.hotkeyTriggered = false; + }); +}); + +test("should trigger when using 'Esc' alias for Escape", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Esc", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.press("Escape"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Del' alias for Delete", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Del", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.press("Delete"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ins' alias for Insert", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ins", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.press("Insert"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'PgUp' alias for PageUp", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "PgUp", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.press("PageUp"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'PgDn' alias for PageDown", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "PgDn", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.press("PageDown"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+,' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+,", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.press(","); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+.' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+.", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.press("."); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+/' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+/", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.press("/"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+\\' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+\\", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.press("\\"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+;' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+;", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.press(";"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+=' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+=", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.press("="); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+`' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+`", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.press("`"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+]' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+]", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.press("]"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+[' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+[", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.press("["); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); diff --git a/tests/generic.html b/tests/assets/generic.html similarity index 89% rename from tests/generic.html rename to tests/assets/generic.html index bebbcd2..0cb583b 100644 --- a/tests/generic.html +++ b/tests/assets/generic.html @@ -16,6 +16,6 @@ - + diff --git a/tests/hotkey.spec.js b/tests/hotkey.spec.js index f3aa8a7..80105e5 100644 --- a/tests/hotkey.spec.js +++ b/tests/hotkey.spec.js @@ -1,7 +1,8 @@ +import process from "node:process"; import { test, expect } from "@playwright/test"; test.beforeEach(async ({ page }) => { - await page.goto(`file://${process.cwd()}/tests/generic.html`); + await page.goto(`file://${process.cwd()}/tests/assets/generic.html`); await page.evaluate(() => { window.hotkeyTriggered = false; }); @@ -399,3 +400,31 @@ test("should trigger multiple times without 'once' option", async ({ page }) => const count = await page.evaluate(() => window.hotkeyCount); expect(count).toBe(2); }); + +test("should trigger when Ctrl+1 is pressed", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+1", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.press("1"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when Alt+0 is pressed", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Alt+0", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Alt"); + await page.keyboard.press("0"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); From d5a07f2648a52b53f07d46df5f093e3a7e40f669 Mon Sep 17 00:00:00 2001 From: rameel Date: Sat, 4 Apr 2026 21:09:49 +0500 Subject: [PATCH 4/5] Clean up --- tests/aliases.spec.js | 18 +++++++++--------- tests/hotkey.spec.js | 27 ++++++++++++++------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/tests/aliases.spec.js b/tests/aliases.spec.js index db50b9d..f95ae26 100644 --- a/tests/aliases.spec.js +++ b/tests/aliases.spec.js @@ -81,7 +81,7 @@ test("should trigger when using 'Ctrl+,' literal character alias", async ({ page }); await page.keyboard.down("Control"); - await page.keyboard.press(","); + await page.keyboard.down(","); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -95,7 +95,7 @@ test("should trigger when using 'Ctrl+.' literal character alias", async ({ page }); await page.keyboard.down("Control"); - await page.keyboard.press("."); + await page.keyboard.down("."); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -109,7 +109,7 @@ test("should trigger when using 'Ctrl+/' literal character alias", async ({ page }); await page.keyboard.down("Control"); - await page.keyboard.press("/"); + await page.keyboard.down("/"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -123,7 +123,7 @@ test("should trigger when using 'Ctrl+\\' literal character alias", async ({ pag }); await page.keyboard.down("Control"); - await page.keyboard.press("\\"); + await page.keyboard.down("\\"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -137,7 +137,7 @@ test("should trigger when using 'Ctrl+;' literal character alias", async ({ page }); await page.keyboard.down("Control"); - await page.keyboard.press(";"); + await page.keyboard.down(";"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -151,7 +151,7 @@ test("should trigger when using 'Ctrl+=' literal character alias", async ({ page }); await page.keyboard.down("Control"); - await page.keyboard.press("="); + await page.keyboard.down("="); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -165,7 +165,7 @@ test("should trigger when using 'Ctrl+`' literal character alias", async ({ page }); await page.keyboard.down("Control"); - await page.keyboard.press("`"); + await page.keyboard.down("`"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -179,7 +179,7 @@ test("should trigger when using 'Ctrl+]' literal character alias", async ({ page }); await page.keyboard.down("Control"); - await page.keyboard.press("]"); + await page.keyboard.down("]"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -193,7 +193,7 @@ test("should trigger when using 'Ctrl+[' literal character alias", async ({ page }); await page.keyboard.down("Control"); - await page.keyboard.press("["); + await page.keyboard.down("["); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); diff --git a/tests/hotkey.spec.js b/tests/hotkey.spec.js index 80105e5..2a60d06 100644 --- a/tests/hotkey.spec.js +++ b/tests/hotkey.spec.js @@ -16,7 +16,7 @@ test("should trigger when Ctrl+Up is pressed on document", async ({ page }) => { }); await page.keyboard.down("Control"); - await page.keyboard.press("ArrowUp"); + await page.keyboard.down("ArrowUp"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -30,7 +30,7 @@ test("should trigger when Ctrl+Up is pressed on window", async ({ page }) => { }); await page.keyboard.down("Control"); - await page.keyboard.press("ArrowUp"); + await page.keyboard.down("ArrowUp"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -48,7 +48,7 @@ test("should trigger when Ctrl+K is pressed on a specific element", async ({ pag await page.locator("#text").focus(); await page.keyboard.down("Control"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -64,7 +64,7 @@ test("should trigger when Shift+K is pressed on an element matching the selector await page.locator("#text").focus(); await page.keyboard.down("Shift"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -82,7 +82,7 @@ test("should trigger when Ctrl+Alt+Shift+K is pressed", async ({ page }) => { await page.keyboard.down("Control"); await page.keyboard.down("Alt"); await page.keyboard.down("Shift"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -99,7 +99,7 @@ test("should trigger when Alt+Shift+K is pressed", async ({ page }) => { await page.keyboard.down("Alt"); await page.keyboard.down("Shift"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -115,7 +115,7 @@ test("should trigger when Alt+K is pressed", async ({ page }) => { await page.locator("#text").focus(); await page.keyboard.down("Alt"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -146,7 +146,7 @@ test("should not trigger hotkey when additional modifier keys are pressed", asyn await page.keyboard.down("Shift"); await page.keyboard.down("Alt"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(false); @@ -164,7 +164,7 @@ test("should not trigger on elements with [data-hotkey-ignore]", async ({ page } await page.locator("#ignored").focus(); await page.keyboard.down("Control"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(false); @@ -182,7 +182,7 @@ test("should not trigger when parent has [data-hotkey-ignore]", async ({ page }) await page.locator("#ignored-via-parent").focus(); await page.keyboard.down("Control"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(false); @@ -263,7 +263,7 @@ test("should only trigger on trusted events when 'trusted' option is set to true await page.locator("#text").focus(); await page.keyboard.down("Control"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -394,6 +394,7 @@ test("should trigger multiple times without 'once' option", async ({ page }) => await page.locator("#text").focus(); + // Press the hotkey twice await page.keyboard.press("Control+k"); await page.keyboard.press("Control+k"); @@ -409,7 +410,7 @@ test("should trigger when Ctrl+1 is pressed", async ({ page }) => { }); await page.keyboard.down("Control"); - await page.keyboard.press("1"); + await page.keyboard.down("1"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -423,7 +424,7 @@ test("should trigger when Alt+0 is pressed", async ({ page }) => { }); await page.keyboard.down("Alt"); - await page.keyboard.press("0"); + await page.keyboard.down("0"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); From 7efeead0c98ccdf1ee48e7aa5b17e4abea0ae210 Mon Sep 17 00:00:00 2001 From: rameel Date: Sat, 4 Apr 2026 21:17:09 +0500 Subject: [PATCH 5/5] Update README.md --- README.md | 60 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 9425263..ea37e48 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![MIT](https://img.shields.io/github/license/rameel/ramstack.hotkey.js)](https://github.com/rameel/ramstack.hotkey.js/blob/main/LICENSE) The `@ramstack/hotkey` package is a very small and lightweight library for handling hotkeys. -The library weighs around 1.3KB and approximately 750 bytes when gzipped. +The library weighs around 1.4KB and approximately 800 bytes when gzipped. ## Installation @@ -102,41 +102,39 @@ registerHotkey(window, "Win + PgUp", e => { #### hotkey (required) The combination of keys for the hotkey, e.g., `Ctrl + Alt + Delete` -The hotkey description is a case-insensitive. Spaces are not important. Standard key names are used. -You can find them here [Key values for keyboard events](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values) +The hotkey description is case-insensitive. Spaces are not important. Standard key names are used. +You can find them here [Key values for keyboard events](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values). In addition, there are also aliases for some key names: ```js const aliases: Record = { - "esc" : "escape", - "ins" : "insert", - "del" : "delete", - "up" : "arrowup", - "down" : "arrowdown", - "right" : "arrowright", - "left" : "arrowleft", - "pgup" : "pageup", - "pgdn" : "pagedown", - "break" : "pause", - "scroll" : "scrolllock", - "scrlk" : "scrolllock", - "prtscr" : "printscreen", - "win" : "meta", - "windows" : "meta", - "cmd" : "meta", - "command" : "meta", - "comma" : ",", - "period" : ".", - "quote" : "\"", - "singlequote" : "'", - "colon" : ":", - "semicolon" : ";", - "plus" : "+", - "minus" : "-", - "tilde" : "~", - "equal" : "=", - "slash" : "/" + "esc" : "escape", + "ins" : "insert", + "del" : "delete", + "up" : "arrowup", + "down" : "arrowdown", + "right" : "arrowright", + "left" : "arrowleft", + "pgup" : "pageup", + "pgdn" : "pagedown", + "break" : "pause", + "scroll" : "scrolllock", + "scrlk" : "scrolllock", + "prtscr" : "printscreen", + "win" : "meta", + "windows" : "meta", + "cmd" : "meta", + "command" : "meta", + "," : "comma", + "." : "period", + "/" : "slash", + "\\" : "backslash", + ";" : "semicolon", + "=" : "equal", + "`" : "backquote", + "]" : "bracketright", + "[" : "bracketleft", }; ```