fix(browser): preserve Live keyboard modifiers (#7213)

* fix(browser): preserve Live keyboard modifiers

* test(browser): cover Meta modifier on named keys

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
Lucien 2026-10-21 20:57:56 +08:00 committed by GitHub
parent c70dd685a9
commit ff07f87cc3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 54 additions and 2 deletions

View File

@ -328,6 +328,8 @@ export function BrowserViewPanel({
key: event.key,
metaKey: event.metaKey,
ctrlKey: event.ctrlKey,
shiftKey: event.shiftKey,
altKey: event.altKey,
});
if (!input) {
return;

View File

@ -25,6 +25,18 @@ export interface BrowserKeyContext {
key: string;
metaKey: boolean;
ctrlKey: boolean;
shiftKey: boolean;
altKey: boolean;
}
function keyChord(ctx: BrowserKeyContext): string {
const modifiers = [
ctx.ctrlKey ? "Control" : null,
ctx.metaKey ? "Meta" : null,
ctx.altKey ? "Alt" : null,
ctx.shiftKey ? "Shift" : null,
].filter((modifier): modifier is string => modifier !== null);
return [...modifiers, ctx.key].join("+");
}
/**
@ -42,14 +54,14 @@ export function decideBrowserKeyInput(
if ((ctx.ctrlKey || ctx.metaKey) && ctx.key.length === 1) {
return {
type: "key",
key: `${ctx.metaKey ? "Meta" : "Control"}+${ctx.key.toUpperCase()}`,
key: keyChord({ ...ctx, key: ctx.key.toUpperCase() }),
};
}
if (ctx.key.length === 1 && !ctx.metaKey && !ctx.ctrlKey) {
return { type: "text", text: ctx.key };
}
if ((FORWARDED_NAMED_KEYS as readonly string[]).includes(ctx.key)) {
return { type: "key", key: ctx.key };
return { type: "key", key: keyChord(ctx) };
}
return null;
}

View File

@ -12,6 +12,8 @@ function ctx(
key: "a",
metaKey: false,
ctrlKey: false,
shiftKey: false,
altKey: false,
...overrides,
};
}
@ -33,6 +35,16 @@ describe("decideBrowserKeyInput", () => {
type: "key",
key: "ArrowLeft",
});
expect(decideBrowserKeyInput(ctx({ key: "Tab", shiftKey: true }))).toEqual({
type: "key",
key: "Shift+Tab",
});
expect(
decideBrowserKeyInput(ctx({ key: "ArrowLeft", shiftKey: true })),
).toEqual({
type: "key",
key: "Shift+ArrowLeft",
});
});
it("forwards modifier combos as a normalized key chord", () => {
@ -44,6 +56,32 @@ describe("decideBrowserKeyInput", () => {
type: "key",
key: "Control+A",
});
expect(
decideBrowserKeyInput(
ctx({ key: "ArrowLeft", ctrlKey: true, shiftKey: true }),
),
).toEqual({
type: "key",
key: "Control+Shift+ArrowLeft",
});
expect(
decideBrowserKeyInput(ctx({ key: "Backspace", ctrlKey: true })),
).toEqual({
type: "key",
key: "Control+Backspace",
});
expect(
decideBrowserKeyInput(ctx({ key: "Backspace", metaKey: true })),
).toEqual({
type: "key",
key: "Meta+Backspace",
});
expect(
decideBrowserKeyInput(ctx({ key: "ArrowRight", altKey: true })),
).toEqual({
type: "key",
key: "Alt+ArrowRight",
});
});
it("does not forward while an IME composition is active", () => {