mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-14 13:22:37 +00:00
Record shortcuts using the keyboard layout character (#6652)
* fix(app): record shortcuts using the keyboard layout character Shortcut capture stored the QWERTY position while dispatch matched the logical character, so a Dvorak B became Cmd+N. Use the existing logical key mappings for capture and preserve code fallback for named and rewritten Option keys. * fix(app): keep named shortcut keys on their supported codes Character capture must not advertise unsupported named keys. Restrict the logical lookup to printable mappings so keypad Enter keeps its existing unsupported behavior. --------- Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { formatShortcut } from "@/utils/format-shortcut";
|
||||
import { keyboardEventToComboString } from "./shortcut-string";
|
||||
import {
|
||||
buildKeyboardShortcutHelpSections,
|
||||
buildEffectiveBindings,
|
||||
@@ -1026,6 +1027,25 @@ describe("unassigned shortcuts", () => {
|
||||
});
|
||||
|
||||
describe("matching", () => {
|
||||
it("dispatches a captured Dvorak binding on the same logical key", () => {
|
||||
const event = keyboardInput({ key: "b", code: "KeyN", metaKey: true });
|
||||
const combo = keyboardEventToComboString(event as KeyboardEvent);
|
||||
expect(combo).toBe("Cmd+B");
|
||||
const bindings = buildEffectiveBindings({ "sidebar-toggle-left-mac-cmd-b": combo! });
|
||||
|
||||
expect(
|
||||
resolveShortcut({ event, context: { isMac: true, isDesktop: true }, bindings }).match
|
||||
?.action,
|
||||
).toBe("sidebar.toggle.left");
|
||||
expect(
|
||||
resolveShortcut({
|
||||
event: { key: "x", code: "KeyB", metaKey: true },
|
||||
context: { isMac: true, isDesktop: true },
|
||||
bindings,
|
||||
}).match,
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("stops matching a shortcut the user unassigned", () => {
|
||||
const bindings = buildEffectiveBindings({ [TAB_NEW_BINDING]: UNASSIGNED_COMBO });
|
||||
|
||||
|
||||
@@ -22,6 +22,45 @@ function keyboardEvent(overrides: Partial<KeyboardEvent>): KeyboardEvent {
|
||||
}
|
||||
|
||||
describe("keyboardEventToComboString", () => {
|
||||
it("captures the Dvorak character rather than its QWERTY position", () => {
|
||||
expect(
|
||||
keyboardEventToComboString(keyboardEvent({ key: "b", code: "KeyN", metaKey: true })),
|
||||
).toBe("Cmd+B");
|
||||
});
|
||||
|
||||
it("captures shifted Dvorak letters and punctuation at other physical positions", () => {
|
||||
expect(
|
||||
keyboardEventToComboString(
|
||||
keyboardEvent({ key: "T", code: "KeyK", metaKey: true, shiftKey: true }),
|
||||
),
|
||||
).toBe("Shift+Cmd+T");
|
||||
expect(
|
||||
keyboardEventToComboString(keyboardEvent({ key: ";", code: "KeyZ", ctrlKey: true })),
|
||||
).toBe("Ctrl+;");
|
||||
expect(
|
||||
keyboardEventToComboString(
|
||||
keyboardEvent({ key: "?", code: "BracketLeft", ctrlKey: true, shiftKey: true }),
|
||||
),
|
||||
).toBe("Ctrl+Shift+/");
|
||||
});
|
||||
|
||||
it("keeps named keys and rewritten Option characters capturable", () => {
|
||||
expect(keyboardEventToComboString(keyboardEvent({ key: "ArrowLeft", code: "ArrowLeft" }))).toBe(
|
||||
"ArrowLeft",
|
||||
);
|
||||
expect(
|
||||
keyboardEventToComboString(keyboardEvent({ key: "†", code: "KeyT", altKey: true })),
|
||||
).toBe("Alt+T");
|
||||
});
|
||||
|
||||
it("does not record a named key that dispatch does not support", () => {
|
||||
expect(
|
||||
keyboardEventToComboString(
|
||||
keyboardEvent({ key: "Enter", code: "NumpadEnter", ctrlKey: true }),
|
||||
),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("captures the minus key (regression: previously returned null)", () => {
|
||||
expect(keyboardEventToComboString(keyboardEvent({ key: "-", code: "Minus" }))).toBe("-");
|
||||
});
|
||||
|
||||
@@ -266,7 +266,16 @@ export function keyboardEventToComboString(event: KeyboardEvent): string | null
|
||||
if (event.shiftKey) parts.push("Shift");
|
||||
if (event.metaKey) parts.push("Cmd");
|
||||
|
||||
const humanKey = CODE_TO_KEY[event.code];
|
||||
// Capture the same logical character that dispatch matches. Keep the code
|
||||
// fallback for named keys and Option combinations whose key is rewritten.
|
||||
const eventKey = event.key.toLowerCase();
|
||||
const logicalKey = Object.entries(KEY_MAP).find(
|
||||
([, mapping]) =>
|
||||
mapping.key?.length === 1 &&
|
||||
(mapping.key.toLowerCase() === eventKey ||
|
||||
(event.shiftKey && mapping.shiftedKey === event.key)),
|
||||
)?.[0];
|
||||
const humanKey = logicalKey ?? CODE_TO_KEY[event.code];
|
||||
if (humanKey) {
|
||||
parts.push(humanKey);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user