electron: use custom oc:// protocol for renderer windows (#23516)
This commit is contained in:
@@ -42,7 +42,7 @@ import { initLogging } from "./logging"
|
|||||||
import { parseMarkdown } from "./markdown"
|
import { parseMarkdown } from "./markdown"
|
||||||
import { createMenu } from "./menu"
|
import { createMenu } from "./menu"
|
||||||
import { getDefaultServerUrl, getWslConfig, setDefaultServerUrl, setWslConfig, spawnLocalServer } from "./server"
|
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 { drizzle } from "drizzle-orm/node-sqlite/driver"
|
||||||
import type { Server } from "virtual:opencode-server"
|
import type { Server } from "virtual:opencode-server"
|
||||||
|
|
||||||
@@ -106,6 +106,7 @@ function setupApp() {
|
|||||||
|
|
||||||
void app.whenReady().then(async () => {
|
void app.whenReady().then(async () => {
|
||||||
app.setAsDefaultProtocolClient("opencode")
|
app.setAsDefaultProtocolClient("opencode")
|
||||||
|
registerRendererProtocol()
|
||||||
setDockIcon()
|
setDockIcon()
|
||||||
setupAutoUpdater()
|
setupAutoUpdater()
|
||||||
await initialize()
|
await initialize()
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export async function spawnLocalServer(hostname: string, port: number, password:
|
|||||||
hostname,
|
hostname,
|
||||||
username: "opencode",
|
username: "opencode",
|
||||||
password,
|
password,
|
||||||
|
cors: ["oc://renderer"],
|
||||||
})
|
})
|
||||||
|
|
||||||
const wait = (async () => {
|
const wait = (async () => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import windowState from "electron-window-state"
|
import windowState from "electron-window-state"
|
||||||
import { app, BrowserWindow, nativeImage, nativeTheme } from "electron"
|
import { app, BrowserWindow, net, nativeImage, nativeTheme, protocol } from "electron"
|
||||||
import { dirname, join } from "node:path"
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path"
|
||||||
import { fileURLToPath } from "node:url"
|
import { fileURLToPath, pathToFileURL } from "node:url"
|
||||||
import type { TitlebarTheme } from "../preload/types"
|
import type { TitlebarTheme } from "../preload/types"
|
||||||
|
|
||||||
type Globals = {
|
type Globals = {
|
||||||
@@ -10,6 +10,20 @@ type Globals = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const root = dirname(fileURLToPath(import.meta.url))
|
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
|
let backgroundColor: string | undefined
|
||||||
|
|
||||||
@@ -131,6 +145,25 @@ export function createLoadingWindow(globals: Globals) {
|
|||||||
return win
|
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) {
|
function loadWindow(win: BrowserWindow, html: string) {
|
||||||
const devUrl = process.env.ELECTRON_RENDERER_URL
|
const devUrl = process.env.ELECTRON_RENDERER_URL
|
||||||
if (devUrl) {
|
if (devUrl) {
|
||||||
@@ -139,7 +172,7 @@ function loadWindow(win: BrowserWindow, html: string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
void win.loadFile(join(root, `../renderer/${html}`))
|
void win.loadURL(`${rendererProtocol}://${rendererHost}/${html}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function injectGlobals(win: BrowserWindow, globals: Globals) {
|
function injectGlobals(win: BrowserWindow, globals: Globals) {
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ const root = resolve(dir, "../..")
|
|||||||
const html = async (name: string) => Bun.file(join(dir, name)).text()
|
const html = async (name: string) => Bun.file(join(dir, name)).text()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Electron loads renderer HTML via `win.loadFile()` which uses the `file://`
|
* Packaged Electron windows load renderer HTML via the privileged `oc://`
|
||||||
* protocol. Absolute paths like `src="/foo.js"` resolve to the filesystem root
|
* protocol. Root-relative asset paths like `src="/foo.js"` would resolve from
|
||||||
* (e.g. `file:///C:/foo.js` on Windows) instead of relative to the app bundle.
|
* the protocol origin root instead of relative to the current HTML entrypoint.
|
||||||
*
|
*
|
||||||
* All local resource references must use relative paths (`./`).
|
* All local resource references must use relative paths (`./`).
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user