Skip to content

Commit 7ba732c

Browse files
Apply PR #23516: electron: use custom oc:// protocol for renderer windows
2 parents 56ee901 + 6369a3f commit 7ba732c

File tree

4 files changed

+43
-8
lines changed

4 files changed

+43
-8
lines changed

packages/desktop-electron/src/main/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import { initLogging } from "./logging"
4242
import { parseMarkdown } from "./markdown"
4343
import { createMenu } from "./menu"
4444
import { getDefaultServerUrl, getWslConfig, setDefaultServerUrl, setWslConfig, spawnLocalServer } from "./server"
45-
import { createLoadingWindow, createMainWindow, setBackgroundColor, setDockIcon } from "./windows"
45+
import { createLoadingWindow, createMainWindow, registerRendererProtocol, setBackgroundColor, setDockIcon } from "./windows"
4646
import { drizzle } from "drizzle-orm/node-sqlite/driver"
4747
import type { Server } from "virtual:opencode-server"
4848

@@ -106,6 +106,7 @@ function setupApp() {
106106

107107
void app.whenReady().then(async () => {
108108
app.setAsDefaultProtocolClient("opencode")
109+
registerRendererProtocol()
109110
setDockIcon()
110111
setupAutoUpdater()
111112
await initialize()

packages/desktop-electron/src/main/server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export async function spawnLocalServer(hostname: string, port: number, password:
5151
hostname,
5252
username: "opencode",
5353
password,
54+
cors: ["oc://renderer"],
5455
})
5556

5657
const relayURL = (process.env.OPENCODE_EXPERIMENTAL_PUSH_RELAY_URL ?? DEFAULT_RELAY_URL).trim()

packages/desktop-electron/src/main/windows.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import windowState from "electron-window-state"
2-
import { app, BrowserWindow, nativeImage, nativeTheme } from "electron"
3-
import { dirname, join } from "node:path"
4-
import { fileURLToPath } from "node:url"
2+
import { app, BrowserWindow, net, nativeImage, nativeTheme, protocol } from "electron"
3+
import { dirname, isAbsolute, join, relative, resolve } from "node:path"
4+
import { fileURLToPath, pathToFileURL } from "node:url"
55
import type { TitlebarTheme } from "../preload/types"
66

77
type Globals = {
@@ -10,6 +10,20 @@ type Globals = {
1010
}
1111

1212
const root = dirname(fileURLToPath(import.meta.url))
13+
const rendererRoot = join(root, "../renderer")
14+
const rendererProtocol = "oc"
15+
const rendererHost = "renderer"
16+
17+
protocol.registerSchemesAsPrivileged([
18+
{
19+
scheme: rendererProtocol,
20+
privileges: {
21+
secure: true,
22+
standard: true,
23+
supportFetchAPI: true,
24+
},
25+
},
26+
])
1327

1428
let backgroundColor: string | undefined
1529

@@ -131,6 +145,25 @@ export function createLoadingWindow(globals: Globals) {
131145
return win
132146
}
133147

148+
export function registerRendererProtocol() {
149+
if (protocol.isProtocolHandled(rendererProtocol)) return
150+
151+
protocol.handle(rendererProtocol, (request) => {
152+
const url = new URL(request.url)
153+
if (url.host !== rendererHost) {
154+
return new Response("Not found", { status: 404 })
155+
}
156+
157+
const file = resolve(rendererRoot, `.${decodeURIComponent(url.pathname)}`)
158+
const rel = relative(rendererRoot, file)
159+
if (rel.startsWith("..") || isAbsolute(rel)) {
160+
return new Response("Not found", { status: 404 })
161+
}
162+
163+
return net.fetch(pathToFileURL(file).toString())
164+
})
165+
}
166+
134167
function loadWindow(win: BrowserWindow, html: string) {
135168
const devUrl = process.env.ELECTRON_RENDERER_URL
136169
if (devUrl) {
@@ -139,7 +172,7 @@ function loadWindow(win: BrowserWindow, html: string) {
139172
return
140173
}
141174

142-
void win.loadFile(join(root, `../renderer/${html}`))
175+
void win.loadURL(`${rendererProtocol}://${rendererHost}/${html}`)
143176
}
144177

145178
function injectGlobals(win: BrowserWindow, globals: Globals) {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ const root = resolve(dir, "../..")
99
const html = async (name: string) => Bun.file(join(dir, name)).text()
1010

1111
/**
12-
* Electron loads renderer HTML via `win.loadFile()` which uses the `file://`
13-
* protocol. Absolute paths like `src="/foo.js"` resolve to the filesystem root
14-
* (e.g. `file:///C:/foo.js` on Windows) instead of relative to the app bundle.
12+
* Packaged Electron windows load renderer HTML via the privileged `oc://`
13+
* protocol. Root-relative asset paths like `src="/foo.js"` would resolve from
14+
* the protocol origin root instead of relative to the current HTML entrypoint.
1515
*
1616
* All local resource references must use relative paths (`./`).
1717
*/

0 commit comments

Comments
 (0)