Przeglądaj źródła

feat(app): default servers on web

adamelmore 3 tygodni temu
rodzic
commit
0d651eab3b

+ 12 - 1
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"}`

+ 3 - 3
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() {
                           >
                             <DropdownMenu.ItemLabel>{language.t("dialog.server.menu.edit")}</DropdownMenu.ItemLabel>
                           </DropdownMenu.Item>
-                          <Show when={isDesktop && defaultUrl() !== i}>
+                          <Show when={canDefault() && defaultUrl() !== i}>
                             <DropdownMenu.Item
                               onSelect={async () => {
                                 await platform.setDefaultServerUrl?.(i)
@@ -517,7 +517,7 @@ export function DialogSelectServer() {
                               </DropdownMenu.ItemLabel>
                             </DropdownMenu.Item>
                           </Show>
-                          <Show when={isDesktop && defaultUrl() === i}>
+                          <Show when={canDefault() && defaultUrl() === i}>
                             <DropdownMenu.Item
                               onSelect={async () => {
                                 await platform.setDefaultServerUrl?.(null)

+ 11 - 3
packages/app/src/components/status-popover.tsx

@@ -125,13 +125,21 @@ export function StatusPopover() {
 
   const [defaultServerUrl, setDefaultServerUrl] = createSignal<string | undefined>()
 
-  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() {
                 <Button
                   variant="secondary"
                   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")}
                 </Button>

+ 4 - 4
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<string | null>
+  /** Get the configured default server URL (platform-specific) */
+  getDefaultServerUrl?(): Promise<string | null> | string | null
 
-  /** Set the default server URL to use on app startup (desktop only) */
-  setDefaultServerUrl?(url: string | null): Promise<void>
+  /** Set the default server URL to use on app startup (platform-specific) */
+  setDefaultServerUrl?(url: string | null): Promise<void> | void
 
   /** Parse markdown to HTML using native parser (desktop only, returns unprocessed code blocks) */
   parseMarkdown?(markdown: string): Promise<string>

+ 22 - 0
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(