desktop: add more basic menu bar items

This commit is contained in:
Brendan Allan
2026-02-06 18:10:48 +08:00
parent 9b20679a61
commit e0e32ed3a8
7 changed files with 138 additions and 34 deletions

View File

@@ -1,2 +1,2 @@
sst-env.d.ts sst-env.d.ts
desktop/src/bindings.ts packages/desktop/src/bindings.ts

View File

@@ -30,7 +30,7 @@ import { HighlightsProvider } from "@/context/highlights"
import Layout from "@/pages/layout" import Layout from "@/pages/layout"
import DirectoryLayout from "@/pages/directory-layout" import DirectoryLayout from "@/pages/directory-layout"
import { ErrorPage } from "./pages/error" import { ErrorPage } from "./pages/error"
import { Suspense } from "solid-js" import { Suspense, JSX } from "solid-js"
const Home = lazy(() => import("@/pages/home")) const Home = lazy(() => import("@/pages/home"))
const Session = lazy(() => import("@/pages/session")) const Session = lazy(() => import("@/pages/session"))
@@ -84,7 +84,7 @@ function ServerKey(props: ParentProps) {
) )
} }
export function AppInterface(props: { defaultUrl?: string }) { export function AppInterface(props: { defaultUrl?: string; children?: JSX.Element }) {
const platform = usePlatform() const platform = usePlatform()
const stored = (() => { const stored = (() => {
@@ -111,7 +111,7 @@ export function AppInterface(props: { defaultUrl?: string }) {
<GlobalSDKProvider> <GlobalSDKProvider>
<GlobalSyncProvider> <GlobalSyncProvider>
<Router <Router
root={(props) => ( root={(routerProps) => (
<SettingsProvider> <SettingsProvider>
<PermissionProvider> <PermissionProvider>
<LayoutProvider> <LayoutProvider>
@@ -119,7 +119,10 @@ export function AppInterface(props: { defaultUrl?: string }) {
<ModelsProvider> <ModelsProvider>
<CommandProvider> <CommandProvider>
<HighlightsProvider> <HighlightsProvider>
<Layout>{props.children}</Layout> <Layout>
{props.children}
{routerProps.children}
</Layout>
</HighlightsProvider> </HighlightsProvider>
</CommandProvider> </CommandProvider>
</ModelsProvider> </ModelsProvider>

View File

@@ -82,6 +82,21 @@ export function Titlebar() {
navigate(to) navigate(to)
} }
command.register(() => [
{
id: "common.goBack",
title: language.t("common.goBack"),
category: language.t("command.category.view"),
onSelect: back,
},
{
id: "common.goForward",
title: language.t("common.goForward"),
category: language.t("command.category.view"),
onSelect: forward,
},
])
const getWin = () => { const getWin = () => {
if (platform.platform !== "desktop") return if (platform.platform !== "desktop") return

View File

@@ -1,2 +1,3 @@
export { PlatformProvider, type Platform } from "./context/platform" export { PlatformProvider, type Platform } from "./context/platform"
export { AppBaseProviders, AppInterface } from "./app" export { AppBaseProviders, AppInterface } from "./app"
export { useCommand } from './context/command'

View File

@@ -1,6 +1,6 @@
// This file has been generated by Tauri Specta. Do not edit this file manually. // This file has been generated by Tauri Specta. Do not edit this file manually.
import { invoke as __TAURI_INVOKE, Channel } from "@tauri-apps/api/core" import { invoke as __TAURI_INVOKE } from "@tauri-apps/api/core"
/** Commands */ /** Commands */
export const commands = { export const commands = {

View File

@@ -1,7 +1,7 @@
// @refresh reload // @refresh reload
import { webviewZoom } from "./webview-zoom" import { webviewZoom } from "./webview-zoom"
import { render } from "solid-js/web" import { render } from "solid-js/web"
import { AppBaseProviders, AppInterface, PlatformProvider, Platform } from "@opencode-ai/app" import { AppBaseProviders, AppInterface, PlatformProvider, Platform, useCommand } from "@opencode-ai/app"
import { open, save } from "@tauri-apps/plugin-dialog" import { open, save } from "@tauri-apps/plugin-dialog"
import { getCurrent, onOpenUrl } from "@tauri-apps/plugin-deep-link" import { getCurrent, onOpenUrl } from "@tauri-apps/plugin-deep-link"
import { openPath as openerOpenPath } from "@tauri-apps/plugin-opener" import { openPath as openerOpenPath } from "@tauri-apps/plugin-opener"
@@ -18,11 +18,11 @@ import { Splash } from "@opencode-ai/ui/logo"
import { createSignal, Show, Accessor, JSX, createResource, onMount, onCleanup } from "solid-js" import { createSignal, Show, Accessor, JSX, createResource, onMount, onCleanup } from "solid-js"
import { UPDATER_ENABLED } from "./updater" import { UPDATER_ENABLED } from "./updater"
import { createMenu } from "./menu"
import { initI18n, t } from "./i18n" import { initI18n, t } from "./i18n"
import pkg from "../package.json" import pkg from "../package.json"
import "./styles.css" import "./styles.css"
import { commands } from "./bindings" import { commands } from "./bindings"
import { createMenu } from "./menu"
const root = document.getElementById("root") const root = document.getElementById("root")
if (import.meta.env.DEV && !(root instanceof HTMLElement)) { if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
@@ -342,7 +342,10 @@ const createPlatform = (password: Accessor<string | null>): Platform => ({
webviewZoom, webviewZoom,
}) })
createMenu() let menuTrigger = null as null | ((id: string) => void)
createMenu((id) => {
menuTrigger?.(id)
})
void listenForDeepLinks() void listenForDeepLinks()
render(() => { render(() => {
@@ -373,7 +376,19 @@ render(() => {
window.__OPENCODE__ ??= {} window.__OPENCODE__ ??= {}
window.__OPENCODE__.serverPassword = data().password ?? undefined window.__OPENCODE__.serverPassword = data().password ?? undefined
return <AppInterface defaultUrl={data().url} /> function Inner() {
const cmd = useCommand()
menuTrigger = (id) => cmd.trigger(id)
return null
}
return (
<AppInterface defaultUrl={data().url}>
<Inner />
</AppInterface>
)
}} }}
</ServerGate> </ServerGate>
</AppBaseProviders> </AppBaseProviders>

View File

@@ -1,13 +1,14 @@
import { Menu, MenuItem, PredefinedMenuItem, Submenu } from "@tauri-apps/api/menu" import { Menu, MenuItem, PredefinedMenuItem, Submenu } from "@tauri-apps/api/menu"
import { type as ostype } from "@tauri-apps/plugin-os" import { type as ostype } from "@tauri-apps/plugin-os"
import { relaunch } from "@tauri-apps/plugin-process" import { relaunch } from "@tauri-apps/plugin-process"
import { openUrl } from "@tauri-apps/plugin-opener"
import { runUpdater, UPDATER_ENABLED } from "./updater" import { runUpdater, UPDATER_ENABLED } from "./updater"
import { installCli } from "./cli" import { installCli } from "./cli"
import { initI18n, t } from "./i18n" import { initI18n, t } from "./i18n"
import { commands } from "./bindings" import { commands } from "./bindings"
export async function createMenu() { export async function createMenu(trigger: (id: string) => void) {
if (ostype() !== "macos") return if (ostype() !== "macos") return
await initI18n() await initI18n()
@@ -60,29 +61,25 @@ export async function createMenu() {
}), }),
].filter(Boolean), ].filter(Boolean),
}), }),
// await Submenu.new({ await Submenu.new({
// text: "File", text: "File",
// items: [ items: [
// await MenuItem.new({ await MenuItem.new({
// enabled: false, text: "New Session",
// text: "Open Project...", action: () => trigger("session.new"),
// }), }),
// await PredefinedMenuItem.new({ await MenuItem.new({
// item: "Separator" text: "Open Project...",
// }), action: () => trigger("project.open"),
// await MenuItem.new({ }),
// enabled: false, await PredefinedMenuItem.new({
// text: "New Session", item: "Separator",
// }), }),
// await PredefinedMenuItem.new({ await PredefinedMenuItem.new({
// item: "Separator" item: "CloseWindow",
// }), }),
// await MenuItem.new({ ],
// enabled: false, }),
// text: "Close Project",
// })
// ]
// }),
await Submenu.new({ await Submenu.new({
text: "Edit", text: "Edit",
items: [ items: [
@@ -109,6 +106,79 @@ export async function createMenu() {
}), }),
], ],
}), }),
await Submenu.new({
text: "View",
items: [
await MenuItem.new({
action: () => trigger("sidebar.toggle"),
text: "Toggle Sidebar",
}),
await MenuItem.new({
action: () => trigger("terminal.toggle"),
text: "Toggle Terminal",
}),
await MenuItem.new({
action: () => trigger("fileTree.toggle"),
text: "Toggle File Tree",
}),
await PredefinedMenuItem.new({
item: "Separator",
}),
await MenuItem.new({
action: () => trigger("common.goBack"),
text: "Back",
}),
await MenuItem.new({
action: () => trigger("common.goForward"),
text: "Forward",
}),
await PredefinedMenuItem.new({
item: "Separator",
}),
await MenuItem.new({
action: () => trigger("session.next"),
text: "Previous Session",
}),
await MenuItem.new({
action: () => trigger("session.previous"),
text: "Next Session",
}),
await PredefinedMenuItem.new({
item: "Separator",
}),
],
}),
await Submenu.new({
text: "Help",
items: [
// missing native macos search
await MenuItem.new({
action: () => openUrl("https://opencode.ai/docs"),
text: "OpenCode Documentation",
}),
await MenuItem.new({
action: () => openUrl("https://discord.com/invite/opencode"),
text: "Support Forum",
}),
await PredefinedMenuItem.new({
item: "Separator",
}),
// await MenuItem.new({
// text: "Release Notes",
// }),
await PredefinedMenuItem.new({
item: "Separator",
}),
await MenuItem.new({
action: () => openUrl("https://github.com/anomalyco/opencode/issues/new?template=feature_request.yml"),
text: "Share Feedback",
}),
await MenuItem.new({
action: () => openUrl("https://github.com/anomalyco/opencode/issues/new?template=bug_report.yml"),
text: "Report a Bug",
}),
],
}),
], ],
}) })
menu.setAsAppMenu() menu.setAsAppMenu()