Preserve browser-native shortcut ownership

This commit is contained in:
Mohamed Boudra
2026-07-16 15:49:39 +00:00
parent 8e936daa9e
commit e58c0a7a28
2 changed files with 73 additions and 3 deletions

View File

@@ -136,6 +136,48 @@ describe("buildBrowserKeyboardPolicy", () => {
});
});
it("keeps Ctrl+W out of the window menu after the tab-close shortcut is remapped", () => {
const bindings = buildEffectiveBindings({
"workspace-tab-close-current-ctrl-w-non-mac": "Ctrl+Y",
});
const policy = buildBrowserKeyboardPolicy({ bindings, isMac: false, isDesktop: true });
expect(policy.prefixes).not.toContainEqual({
alt: false,
code: "KeyW",
control: true,
key: "w",
meta: false,
shift: false,
});
expect(policy.menuPrefixes).toContainEqual({
alt: false,
code: "KeyW",
control: true,
key: "w",
meta: false,
shift: false,
});
});
it("leaves macOS browser back and forward shortcuts in the guest", () => {
const bindings = buildEffectiveBindings({});
const policy = buildBrowserKeyboardPolicy({ bindings, isMac: true, isDesktop: true });
for (const code of ["BracketLeft", "BracketRight"]) {
expect(policy.prefixes).not.toContainEqual({
alt: false,
code,
control: false,
key: code === "BracketLeft" ? "[" : "]",
meta: true,
shift: false,
});
}
});
it("does not publish plain browser keys", () => {
const bindings = buildEffectiveBindings({});
const policy = buildBrowserKeyboardPolicy({ bindings, isMac: false, isDesktop: true });

View File

@@ -104,8 +104,22 @@ function prefixFromCombo(combo: KeyCombo, isMac: boolean): BrowserShortcutPrefix
return prefix.meta || prefix.control || prefix.alt ? prefix : null;
}
function isBrowserNativeNavigationPrefix(prefix: BrowserShortcutPrefix, isMac: boolean): boolean {
return (
isMac &&
prefix.meta &&
!prefix.control &&
!prefix.alt &&
!prefix.shift &&
(prefix.code === "BracketLeft" || prefix.code === "BracketRight")
);
}
function canCrossBrowserBoundary(binding: ParsedShortcutBinding, isMac: boolean): boolean {
return binding.parsedChord.every((combo) => prefixFromCombo(combo, isMac) !== null);
return binding.parsedChord.every((combo) => {
const prefix = prefixFromCombo(combo, isMac);
return prefix !== null && !isBrowserNativeNavigationPrefix(prefix, isMac);
});
}
function prefixKey(prefix: BrowserShortcutPrefix): string {
@@ -165,10 +179,24 @@ function buildBrowserShortcutPrefixes(input: BrowserShortcutPolicyInput): Browse
export function buildBrowserKeyboardPolicy(
input: BrowserShortcutPolicyInput,
): BrowserKeyboardPolicy {
const menuPrefixes = buildBrowserShortcutPrefixes({ ...input, chordState: undefined });
const idlePrefixes = buildBrowserShortcutPrefixes({ ...input, chordState: undefined });
const prefixes =
input.chordState && input.chordState.step > 0
? buildBrowserShortcutPrefixes(input)
: menuPrefixes;
: idlePrefixes;
const menuPrefixes = [...idlePrefixes];
if (!input.isMac) {
const closeWindowGuard: BrowserShortcutPrefix = {
alt: false,
code: "KeyW",
control: true,
key: "w",
meta: false,
shift: false,
};
if (!menuPrefixes.some((prefix) => prefixKey(prefix) === prefixKey(closeWindowGuard))) {
menuPrefixes.push(closeWindowGuard);
}
}
return { menuPrefixes, prefixes };
}