Kaynağa Gözat

electron: use custom oc:// protocol for renderer windows

Brendan Allan 16 saat önce
ebeveyn
işleme
6369a3fc12

+ 2 - 1
packages/desktop-electron/src/main/index.ts

@@ -42,7 +42,7 @@ import { initLogging } from "./logging"
 import { parseMarkdown } from "./markdown"
 import { createMenu } from "./menu"
 import { getDefaultServerUrl, getWslConfig, setDefaultServerUrl, setWslConfig, spawnLocalServer } from "./server"
-import { createLoadingWindow, createMainWindow, setBackgroundColor, setDockIcon } from "./windows"
+import { createLoadingWindow, createMainWindow, registerRendererProtocol, setBackgroundColor, setDockIcon } from "./windows"
 import { drizzle } from "drizzle-orm/node-sqlite/driver"
 import type { Server } from "virtual:opencode-server"
 
@@ -106,6 +106,7 @@ function setupApp() {
 
   void app.whenReady().then(async () => {
     app.setAsDefaultProtocolClient("opencode")
+    registerRendererProtocol()
     setDockIcon()
     setupAutoUpdater()
     await initialize()

+ 1 - 0
packages/desktop-electron/src/main/server.ts

@@ -39,6 +39,7 @@ export async function spawnLocalServer(hostname: string, port: number, password:
     hostname,
     username: "opencode",
     password,
+    cors: ["oc://renderer"],
   })
 
   const wait = (async () => {

+ 37 - 4
packages/desktop-electron/src/main/windows.ts

@@ -1,7 +1,7 @@
 import windowState from "electron-window-state"
-import { app, BrowserWindow, nativeImage, nativeTheme } from "electron"
-import { dirname, join } from "node:path"
-import { fileURLToPath } from "node:url"
+import { app, BrowserWindow, net, nativeImage, nativeTheme, protocol } from "electron"
+import { dirname, isAbsolute, join, relative, resolve } from "node:path"
+import { fileURLToPath, pathToFileURL } from "node:url"
 import type { TitlebarTheme } from "../preload/types"
 
 type Globals = {
@@ -10,6 +10,20 @@ type Globals = {
 }
 
 const root = dirname(fileURLToPath(import.meta.url))
+const rendererRoot = join(root, "../renderer")
+const rendererProtocol = "oc"
+const rendererHost = "renderer"
+
+protocol.registerSchemesAsPrivileged([
+  {
+    scheme: rendererProtocol,
+    privileges: {
+      secure: true,
+      standard: true,
+      supportFetchAPI: true,
+    },
+  },
+])
 
 let backgroundColor: string | undefined
 
@@ -131,6 +145,25 @@ export function createLoadingWindow(globals: Globals) {
   return win
 }
 
+export function registerRendererProtocol() {
+  if (protocol.isProtocolHandled(rendererProtocol)) return
+
+  protocol.handle(rendererProtocol, (request) => {
+    const url = new URL(request.url)
+    if (url.host !== rendererHost) {
+      return new Response("Not found", { status: 404 })
+    }
+
+    const file = resolve(rendererRoot, `.${decodeURIComponent(url.pathname)}`)
+    const rel = relative(rendererRoot, file)
+    if (rel.startsWith("..") || isAbsolute(rel)) {
+      return new Response("Not found", { status: 404 })
+    }
+
+    return net.fetch(pathToFileURL(file).toString())
+  })
+}
+
 function loadWindow(win: BrowserWindow, html: string) {
   const devUrl = process.env.ELECTRON_RENDERER_URL
   if (devUrl) {
@@ -139,7 +172,7 @@ function loadWindow(win: BrowserWindow, html: string) {
     return
   }
 
-  void win.loadFile(join(root, `../renderer/${html}`))
+  void win.loadURL(`${rendererProtocol}://${rendererHost}/${html}`)
 }
 
 function injectGlobals(win: BrowserWindow, globals: Globals) {

+ 3 - 3
packages/desktop-electron/src/renderer/html.test.ts

@@ -9,9 +9,9 @@ const root = resolve(dir, "../..")
 const html = async (name: string) => Bun.file(join(dir, name)).text()
 
 /**
- * Electron loads renderer HTML via `win.loadFile()` which uses the `file://`
- * protocol. Absolute paths like `src="/foo.js"` resolve to the filesystem root
- * (e.g. `file:///C:/foo.js` on Windows) instead of relative to the app bundle.
+ * Packaged Electron windows load renderer HTML via the privileged `oc://`
+ * protocol. Root-relative asset paths like `src="/foo.js"` would resolve from
+ * the protocol origin root instead of relative to the current HTML entrypoint.
  *
  * All local resource references must use relative paths (`./`).
  */