feat(app): default servers on web

This commit is contained in:
adamelmore
2026-01-26 06:38:20 -06:00
parent 0edd304f42
commit 0d651eab3b
5 changed files with 52 additions and 11 deletions

View File

@@ -14,7 +14,7 @@ import { GlobalSyncProvider } from "@/context/global-sync"
import { PermissionProvider } from "@/context/permission" import { PermissionProvider } from "@/context/permission"
import { LayoutProvider } from "@/context/layout" import { LayoutProvider } from "@/context/layout"
import { GlobalSDKProvider } from "@/context/global-sdk" 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 { SettingsProvider } from "@/context/settings"
import { TerminalProvider } from "@/context/terminal" import { TerminalProvider } from "@/context/terminal"
import { PromptProvider } from "@/context/prompt" import { PromptProvider } from "@/context/prompt"
@@ -85,8 +85,19 @@ function ServerKey(props: ParentProps) {
} }
export function AppInterface(props: { defaultUrl?: string }) { 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 = () => { const defaultServerUrl = () => {
if (props.defaultUrl) return props.defaultUrl if (props.defaultUrl) return props.defaultUrl
if (stored) return stored
if (location.hostname.includes("opencode.ai")) return "http://localhost:4096" if (location.hostname.includes("opencode.ai")) return "http://localhost:4096"
if (import.meta.env.DEV) if (import.meta.env.DEV)
return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}` return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}`

View File

@@ -155,7 +155,7 @@ export function DialogSelectServer() {
}, },
{ initialValue: null }, { initialValue: null },
) )
const isDesktop = platform.platform === "desktop" const canDefault = createMemo(() => !!platform.getDefaultServerUrl && !!platform.setDefaultServerUrl)
const looksComplete = (value: string) => { const looksComplete = (value: string) => {
const normalized = normalizeServerUrl(value) const normalized = normalizeServerUrl(value)
@@ -505,7 +505,7 @@ export function DialogSelectServer() {
> >
<DropdownMenu.ItemLabel>{language.t("dialog.server.menu.edit")}</DropdownMenu.ItemLabel> <DropdownMenu.ItemLabel>{language.t("dialog.server.menu.edit")}</DropdownMenu.ItemLabel>
</DropdownMenu.Item> </DropdownMenu.Item>
<Show when={isDesktop && defaultUrl() !== i}> <Show when={canDefault() && defaultUrl() !== i}>
<DropdownMenu.Item <DropdownMenu.Item
onSelect={async () => { onSelect={async () => {
await platform.setDefaultServerUrl?.(i) await platform.setDefaultServerUrl?.(i)
@@ -517,7 +517,7 @@ export function DialogSelectServer() {
</DropdownMenu.ItemLabel> </DropdownMenu.ItemLabel>
</DropdownMenu.Item> </DropdownMenu.Item>
</Show> </Show>
<Show when={isDesktop && defaultUrl() === i}> <Show when={canDefault() && defaultUrl() === i}>
<DropdownMenu.Item <DropdownMenu.Item
onSelect={async () => { onSelect={async () => {
await platform.setDefaultServerUrl?.(null) await platform.setDefaultServerUrl?.(null)

View File

@@ -125,13 +125,21 @@ export function StatusPopover() {
const [defaultServerUrl, setDefaultServerUrl] = createSignal<string | undefined>() const [defaultServerUrl, setDefaultServerUrl] = createSignal<string | undefined>()
createEffect(() => { const refreshDefaultServerUrl = () => {
const result = platform.getDefaultServerUrl?.() const result = platform.getDefaultServerUrl?.()
if (!result) {
setDefaultServerUrl(undefined)
return
}
if (result instanceof Promise) { if (result instanceof Promise) {
result.then((url) => setDefaultServerUrl(url ? normalizeServerUrl(url) : undefined)) result.then((url) => setDefaultServerUrl(url ? normalizeServerUrl(url) : undefined))
return return
} }
if (result) setDefaultServerUrl(normalizeServerUrl(result)) setDefaultServerUrl(normalizeServerUrl(result))
}
createEffect(() => {
refreshDefaultServerUrl()
}) })
return ( return (
@@ -294,7 +302,7 @@ export function StatusPopover() {
<Button <Button
variant="secondary" variant="secondary"
class="mt-3 self-start h-8 px-3 py-1.5" class="mt-3 self-start h-8 px-3 py-1.5"
onClick={() => dialog.show(() => <DialogSelectServer />)} onClick={() => dialog.show(() => <DialogSelectServer />, refreshDefaultServerUrl)}
> >
{language.t("status.popover.action.manageServers")} {language.t("status.popover.action.manageServers")}
</Button> </Button>

View File

@@ -41,11 +41,11 @@ export type Platform = {
/** Fetch override */ /** Fetch override */
fetch?: typeof fetch fetch?: typeof fetch
/** Get the configured default server URL (desktop only) */ /** Get the configured default server URL (platform-specific) */
getDefaultServerUrl?(): Promise<string | null> getDefaultServerUrl?(): Promise<string | null> | string | null
/** Set the default server URL to use on app startup (desktop only) */ /** Set the default server URL to use on app startup (platform-specific) */
setDefaultServerUrl?(url: string | null): Promise<void> setDefaultServerUrl?(url: string | null): Promise<void> | void
/** Parse markdown to HTML using native parser (desktop only, returns unprocessed code blocks) */ /** Parse markdown to HTML using native parser (desktop only, returns unprocessed code blocks) */
parseMarkdown?(markdown: string): Promise<string> parseMarkdown?(markdown: string): Promise<string>

View File

@@ -6,6 +6,8 @@ import { dict as en } from "@/i18n/en"
import { dict as zh } from "@/i18n/zh" import { dict as zh } from "@/i18n/zh"
import pkg from "../package.json" import pkg from "../package.json"
const DEFAULT_SERVER_URL_KEY = "opencode.settings.dat:defaultServerUrl"
const root = document.getElementById("root") const root = document.getElementById("root")
if (import.meta.env.DEV && !(root instanceof HTMLElement)) { if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
const locale = (() => { const locale = (() => {
@@ -62,6 +64,26 @@ const platform: Platform = {
}) })
.catch(() => undefined) .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( render(