Add Windows desktop app menu (#28420)
This commit is contained in:
parent
66d409d679
commit
82c5d45601
14 changed files with 554 additions and 134 deletions
|
|
@ -12,6 +12,7 @@ import { usePlatform } from "@/context/platform"
|
|||
import { useCommand } from "@/context/command"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { WindowsAppMenu } from "./windows-app-menu"
|
||||
import { applyPath, backPath, forwardPath } from "./titlebar-history"
|
||||
|
||||
type TauriDesktopWindow = {
|
||||
|
|
@ -191,6 +192,9 @@ export function Titlebar() {
|
|||
"pl-2": !mac(),
|
||||
}}
|
||||
>
|
||||
<Show when={windows()}>
|
||||
<WindowsAppMenu command={command} platform={platform} />
|
||||
</Show>
|
||||
<Show when={mac()}>
|
||||
<div class="h-full shrink-0" style={{ width: `${72 / zoom()}px` }} />
|
||||
<div class="xl:hidden w-10 shrink-0 flex items-center justify-center">
|
||||
|
|
|
|||
106
packages/app/src/components/windows-app-menu.tsx
Normal file
106
packages/app/src/components/windows-app-menu.tsx
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import { Show, type JSX } from "solid-js"
|
||||
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
|
||||
import { useCommand } from "@/context/command"
|
||||
import { DESKTOP_MENU, desktopMenuVisible, type DesktopMenuAction, type DesktopMenuEntry } from "@/desktop-menu"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
|
||||
export function WindowsAppMenu(props: { command: ReturnType<typeof useCommand>; platform: ReturnType<typeof usePlatform> }) {
|
||||
let lastFocused: HTMLElement | undefined
|
||||
|
||||
const rememberFocus = () => {
|
||||
const active = document.activeElement
|
||||
lastFocused = active instanceof HTMLElement ? active : undefined
|
||||
}
|
||||
const commandDisabled = (id: string) => {
|
||||
const option = props.command.options.find((option) => option.id === id)
|
||||
if (!option) return true
|
||||
return option.disabled ?? false
|
||||
}
|
||||
const runCommand = (id: string) => {
|
||||
if (commandDisabled(id)) return
|
||||
props.command.trigger(id)
|
||||
}
|
||||
const runAction = (action: DesktopMenuAction) => {
|
||||
if (action.startsWith("edit.") && lastFocused?.isConnected) lastFocused.focus({ preventScroll: true })
|
||||
void props.platform.runDesktopMenuAction?.(action)
|
||||
}
|
||||
const runEntry = (entry: DesktopMenuEntry) => {
|
||||
if (entry.type === "separator") return
|
||||
if (entry.command) {
|
||||
runCommand(entry.command)
|
||||
return
|
||||
}
|
||||
if (entry.action) {
|
||||
runAction(entry.action)
|
||||
return
|
||||
}
|
||||
if (entry.href) props.platform.openLink(entry.href)
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu gutter={4} modal={false} placement="bottom-start">
|
||||
<DropdownMenu.Trigger
|
||||
as={IconButton}
|
||||
icon="menu"
|
||||
variant="ghost"
|
||||
class="titlebar-icon rounded-md shrink-0"
|
||||
aria-label="OpenCode menu"
|
||||
onPointerDown={rememberFocus}
|
||||
onKeyDown={rememberFocus}
|
||||
/>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content class="desktop-app-menu">
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.GroupLabel class="desktop-app-menu-heading">OpenCode</DropdownMenu.GroupLabel>
|
||||
{DESKTOP_MENU.filter((menu) => desktopMenuVisible(menu, "windows")).map((menu) => (
|
||||
<DesktopMenuSubmenu label={menu.label}>
|
||||
{menu.items?.filter((entry) => desktopMenuVisible(entry, "windows")).map((entry) =>
|
||||
entry.type === "separator" ? (
|
||||
<DropdownMenu.Separator />
|
||||
) : (
|
||||
<DesktopMenuItem
|
||||
label={entry.label ?? ""}
|
||||
keybind={entry.command ? props.command.keybind(entry.command) : entry.accelerator?.windows}
|
||||
disabled={entry.command ? commandDisabled(entry.command) : false}
|
||||
onSelect={() => runEntry(entry)}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</DesktopMenuSubmenu>
|
||||
))}
|
||||
</DropdownMenu.Group>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
||||
function DesktopMenuSubmenu(props: { label: string; children: JSX.Element }) {
|
||||
return (
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger>
|
||||
<span data-slot="dropdown-menu-item-label">{props.label}</span>
|
||||
<span data-slot="desktop-app-menu-chevron">
|
||||
<Icon name="chevron-right" size="small" />
|
||||
</span>
|
||||
</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.SubContent class="desktop-app-menu">{props.children}</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu.Sub>
|
||||
)
|
||||
}
|
||||
|
||||
function DesktopMenuItem(props: { label: string; keybind?: string; disabled?: boolean; onSelect: () => void }) {
|
||||
return (
|
||||
<DropdownMenu.Item disabled={props.disabled} onSelect={props.onSelect}>
|
||||
<DropdownMenu.ItemLabel>{props.label}</DropdownMenu.ItemLabel>
|
||||
<Show when={props.keybind}>
|
||||
<span data-slot="desktop-app-menu-keybind">{props.keybind}</span>
|
||||
</Show>
|
||||
</DropdownMenu.Item>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue