summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLuke Parker <[email protected]>2026-03-12 14:19:44 +1000
committerGitHub <[email protected]>2026-03-12 12:19:44 +0800
commit54e7baa6cfff86627e5555842560b4a20e4be424 (patch)
tree901937a48c87d04a853ea4c62a79d2b86640ee2c
parent1d7fcd40b45548e20b43e29cd16b83899c8f6eb1 (diff)
downloadopencode-54e7baa6cfff86627e5555842560b4a20e4be424.tar.gz
opencode-54e7baa6cfff86627e5555842560b4a20e4be424.zip
fix(desktop-electron): fix resource loading under file:// protocol (#17125)
-rw-r--r--packages/desktop-electron/electron.vite.config.ts2
-rw-r--r--packages/desktop-electron/src/renderer/html.test.ts62
-rw-r--r--packages/desktop-electron/src/renderer/index.html17
-rw-r--r--packages/desktop-electron/src/renderer/loading.html17
-rw-r--r--packages/desktop-electron/tsconfig.json3
-rw-r--r--packages/ui/src/components/font.tsx7
6 files changed, 86 insertions, 22 deletions
diff --git a/packages/desktop-electron/electron.vite.config.ts b/packages/desktop-electron/electron.vite.config.ts
index 80c1d6b70..6903d5ed2 100644
--- a/packages/desktop-electron/electron.vite.config.ts
+++ b/packages/desktop-electron/electron.vite.config.ts
@@ -27,7 +27,7 @@ export default defineConfig({
},
renderer: {
plugins: [appPlugin],
- publicDir: "../app/public",
+ publicDir: "../../../app/public",
root: "src/renderer",
build: {
rollupOptions: {
diff --git a/packages/desktop-electron/src/renderer/html.test.ts b/packages/desktop-electron/src/renderer/html.test.ts
new file mode 100644
index 000000000..bd8281c2f
--- /dev/null
+++ b/packages/desktop-electron/src/renderer/html.test.ts
@@ -0,0 +1,62 @@
+import { describe, expect, test } from "bun:test"
+import { join, dirname, resolve } from "node:path"
+import { existsSync } from "node:fs"
+import { fileURLToPath } from "node:url"
+
+const dir = dirname(fileURLToPath(import.meta.url))
+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.
+ *
+ * All local resource references must use relative paths (`./`).
+ */
+describe("electron renderer html", () => {
+ for (const name of ["index.html", "loading.html"]) {
+ describe(name, () => {
+ test("script src attributes use relative paths", async () => {
+ const content = await html(name)
+ const srcs = [...content.matchAll(/\bsrc=["']([^"']+)["']/g)].map((m) => m[1])
+ for (const src of srcs) {
+ expect(src).not.toMatch(/^\/[^/]/)
+ }
+ })
+
+ test("link href attributes use relative paths", async () => {
+ const content = await html(name)
+ const hrefs = [...content.matchAll(/<link[^>]+href=["']([^"']+)["']/g)].map((m) => m[1])
+ for (const href of hrefs) {
+ expect(href).not.toMatch(/^\/[^/]/)
+ }
+ })
+
+ test("no web manifest link (not applicable in Electron)", async () => {
+ const content = await html(name)
+ expect(content).not.toContain('rel="manifest"')
+ })
+ })
+ }
+})
+
+/**
+ * Vite resolves `publicDir` relative to `root`, not the config file.
+ * This test reads the actual values from electron.vite.config.ts to catch
+ * regressions where the publicDir path no longer resolves correctly
+ * after the renderer root is accounted for.
+ */
+describe("electron vite publicDir", () => {
+ test("configured publicDir resolves to a directory with oc-theme-preload.js", async () => {
+ const config = await Bun.file(join(root, "electron.vite.config.ts")).text()
+ const pub = config.match(/publicDir:\s*["']([^"']+)["']/)
+ const rendererRoot = config.match(/root:\s*["']([^"']+)["']/)
+ expect(pub).not.toBeNull()
+ expect(rendererRoot).not.toBeNull()
+ const resolved = resolve(root, rendererRoot![1], pub![1])
+ expect(existsSync(resolved)).toBe(true)
+ expect(existsSync(join(resolved, "oc-theme-preload.js"))).toBe(true)
+ })
+})
diff --git a/packages/desktop-electron/src/renderer/index.html b/packages/desktop-electron/src/renderer/index.html
index 175640819..dd8675ee6 100644
--- a/packages/desktop-electron/src/renderer/index.html
+++ b/packages/desktop-electron/src/renderer/index.html
@@ -4,20 +4,19 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OpenCode</title>
- <link rel="icon" type="image/png" href="/favicon-96x96-v3.png" sizes="96x96" />
- <link rel="icon" type="image/svg+xml" href="/favicon-v3.svg" />
- <link rel="shortcut icon" href="/favicon-v3.ico" />
- <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-v3.png" />
- <link rel="manifest" href="/site.webmanifest" />
+ <link rel="icon" type="image/png" href="./favicon-96x96-v3.png" sizes="96x96" />
+ <link rel="icon" type="image/svg+xml" href="./favicon-v3.svg" />
+ <link rel="shortcut icon" href="./favicon-v3.ico" />
+ <link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon-v3.png" />
<meta name="theme-color" content="#F8F7F7" />
<meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" />
- <meta property="og:image" content="/social-share.png" />
- <meta property="twitter:image" content="/social-share.png" />
- <script id="oc-theme-preload-script" src="/oc-theme-preload.js"></script>
+ <meta property="og:image" content="./social-share.png" />
+ <meta property="twitter:image" content="./social-share.png" />
+ <script id="oc-theme-preload-script" src="./oc-theme-preload.js"></script>
</head>
<body class="antialiased overscroll-none text-12-regular overflow-hidden">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" class="flex flex-col h-dvh"></div>
- <script src="/index.tsx" type="module"></script>
+ <script src="./index.tsx" type="module"></script>
</body>
</html>
diff --git a/packages/desktop-electron/src/renderer/loading.html b/packages/desktop-electron/src/renderer/loading.html
index 8def243b4..ae3725af6 100644
--- a/packages/desktop-electron/src/renderer/loading.html
+++ b/packages/desktop-electron/src/renderer/loading.html
@@ -4,20 +4,19 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OpenCode</title>
- <link rel="icon" type="image/png" href="/favicon-96x96-v3.png" sizes="96x96" />
- <link rel="icon" type="image/svg+xml" href="/favicon-v3.svg" />
- <link rel="shortcut icon" href="/favicon-v3.ico" />
- <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-v3.png" />
- <link rel="manifest" href="/site.webmanifest" />
+ <link rel="icon" type="image/png" href="./favicon-96x96-v3.png" sizes="96x96" />
+ <link rel="icon" type="image/svg+xml" href="./favicon-v3.svg" />
+ <link rel="shortcut icon" href="./favicon-v3.ico" />
+ <link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon-v3.png" />
<meta name="theme-color" content="#F8F7F7" />
<meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" />
- <meta property="og:image" content="/social-share.png" />
- <meta property="twitter:image" content="/social-share.png" />
- <script id="oc-theme-preload-script" src="/oc-theme-preload.js"></script>
+ <meta property="og:image" content="./social-share.png" />
+ <meta property="twitter:image" content="./social-share.png" />
+ <script id="oc-theme-preload-script" src="./oc-theme-preload.js"></script>
</head>
<body class="antialiased overscroll-none text-12-regular overflow-hidden">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" class="flex flex-col h-dvh"></div>
- <script src="/loading.tsx" type="module"></script>
+ <script src="./loading.tsx" type="module"></script>
</body>
</html>
diff --git a/packages/desktop-electron/tsconfig.json b/packages/desktop-electron/tsconfig.json
index 160f6c3fd..9637fe03d 100644
--- a/packages/desktop-electron/tsconfig.json
+++ b/packages/desktop-electron/tsconfig.json
@@ -18,5 +18,6 @@
"types": ["vite/client", "node", "electron"]
},
"references": [{ "path": "../app" }],
- "include": ["src", "package.json"]
+ "include": ["src", "package.json"],
+ "exclude": ["src/**/*.test.ts"]
}
diff --git a/packages/ui/src/components/font.tsx b/packages/ui/src/components/font.tsx
index bcb8863c8..f735747a4 100644
--- a/packages/ui/src/components/font.tsx
+++ b/packages/ui/src/components/font.tsx
@@ -1,3 +1,4 @@
+import { Show } from "solid-js"
import { Style, Link } from "@solidjs/meta"
import inter from "../assets/fonts/inter.woff2"
import ibmPlexMonoRegular from "../assets/fonts/ibm-plex-mono.woff2"
@@ -166,8 +167,10 @@ export const Font = () => {
}
${monoNerdCss}
`}</Style>
- <Link rel="preload" href={inter} as="font" type="font/woff2" crossorigin="anonymous" />
- <Link rel="preload" href={ibmPlexMonoRegular} as="font" type="font/woff2" crossorigin="anonymous" />
+ <Show when={typeof location === "undefined" || location.protocol !== "file:"}>
+ <Link rel="preload" href={inter} as="font" type="font/woff2" crossorigin="anonymous" />
+ <Link rel="preload" href={ibmPlexMonoRegular} as="font" type="font/woff2" crossorigin="anonymous" />
+ </Show>
</>
)
}