fix(editor): distinguish macOS Control and Command shortcuts
This commit is contained in:
3 files changed
+74
-13
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { eventToModifierOnlyShortcut, eventToShortcut, isExecuteSqlInNewResultTabShortcut, matchesModifierOnlyShortcut, matchesShortcut } from "@/lib/editor/keyboardShortcuts";
|
||||
import { formatShortcutDisplay, isMacShortcutPlatform } from "@/lib/editor/shortcutDisplay";
|
||||
|
||||
describe("keyboard shortcut matching", () => {
|
||||
it("records modifier-only mouse shortcut settings", () => {
|
||||
@@ -23,23 +24,62 @@ describe("keyboard shortcut matching", () => {
|
||||
});
|
||||
|
||||
it("records the plus key without losing it to the separator", () => {
|
||||
expect(eventToShortcut({ key: "+", ctrlKey: true })).toBe("Mod+Plus");
|
||||
expect(eventToShortcut({ key: "+", ctrlKey: true, shiftKey: true })).toBe("Shift+Mod+Plus");
|
||||
expect(eventToShortcut({ key: "+", ctrlKey: true }, "Win32")).toBe("Mod+Plus");
|
||||
expect(eventToShortcut({ key: "+", ctrlKey: true, shiftKey: true }, "Win32")).toBe("Shift+Mod+Plus");
|
||||
});
|
||||
|
||||
it("keeps Control distinct from Command when recording macOS shortcuts", () => {
|
||||
const controlShortcut = eventToShortcut({ key: "b", ctrlKey: true }, "MacIntel");
|
||||
|
||||
expect(controlShortcut).toBe("Ctrl+B");
|
||||
expect(formatShortcutDisplay(controlShortcut!, "MacIntel")).toBe("⌃ B");
|
||||
expect(matchesShortcut({ key: "b", ctrlKey: true }, controlShortcut!, "MacIntel")).toBe(true);
|
||||
expect(matchesShortcut({ key: "b", metaKey: true }, controlShortcut!, "MacIntel")).toBe(false);
|
||||
expect(eventToShortcut({ key: "b", metaKey: true }, "MacIntel")).toBe("Mod+B");
|
||||
expect(matchesShortcut({ key: "b", metaKey: true }, "Mod+B", "MacIntel")).toBe(true);
|
||||
expect(matchesShortcut({ key: "b", ctrlKey: true }, "Mod+B", "MacIntel")).toBe(false);
|
||||
expect(matchesShortcut({ key: "b", ctrlKey: true, metaKey: true }, "Mod+B", "MacIntel")).toBe(false);
|
||||
});
|
||||
|
||||
it("preserves non-macOS Mod recording compatibility", () => {
|
||||
expect(eventToShortcut({ key: "b", ctrlKey: true }, "Win32")).toBe("Mod+B");
|
||||
expect(eventToShortcut({ key: "b", metaKey: true }, "Win32")).toBe("Mod+B");
|
||||
expect(matchesShortcut({ key: "b", ctrlKey: true }, "Mod+B", "Win32")).toBe(true);
|
||||
expect(matchesShortcut({ key: "b", metaKey: true }, "Mod+B", "Win32")).toBe(true);
|
||||
});
|
||||
|
||||
it("preserves combined platform modifiers", () => {
|
||||
const combinedShortcut = eventToShortcut({ key: "b", ctrlKey: true, metaKey: true, shiftKey: true, altKey: true }, "MacIntel");
|
||||
|
||||
expect(combinedShortcut).toBe("Shift+Ctrl+Mod+Alt+B");
|
||||
expect(matchesShortcut({ key: "b", ctrlKey: true, metaKey: true, shiftKey: true, altKey: true }, combinedShortcut!, "MacIntel")).toBe(true);
|
||||
expect(matchesShortcut({ key: "b", ctrlKey: true, shiftKey: true, altKey: true }, combinedShortcut!, "MacIntel")).toBe(false);
|
||||
expect(matchesShortcut({ key: "b", metaKey: true, shiftKey: true, altKey: true }, combinedShortcut!, "MacIntel")).toBe(false);
|
||||
|
||||
const nonMacCombinedShortcut = eventToShortcut({ key: "b", ctrlKey: true, metaKey: true, shiftKey: true, altKey: true }, "Win32");
|
||||
expect(nonMacCombinedShortcut).toBe("Shift+Mod+Meta+Alt+B");
|
||||
expect(matchesShortcut({ key: "b", ctrlKey: true, metaKey: true, shiftKey: true, altKey: true }, nonMacCombinedShortcut!, "Win32")).toBe(true);
|
||||
expect(matchesShortcut({ key: "b", ctrlKey: true, shiftKey: true, altKey: true }, nonMacCombinedShortcut!, "Win32")).toBe(false);
|
||||
expect(matchesShortcut({ key: "b", metaKey: true, shiftKey: true, altKey: true }, nonMacCombinedShortcut!, "Win32")).toBe(false);
|
||||
});
|
||||
|
||||
it("matches canonical plus-key shortcuts", () => {
|
||||
expect(matchesShortcut({ key: "+", ctrlKey: true }, "Mod+Plus")).toBe(true);
|
||||
expect(matchesShortcut({ key: "+", ctrlKey: true, shiftKey: true }, "Shift+Mod+Plus")).toBe(true);
|
||||
expect(matchesShortcut({ key: "+", ctrlKey: true }, "Mod+Plus", "Win32")).toBe(true);
|
||||
expect(matchesShortcut({ key: "+", ctrlKey: true, shiftKey: true }, "Shift+Mod+Plus", "Win32")).toBe(true);
|
||||
});
|
||||
|
||||
it("matches the configurable execute-in-new-result-tab shortcut", () => {
|
||||
expect(isExecuteSqlInNewResultTabShortcut({ key: "\\", ctrlKey: true }, { executeSqlInNewResultTab: "Mod+\\" })).toBe(true);
|
||||
const isMac = isMacShortcutPlatform();
|
||||
const platformModEvent = isMac ? { key: "\\", metaKey: true } : { key: "\\", ctrlKey: true };
|
||||
|
||||
expect(isExecuteSqlInNewResultTabShortcut(platformModEvent, { executeSqlInNewResultTab: "Mod+\\" })).toBe(true);
|
||||
expect(isExecuteSqlInNewResultTabShortcut({ ...platformModEvent, shiftKey: true }, { executeSqlInNewResultTab: "Mod+\\" })).toBe(false);
|
||||
expect(isExecuteSqlInNewResultTabShortcut({ key: "\\", ctrlKey: true }, { executeSqlInNewResultTab: "Mod+\\" })).toBe(!isMac);
|
||||
expect(isExecuteSqlInNewResultTabShortcut({ key: "\\", metaKey: true }, { executeSqlInNewResultTab: "Mod+\\" })).toBe(true);
|
||||
expect(isExecuteSqlInNewResultTabShortcut({ key: "\\", ctrlKey: true, shiftKey: true }, { executeSqlInNewResultTab: "Mod+\\" })).toBe(false);
|
||||
});
|
||||
|
||||
it("matches legacy plus-key shortcuts saved with plus as a separator", () => {
|
||||
expect(matchesShortcut({ key: "+", ctrlKey: true }, "Mod++")).toBe(true);
|
||||
expect(matchesShortcut({ key: "+", ctrlKey: true, shiftKey: true }, "Shift+Mod++")).toBe(true);
|
||||
expect(matchesShortcut({ key: "+", ctrlKey: true }, "Mod++", "Win32")).toBe(true);
|
||||
expect(matchesShortcut({ key: "+", ctrlKey: true, shiftKey: true }, "Shift+Mod++", "Win32")).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -37,7 +37,7 @@ function shortcutKeyName(key: string): string | null {
|
||||
return key;
|
||||
}
|
||||
|
||||
export function eventToShortcut(event: ShortcutLikeEvent): string | null {
|
||||
export function eventToShortcut(event: ShortcutLikeEvent, platform = globalThis.navigator?.platform || ""): string | null {
|
||||
if (event.isComposing) return null;
|
||||
|
||||
const key = shortcutKeyName(event.key);
|
||||
@@ -46,9 +46,17 @@ export function eventToShortcut(event: ShortcutLikeEvent): string | null {
|
||||
const hasModifier = !!event.metaKey || !!event.ctrlKey || !!event.altKey || !!event.shiftKey;
|
||||
if (!hasModifier && event.key.length === 1 && event.key !== " ") return null;
|
||||
|
||||
const isMac = isMacShortcutPlatform(platform);
|
||||
const parts: string[] = [];
|
||||
if (event.shiftKey) parts.push("Shift");
|
||||
if (event.metaKey || event.ctrlKey) parts.push("Mod");
|
||||
if (isMac) {
|
||||
if (event.ctrlKey) parts.push("Ctrl");
|
||||
if (event.metaKey) parts.push("Mod");
|
||||
} else if (event.ctrlKey && event.metaKey) {
|
||||
parts.push("Mod", "Meta");
|
||||
} else if (event.ctrlKey || event.metaKey) {
|
||||
parts.push("Mod");
|
||||
}
|
||||
if (event.altKey) parts.push("Alt");
|
||||
parts.push(key);
|
||||
return parts.join("+");
|
||||
@@ -87,7 +95,13 @@ export function matchesShortcut(event: ShortcutLikeEvent, shortcut: string, plat
|
||||
const usesCtrl = modifiers.has("Ctrl");
|
||||
|
||||
if (usesMod) {
|
||||
if (!event.metaKey && !event.ctrlKey) return false;
|
||||
if (isMacShortcutPlatform(platform)) {
|
||||
if (!event.metaKey || !!event.ctrlKey !== usesCtrl) return false;
|
||||
} else {
|
||||
if (!event.metaKey && !event.ctrlKey) return false;
|
||||
if (usesCtrl && (!event.ctrlKey || !event.metaKey)) return false;
|
||||
if (usesMeta && (!event.metaKey || !event.ctrlKey)) return false;
|
||||
}
|
||||
} else {
|
||||
if (!!event.metaKey !== usesMeta) return false;
|
||||
if (!!event.ctrlKey !== usesCtrl) return false;
|
||||
|
||||
Reference in new issue
Block a user