From 0d651eab3b1c3ce8b55fe5d70f1e348c4b9753df Mon Sep 17 00:00:00 2001
From: adamelmore <2363879+adamdottv@users.noreply.github.com>
Date: Mon, 26 Jan 2026 06:38:20 -0600
Subject: [PATCH] feat(app): default servers on web
---
packages/app/src/app.tsx | 13 ++++++++++-
.../src/components/dialog-select-server.tsx | 6 ++---
.../app/src/components/status-popover.tsx | 14 +++++++++---
packages/app/src/context/platform.tsx | 8 +++----
packages/app/src/entry.tsx | 22 +++++++++++++++++++
5 files changed, 52 insertions(+), 11 deletions(-)
diff --git a/packages/app/src/app.tsx b/packages/app/src/app.tsx
index d5009c8d1..6b9f887fa 100644
--- a/packages/app/src/app.tsx
+++ b/packages/app/src/app.tsx
@@ -14,7 +14,7 @@ import { GlobalSyncProvider } from "@/context/global-sync"
import { PermissionProvider } from "@/context/permission"
import { LayoutProvider } from "@/context/layout"
import { GlobalSDKProvider } from "@/context/global-sdk"
-import { ServerProvider, useServer } from "@/context/server"
+import { normalizeServerUrl, ServerProvider, useServer } from "@/context/server"
import { SettingsProvider } from "@/context/settings"
import { TerminalProvider } from "@/context/terminal"
import { PromptProvider } from "@/context/prompt"
@@ -85,8 +85,19 @@ function ServerKey(props: ParentProps) {
}
export function AppInterface(props: { defaultUrl?: string }) {
+ const platform = usePlatform()
+
+ const stored = (() => {
+ if (platform.platform !== "web") return
+ const result = platform.getDefaultServerUrl?.()
+ if (result instanceof Promise) return
+ if (!result) return
+ return normalizeServerUrl(result)
+ })()
+
const defaultServerUrl = () => {
if (props.defaultUrl) return props.defaultUrl
+ if (stored) return stored
if (location.hostname.includes("opencode.ai")) return "http://localhost:4096"
if (import.meta.env.DEV)
return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}`
diff --git a/packages/app/src/components/dialog-select-server.tsx b/packages/app/src/components/dialog-select-server.tsx
index 2206e4a48..774ad51cc 100644
--- a/packages/app/src/components/dialog-select-server.tsx
+++ b/packages/app/src/components/dialog-select-server.tsx
@@ -155,7 +155,7 @@ export function DialogSelectServer() {
},
{ initialValue: null },
)
- const isDesktop = platform.platform === "desktop"
+ const canDefault = createMemo(() => !!platform.getDefaultServerUrl && !!platform.setDefaultServerUrl)
const looksComplete = (value: string) => {
const normalized = normalizeServerUrl(value)
@@ -505,7 +505,7 @@ export function DialogSelectServer() {
>
{language.t("dialog.server.menu.edit")}
-
+
{
await platform.setDefaultServerUrl?.(i)
@@ -517,7 +517,7 @@ export function DialogSelectServer() {
-
+
{
await platform.setDefaultServerUrl?.(null)
diff --git a/packages/app/src/components/status-popover.tsx b/packages/app/src/components/status-popover.tsx
index 3963a54f3..cb45c3689 100644
--- a/packages/app/src/components/status-popover.tsx
+++ b/packages/app/src/components/status-popover.tsx
@@ -125,13 +125,21 @@ export function StatusPopover() {
const [defaultServerUrl, setDefaultServerUrl] = createSignal()
- createEffect(() => {
+ const refreshDefaultServerUrl = () => {
const result = platform.getDefaultServerUrl?.()
+ if (!result) {
+ setDefaultServerUrl(undefined)
+ return
+ }
if (result instanceof Promise) {
result.then((url) => setDefaultServerUrl(url ? normalizeServerUrl(url) : undefined))
return
}
- if (result) setDefaultServerUrl(normalizeServerUrl(result))
+ setDefaultServerUrl(normalizeServerUrl(result))
+ }
+
+ createEffect(() => {
+ refreshDefaultServerUrl()
})
return (
@@ -294,7 +302,7 @@ export function StatusPopover() {
diff --git a/packages/app/src/context/platform.tsx b/packages/app/src/context/platform.tsx
index 89056b2c8..b97f70fea 100644
--- a/packages/app/src/context/platform.tsx
+++ b/packages/app/src/context/platform.tsx
@@ -41,11 +41,11 @@ export type Platform = {
/** Fetch override */
fetch?: typeof fetch
- /** Get the configured default server URL (desktop only) */
- getDefaultServerUrl?(): Promise
+ /** Get the configured default server URL (platform-specific) */
+ getDefaultServerUrl?(): Promise | string | null
- /** Set the default server URL to use on app startup (desktop only) */
- setDefaultServerUrl?(url: string | null): Promise
+ /** Set the default server URL to use on app startup (platform-specific) */
+ setDefaultServerUrl?(url: string | null): Promise | void
/** Parse markdown to HTML using native parser (desktop only, returns unprocessed code blocks) */
parseMarkdown?(markdown: string): Promise
diff --git a/packages/app/src/entry.tsx b/packages/app/src/entry.tsx
index df8547636..7fe03bb6a 100644
--- a/packages/app/src/entry.tsx
+++ b/packages/app/src/entry.tsx
@@ -6,6 +6,8 @@ import { dict as en } from "@/i18n/en"
import { dict as zh } from "@/i18n/zh"
import pkg from "../package.json"
+const DEFAULT_SERVER_URL_KEY = "opencode.settings.dat:defaultServerUrl"
+
const root = document.getElementById("root")
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
const locale = (() => {
@@ -62,6 +64,26 @@ const platform: Platform = {
})
.catch(() => undefined)
},
+ getDefaultServerUrl: () => {
+ if (typeof localStorage === "undefined") return null
+ try {
+ return localStorage.getItem(DEFAULT_SERVER_URL_KEY)
+ } catch {
+ return null
+ }
+ },
+ setDefaultServerUrl: (url) => {
+ if (typeof localStorage === "undefined") return
+ try {
+ if (url) {
+ localStorage.setItem(DEFAULT_SERVER_URL_KEY, url)
+ return
+ }
+ localStorage.removeItem(DEFAULT_SERVER_URL_KEY)
+ } catch {
+ return
+ }
+ },
}
render(