summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBrendan Allan <[email protected]>2026-02-06 18:10:48 +0800
committerBrendan Allan <[email protected]>2026-02-06 18:10:48 +0800
commite0e32ed3a846be03f66d2f48e2b2659a9e658a11 (patch)
treebe2b6430e46a9a8561f8ad8c87f7d7bbbccd1949
parent9b20679a610af778e4f0fb741baed524edb2514a (diff)
downloadopencode-e0e32ed3a846be03f66d2f48e2b2659a9e658a11.tar.gz
opencode-e0e32ed3a846be03f66d2f48e2b2659a9e658a11.zip
desktop: add more basic menu bar items
-rw-r--r--.prettierignore2
-rw-r--r--packages/app/src/app.tsx11
-rw-r--r--packages/app/src/components/titlebar.tsx15
-rw-r--r--packages/app/src/index.ts1
-rw-r--r--packages/desktop/src/bindings.ts2
-rw-r--r--packages/desktop/src/index.tsx23
-rw-r--r--packages/desktop/src/menu.ts118
7 files changed, 138 insertions, 34 deletions
diff --git a/.prettierignore b/.prettierignore
index 5f86f710f..a2a277659 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,2 +1,2 @@
sst-env.d.ts
-desktop/src/bindings.ts
+packages/desktop/src/bindings.ts
diff --git a/packages/app/src/app.tsx b/packages/app/src/app.tsx
index 11fdb5743..8a111472b 100644
--- a/packages/app/src/app.tsx
+++ b/packages/app/src/app.tsx
@@ -30,7 +30,7 @@ import { HighlightsProvider } from "@/context/highlights"
import Layout from "@/pages/layout"
import DirectoryLayout from "@/pages/directory-layout"
import { ErrorPage } from "./pages/error"
-import { Suspense } from "solid-js"
+import { Suspense, JSX } from "solid-js"
const Home = lazy(() => import("@/pages/home"))
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 stored = (() => {
@@ -111,7 +111,7 @@ export function AppInterface(props: { defaultUrl?: string }) {
<GlobalSDKProvider>
<GlobalSyncProvider>
<Router
- root={(props) => (
+ root={(routerProps) => (
<SettingsProvider>
<PermissionProvider>
<LayoutProvider>
@@ -119,7 +119,10 @@ export function AppInterface(props: { defaultUrl?: string }) {
<ModelsProvider>
<CommandProvider>
<HighlightsProvider>
- <Layout>{props.children}</Layout>
+ <Layout>
+ {props.children}
+ {routerProps.children}
+ </Layout>
</HighlightsProvider>
</CommandProvider>
</ModelsProvider>
diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx
index 86b4fbeb1..32e36815e 100644
--- a/packages/app/src/components/titlebar.tsx
+++ b/packages/app/src/components/titlebar.tsx
@@ -82,6 +82,21 @@ export function Titlebar() {
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 = () => {
if (platform.platform !== "desktop") return
diff --git a/packages/app/src/index.ts b/packages/app/src/index.ts
index df3181133..c32af728a 100644
--- a/packages/app/src/index.ts
+++ b/packages/app/src/index.ts
@@ -1,2 +1,3 @@
export { PlatformProvider, type Platform } from "./context/platform"
export { AppBaseProviders, AppInterface } from "./app"
+export { useCommand } from './context/command'
diff --git a/packages/desktop/src/bindings.ts b/packages/desktop/src/bindings.ts
index 440e138b4..c6ca0fec7 100644
--- a/packages/desktop/src/bindings.ts
+++ b/packages/desktop/src/bindings.ts
@@ -1,6 +1,6 @@
// 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 */
export const commands = {
diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx
index 30cb7ba7a..66e86bf52 100644
--- a/packages/desktop/src/index.tsx
+++ b/packages/desktop/src/index.tsx
@@ -1,7 +1,7 @@
// @refresh reload
import { webviewZoom } from "./webview-zoom"
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 { getCurrent, onOpenUrl } from "@tauri-apps/plugin-deep-link"
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 { UPDATER_ENABLED } from "./updater"
-import { createMenu } from "./menu"
import { initI18n, t } from "./i18n"
import pkg from "../package.json"
import "./styles.css"
import { commands } from "./bindings"
+import { createMenu } from "./menu"
const root = document.getElementById("root")
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
@@ -342,7 +342,10 @@ const createPlatform = (password: Accessor<string | null>): Platform => ({
webviewZoom,
})
-createMenu()
+let menuTrigger = null as null | ((id: string) => void)
+createMenu((id) => {
+ menuTrigger?.(id)
+})
void listenForDeepLinks()
render(() => {
@@ -373,7 +376,19 @@ render(() => {
window.__OPENCODE__ ??= {}
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>
</AppBaseProviders>
diff --git a/packages/desktop/src/menu.ts b/packages/desktop/src/menu.ts
index d41084404..9af6d2b84 100644
--- a/packages/desktop/src/menu.ts
+++ b/packages/desktop/src/menu.ts
@@ -1,13 +1,14 @@
import { Menu, MenuItem, PredefinedMenuItem, Submenu } from "@tauri-apps/api/menu"
import { type as ostype } from "@tauri-apps/plugin-os"
import { relaunch } from "@tauri-apps/plugin-process"
+import { openUrl } from "@tauri-apps/plugin-opener"
import { runUpdater, UPDATER_ENABLED } from "./updater"
import { installCli } from "./cli"
import { initI18n, t } from "./i18n"
import { commands } from "./bindings"
-export async function createMenu() {
+export async function createMenu(trigger: (id: string) => void) {
if (ostype() !== "macos") return
await initI18n()
@@ -60,29 +61,25 @@ export async function createMenu() {
}),
].filter(Boolean),
}),
- // await Submenu.new({
- // text: "File",
- // items: [
- // await MenuItem.new({
- // enabled: false,
- // text: "Open Project...",
- // }),
- // await PredefinedMenuItem.new({
- // item: "Separator"
- // }),
- // await MenuItem.new({
- // enabled: false,
- // text: "New Session",
- // }),
- // await PredefinedMenuItem.new({
- // item: "Separator"
- // }),
- // await MenuItem.new({
- // enabled: false,
- // text: "Close Project",
- // })
- // ]
- // }),
+ await Submenu.new({
+ text: "File",
+ items: [
+ await MenuItem.new({
+ text: "New Session",
+ action: () => trigger("session.new"),
+ }),
+ await MenuItem.new({
+ text: "Open Project...",
+ action: () => trigger("project.open"),
+ }),
+ await PredefinedMenuItem.new({
+ item: "Separator",
+ }),
+ await PredefinedMenuItem.new({
+ item: "CloseWindow",
+ }),
+ ],
+ }),
await Submenu.new({
text: "Edit",
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()