diff --git a/packages/desktop-electron/src/main/index.ts b/packages/desktop-electron/src/main/index.ts index 64c2eb10f2..3f266e1145 100644 --- a/packages/desktop-electron/src/main/index.ts +++ b/packages/desktop-electron/src/main/index.ts @@ -32,7 +32,7 @@ import { initLogging } from "./logging" import { parseMarkdown } from "./markdown" import { createMenu } from "./menu" import { getDefaultServerUrl, getWslConfig, setDefaultServerUrl, setWslConfig, spawnLocalServer } from "./server" -import { createLoadingWindow, createMainWindow, setDockIcon } from "./windows" +import { createLoadingWindow, createMainWindow, setBackgroundColor, setDockIcon } from "./windows" const initEmitter = new EventEmitter() let initStep: InitStep = { phase: "server_waiting" } @@ -156,7 +156,6 @@ async function initialize() { const globals = { updaterEnabled: UPDATER_ENABLED, - wsl: getWslConfig().enabled, deepLinks: pendingDeepLinks, } @@ -231,6 +230,7 @@ registerIpcHandlers({ runUpdater: async (alertOnFail) => checkForUpdates(alertOnFail), checkUpdate: async () => checkUpdate(), installUpdate: async () => installUpdate(), + setBackgroundColor: (color) => setBackgroundColor(color), }) function killSidecar() { diff --git a/packages/desktop-electron/src/main/ipc.ts b/packages/desktop-electron/src/main/ipc.ts index c0d8773c29..6fb0ff3631 100644 --- a/packages/desktop-electron/src/main/ipc.ts +++ b/packages/desktop-electron/src/main/ipc.ts @@ -24,6 +24,7 @@ type Deps = { runUpdater: (alertOnFail: boolean) => Promise | void checkUpdate: () => Promise<{ updateAvailable: boolean; version?: string }> installUpdate: () => Promise | void + setBackgroundColor: (color: string) => void } export function registerIpcHandlers(deps: Deps) { @@ -53,6 +54,7 @@ export function registerIpcHandlers(deps: Deps) { ipcMain.handle("run-updater", (_event: IpcMainInvokeEvent, alertOnFail: boolean) => deps.runUpdater(alertOnFail)) ipcMain.handle("check-update", () => deps.checkUpdate()) ipcMain.handle("install-update", () => deps.installUpdate()) + ipcMain.handle("set-background-color", (_event: IpcMainInvokeEvent, color: string) => deps.setBackgroundColor(color)) ipcMain.handle("store-get", (_event: IpcMainInvokeEvent, name: string, key: string) => { const store = getStore(name) const value = store.get(key) diff --git a/packages/desktop-electron/src/main/menu.ts b/packages/desktop-electron/src/main/menu.ts index 53707ba7f2..d8997be310 100644 --- a/packages/desktop-electron/src/main/menu.ts +++ b/packages/desktop-electron/src/main/menu.ts @@ -1,6 +1,7 @@ import { BrowserWindow, Menu, shell } from "electron" import { UPDATER_ENABLED } from "./constants" +import { createMainWindow } from "./windows" type Deps = { trigger: (id: string) => void @@ -48,6 +49,11 @@ export function createMenu(deps: Deps) { submenu: [ { label: "New Session", accelerator: "Shift+Cmd+S", click: () => deps.trigger("session.new") }, { label: "Open Project...", accelerator: "Cmd+O", click: () => deps.trigger("project.open") }, + { + label: "New Window", + accelerator: "Cmd+Shift+N", + click: () => createMainWindow({ updaterEnabled: UPDATER_ENABLED }), + }, { type: "separator" }, { role: "close" }, ], diff --git a/packages/desktop-electron/src/main/windows.ts b/packages/desktop-electron/src/main/windows.ts index d4ec5ac790..0b7783f289 100644 --- a/packages/desktop-electron/src/main/windows.ts +++ b/packages/desktop-electron/src/main/windows.ts @@ -6,12 +6,21 @@ import type { TitlebarTheme } from "../preload/types" type Globals = { updaterEnabled: boolean - wsl: boolean deepLinks?: string[] } const root = dirname(fileURLToPath(import.meta.url)) +let backgroundColor: string | undefined + +export function setBackgroundColor(color: string) { + backgroundColor = color +} + +export function getBackgroundColor(): string | undefined { + return backgroundColor +} + function iconsDir() { return app.isPackaged ? join(process.resourcesPath, "icons") : join(root, "../../resources/icons") } @@ -59,6 +68,7 @@ export function createMainWindow(globals: Globals) { show: true, title: "OpenCode", icon: iconPath(), + backgroundColor, ...(process.platform === "darwin" ? { titleBarStyle: "hidden" as const, @@ -95,6 +105,7 @@ export function createLoadingWindow(globals: Globals) { center: true, show: true, icon: iconPath(), + backgroundColor, ...(process.platform === "darwin" ? { titleBarStyle: "hidden" as const } : {}), ...(process.platform === "win32" ? { @@ -131,7 +142,6 @@ function injectGlobals(win: BrowserWindow, globals: Globals) { const deepLinks = globals.deepLinks ?? [] const data = { updaterEnabled: globals.updaterEnabled, - wsl: globals.wsl, deepLinks: Array.isArray(deepLinks) ? deepLinks.splice(0) : deepLinks, } void win.webContents.executeJavaScript( diff --git a/packages/desktop-electron/src/preload/index.ts b/packages/desktop-electron/src/preload/index.ts index c1ed9afd22..b86fde2fdd 100644 --- a/packages/desktop-electron/src/preload/index.ts +++ b/packages/desktop-electron/src/preload/index.ts @@ -62,6 +62,7 @@ const api: ElectronAPI = { runUpdater: (alertOnFail) => ipcRenderer.invoke("run-updater", alertOnFail), checkUpdate: () => ipcRenderer.invoke("check-update"), installUpdate: () => ipcRenderer.invoke("install-update"), + setBackgroundColor: (color: string) => ipcRenderer.invoke("set-background-color", color), } contextBridge.exposeInMainWorld("api", api) diff --git a/packages/desktop-electron/src/preload/types.ts b/packages/desktop-electron/src/preload/types.ts index ae4ca213de..9d3ad38afb 100644 --- a/packages/desktop-electron/src/preload/types.ts +++ b/packages/desktop-electron/src/preload/types.ts @@ -66,4 +66,5 @@ export type ElectronAPI = { runUpdater: (alertOnFail: boolean) => Promise checkUpdate: () => Promise<{ updateAvailable: boolean; version?: string }> installUpdate: () => Promise + setBackgroundColor: (color: string) => Promise } diff --git a/packages/desktop-electron/src/renderer/index.tsx b/packages/desktop-electron/src/renderer/index.tsx index e313d5594c..1f88692871 100644 --- a/packages/desktop-electron/src/renderer/index.tsx +++ b/packages/desktop-electron/src/renderer/index.tsx @@ -10,9 +10,9 @@ import { useCommand, } from "@opencode-ai/app" import type { AsyncStorage } from "@solid-primitives/storage" +import { MemoryRouter } from "@solidjs/router" import { createResource, onCleanup, onMount, Show } from "solid-js" import { render } from "solid-js/web" -import { MemoryRouter } from "@solidjs/router" import pkg from "../../package.json" import { initI18n, t } from "./i18n" import { UPDATER_ENABLED } from "./updater" @@ -226,7 +226,9 @@ const createPlatform = (): Platform => { const image = await window.api.readClipboardImage().catch(() => null) if (!image) return null const blob = new Blob([image.buffer], { type: "image/png" }) - return new File([blob], `pasted-image-${Date.now()}.png`, { type: "image/png" }) + return new File([blob], `pasted-image-${Date.now()}.png`, { + type: "image/png", + }) }, } } @@ -286,6 +288,21 @@ render(() => { }) }) + // function ThemeSync() { + // const theme = useTheme() + + // createEffect(() => { + // theme.themeId() + // theme.mode() + // const bg = getComputedStyle(document.documentElement).getPropertyValue("--background-base").trim() + // if (bg) { + // void window.api.setBackgroundColor(bg) + // } + // }) + + // return null + // } + return ( @@ -298,6 +315,7 @@ render(() => { router={MemoryRouter} > + ) }} diff --git a/packages/desktop/src/menu.ts b/packages/desktop/src/menu.ts index de6a1d6a76..9005dd702f 100644 --- a/packages/desktop/src/menu.ts +++ b/packages/desktop/src/menu.ts @@ -1,12 +1,11 @@ import { Menu, MenuItem, PredefinedMenuItem, Submenu } from "@tauri-apps/api/menu" +import { openUrl } from "@tauri-apps/plugin-opener" 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 { commands } from "./bindings" import { installCli } from "./cli" import { initI18n, t } from "./i18n" -import { commands } from "./bindings" +import { runUpdater, UPDATER_ENABLED } from "./updater" export async function createMenu(trigger: (id: string) => void) { if (ostype() !== "macos") return diff --git a/packages/ui/src/theme/context.tsx b/packages/ui/src/theme/context.tsx index ad82a088d1..9808c8e841 100644 --- a/packages/ui/src/theme/context.tsx +++ b/packages/ui/src/theme/context.tsx @@ -1,9 +1,9 @@ -import { onMount, onCleanup, createEffect } from "solid-js" +import { createEffect, onCleanup, onMount } from "solid-js" import { createStore } from "solid-js/store" -import type { DesktopTheme } from "./types" -import { resolveThemeVariant, themeToCss } from "./resolve" -import { DEFAULT_THEMES } from "./default-themes" import { createSimpleContext } from "../context/helper" +import { DEFAULT_THEMES } from "./default-themes" +import { resolveThemeVariant, themeToCss } from "./resolve" +import type { DesktopTheme } from "./types" export type ColorScheme = "light" | "dark" | "system" @@ -87,6 +87,14 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ previewScheme: null as ColorScheme | null, }) + window.addEventListener("storage", (e) => { + if (e.key === STORAGE_KEYS.THEME_ID && e.newValue) setStore("themeId", e.newValue) + if (e.key === STORAGE_KEYS.COLOR_SCHEME && e.newValue) { + setStore("colorScheme", e.newValue as ColorScheme) + setStore("mode", e.newValue === "system" ? getSystemMode() : (e.newValue as any)) + } + }) + onMount(() => { const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)") const handler = () => {