fix(desktop): open external links in system browser (#39820)

This commit is contained in:
Luke Parker 2026-07-31 18:02:11 +10:00 committed by GitHub
commit 2039c90c06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 242 additions and 258 deletions

View file

@ -4,7 +4,6 @@ import {
ACCEPTED_FILE_EXTENSIONS,
AppBaseProviders,
AppInterface,
handleNotificationClick,
loadLocaleDict,
normalizeLocale,
type Locale,
@ -18,11 +17,11 @@ import type { UpdaterState } from "@opencode-ai/app/updater"
import * as Sentry from "@sentry/solid"
import type { AsyncStorage } from "@solid-primitives/storage"
import { createMemoryHistory, MemoryRouter, type BaseRouterProps } from "@solidjs/router"
import { createEffect, createMemo, createResource, createSignal, onCleanup, onMount, Show } from "solid-js"
import { createEffect, createMemo, createResource, createSignal, onCleanup, Show } from "solid-js"
import { render } from "solid-js/web"
import pkg from "../../package.json"
import { initI18n, t } from "./i18n"
import { initializationData, initializationReady } from "./initialization"
import { initializationData } from "./initialization"
import { DesktopFirstLaunchOnboarding } from "./onboarding"
import { resetZoom, setPinchZoomEnabled, webviewZoom, zoomIn, zoomOut } from "./webview-zoom"
import { windowFullscreen } from "./window-fullscreen"
@ -209,8 +208,11 @@ const createPlatform = (windowState: DesktopWindowState): Platform => {
})
},
openLink(url: string) {
window.api.openLink(url)
openExternal(url: string) {
window.api.openExternal(url)
},
openLocalFile(url: string) {
window.api.openLocalFile(url)
},
async openPath(path: string, app?: string) {
if (os === "windows") {
@ -223,14 +225,6 @@ const createPlatform = (windowState: DesktopWindowState): Platform => {
return window.api.revealPath(path)
},
back() {
window.history.back()
},
forward() {
window.history.forward()
},
storage,
updater: {
@ -250,7 +244,7 @@ const createPlatform = (windowState: DesktopWindowState): Platform => {
window.api.relaunch()
},
notify: async (title, description, href) => {
notify: async (title, description, onClick) => {
const focused = await window.api.getWindowFocused().catch(() => document.hasFocus())
if (focused) return
@ -261,7 +255,7 @@ const createPlatform = (windowState: DesktopWindowState): Platform => {
notification.onclick = () => {
void window.api.showWindow()
void window.api.setWindowFocus()
handleNotificationClick(href)
onClick?.()
notification.close()
}
},
@ -291,8 +285,6 @@ const createPlatform = (windowState: DesktopWindowState): Platform => {
await window.api.setDisplayBackend(backend)
},
parseMarkdown: (markdown: string) => window.api.parseMarkdownCommand(markdown),
webviewZoom,
windowFullscreen,
@ -346,8 +338,6 @@ function DesktopRoot(props: { windowState: DesktopWindowState }) {
return next satisfies Locale
}
const [windowCount] = createResource(() => window.api.getWindowCount())
// Fetch sidecar credentials (available immediately, before health check)
const [sidecar] = createResource(() => window.api.awaitInitialization())
@ -358,14 +348,6 @@ function DesktopRoot(props: { windowState: DesktopWindowState }) {
)
const onboarding = Promise.withResolvers<void>()
function handleClick(e: MouseEvent) {
const link = (e.target as HTMLElement).closest("a.external-link") as HTMLAnchorElement | null
if (link?.href) {
e.preventDefault()
platform.openLink(link.href)
}
}
function Inner() {
const cmd = useCommand()
menuTrigger = (id) => cmd.trigger(id)
@ -388,7 +370,7 @@ function DesktopRoot(props: { windowState: DesktopWindowState }) {
const wslServers = useWslServers()
const ready = createMemo(
() =>
!defaultServer.loading && !sidecar.loading && !windowCount.loading && !locale.loading && !wslServers.isLoading,
!defaultServer.loading && !sidecar.loading && !locale.loading && !wslServers.isLoading,
)
const servers = createMemo(() => {
const data = initializationData(sidecar)
@ -435,13 +417,6 @@ function DesktopRoot(props: { windowState: DesktopWindowState }) {
)
}
onMount(() => {
document.addEventListener("click", handleClick)
onCleanup(() => {
document.removeEventListener("click", handleClick)
})
})
return (
<PlatformProvider value={platform}>
<AppBaseProviders locale={locale.latest}>