fix(app): handle Windows menu accelerators (#44607)

Co-authored-by: Brendonovich <14191578+Brendonovich@users.noreply.github.com>
This commit is contained in:
opencode-agent[bot]
2026-08-24 13:57:52 +08:00
committed by GitHub
Unverified
parent e63aba48bc
commit 6704f34cad
2 changed files with 42 additions and 2 deletions
@@ -0,0 +1,14 @@
import { describe, expect, test } from "bun:test"
import { windowsMenuAccelerator } from "./windows-menu"
describe("Windows app menu", () => {
test("resolves the new window accelerator", () => {
expect(windowsMenuAccelerator(new KeyboardEvent("keydown", { key: "N", ctrlKey: true, shiftKey: true }))).toBe(
"window.new",
)
})
test("ignores the accelerator without its modifiers", () => {
expect(windowsMenuAccelerator(new KeyboardEvent("keydown", { key: "N" }))).toBeUndefined()
})
})
@@ -1,9 +1,10 @@
import { For, type JSX } from "solid-js"
import { makeEventListener } from "@solid-primitives/event-listener"
import { For, onMount, type JSX } from "solid-js"
import { Menu } from "@opencode-ai/ui/menu"
import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { useCommand } from "@/shell/commands/command"
import { matchKeybind, parseKeybind, useCommand } from "@/shell/commands/command"
import {
DESKTOP_MENU,
desktopMenuVisible,
@@ -13,6 +14,15 @@ import {
import { usePlatform } from "@/runtime/platform/platform"
import { useLanguage } from "@/runtime/i18n/language"
const accelerators = DESKTOP_MENU.flatMap((menu) => menu.items ?? []).flatMap((entry) => {
if (entry.type === "separator" || !entry.action || !entry.accelerator?.windows) return []
return [{ action: entry.action, keybind: parseKeybind(entry.accelerator.windows) }]
})
export function windowsMenuAccelerator(event: KeyboardEvent) {
return accelerators.find((entry) => matchKeybind(entry.keybind, event))?.action
}
export function WindowsAppMenu(props: {
command: ReturnType<typeof useCommand>
platform: ReturnType<typeof usePlatform>
@@ -50,6 +60,22 @@ export function WindowsAppMenu(props: {
if (entry.href) props.platform.openExternal(entry.href)
}
onMount(() => {
makeEventListener(
document,
"keydown",
(event) => {
if (event.defaultPrevented) return
const action = windowsMenuAccelerator(event)
if (!action) return
event.preventDefault()
event.stopPropagation()
runAction(action)
},
{ capture: true },
)
})
return (
<Menu appearance="standard" gutter={4} modal={false} placement="bottom-start">
<div