mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-21 19:35:10 +00:00
.
This commit is contained in:
@@ -59,7 +59,6 @@ interface ServerFormProps {
|
||||
placeholder: string
|
||||
busy: boolean
|
||||
error: string
|
||||
status: boolean | undefined
|
||||
onChange: (value: string) => void
|
||||
onNameChange: (value: string) => void
|
||||
onUsernameChange: (value: string) => void
|
||||
@@ -80,38 +79,6 @@ function isWslSidecar(conn: ServerConnection.Any): conn is ServerConnection.Side
|
||||
return conn.type === "sidecar" && conn.variant === "wsl"
|
||||
}
|
||||
|
||||
function useServerPreview() {
|
||||
const checkServerHealth = useCheckServerHealth()
|
||||
|
||||
const looksComplete = (value: string) => {
|
||||
const normalized = normalizeServerUrl(value)
|
||||
if (!normalized) return false
|
||||
const host = normalized.replace(/^https?:\/\//, "").split("/")[0]
|
||||
if (!host) return false
|
||||
if (host.includes("localhost") || host.startsWith("127.0.0.1")) return true
|
||||
return host.includes(".") || host.includes(":")
|
||||
}
|
||||
|
||||
const previewStatus = async (
|
||||
value: string,
|
||||
username: string,
|
||||
password: string,
|
||||
setStatus: (value: boolean | undefined) => void,
|
||||
) => {
|
||||
setStatus(undefined)
|
||||
if (!looksComplete(value)) return
|
||||
const normalized = normalizeServerUrl(value)
|
||||
if (!normalized) return
|
||||
const http: ServerConnection.HttpBase = { url: normalized }
|
||||
if (username) http.username = username
|
||||
if (password) http.password = password
|
||||
const result = await checkServerHealth(http)
|
||||
setStatus(result.healthy)
|
||||
}
|
||||
|
||||
return { previewStatus }
|
||||
}
|
||||
|
||||
function ServerForm(props: ServerFormProps) {
|
||||
const language = useLanguage()
|
||||
const keyDown = (event: KeyboardEvent) => {
|
||||
@@ -184,7 +151,6 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
const language = useLanguage()
|
||||
const { defaultKey, canDefault, setDefault } = useDefaultServer()
|
||||
const wslServers = useWslServers()
|
||||
const { previewStatus } = useServerPreview()
|
||||
const checkServerHealth = useCheckServerHealth()
|
||||
let disposed = false
|
||||
onCleanup(() => {
|
||||
@@ -199,7 +165,6 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
password: "",
|
||||
error: "",
|
||||
showForm: false,
|
||||
status: undefined as boolean | undefined,
|
||||
},
|
||||
addWsl: {
|
||||
showWizard: props.initialView === "add-wsl",
|
||||
@@ -212,7 +177,6 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
username: "",
|
||||
password: "",
|
||||
error: "",
|
||||
status: undefined as boolean | undefined,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -224,7 +188,6 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
password: "",
|
||||
error: "",
|
||||
showForm: false,
|
||||
status: undefined,
|
||||
})
|
||||
}
|
||||
const resetEdit = () => {
|
||||
@@ -235,7 +198,6 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
username: "",
|
||||
password: "",
|
||||
error: "",
|
||||
status: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -417,10 +379,6 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
return wslState()?.opencodeChecks[conn.distro] ?? null
|
||||
}
|
||||
|
||||
const displayVersion = (conn: ServerConnection.Any) => {
|
||||
return wslCheck(conn)?.version ?? undefined
|
||||
}
|
||||
|
||||
async function select(conn: ServerConnection.Any, persist?: boolean) {
|
||||
if (!isSelectable(conn)) return
|
||||
if (!persist && health(ServerConnection.key(conn))?.healthy === false) return
|
||||
@@ -470,9 +428,6 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
const handleAddChange = (value: string) => {
|
||||
if (addMutation.isPending) return
|
||||
setStore("addServer", { url: value, error: "" })
|
||||
void previewStatus(value, store.addServer.username, store.addServer.password, (next) =>
|
||||
setStore("addServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleAddNameChange = (value: string) => {
|
||||
@@ -483,25 +438,16 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
const handleAddUsernameChange = (value: string) => {
|
||||
if (addMutation.isPending) return
|
||||
setStore("addServer", { username: value, error: "" })
|
||||
void previewStatus(store.addServer.url, value, store.addServer.password, (next) =>
|
||||
setStore("addServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleAddPasswordChange = (value: string) => {
|
||||
if (addMutation.isPending) return
|
||||
setStore("addServer", { password: value, error: "" })
|
||||
void previewStatus(store.addServer.url, store.addServer.username, value, (next) =>
|
||||
setStore("addServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleEditChange = (value: string) => {
|
||||
if (editMutation.isPending) return
|
||||
setStore("editServer", { value, error: "" })
|
||||
void previewStatus(value, store.editServer.username, store.editServer.password, (next) =>
|
||||
setStore("editServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleEditNameChange = (value: string) => {
|
||||
@@ -512,17 +458,11 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
const handleEditUsernameChange = (value: string) => {
|
||||
if (editMutation.isPending) return
|
||||
setStore("editServer", { username: value, error: "" })
|
||||
void previewStatus(store.editServer.value, value, store.editServer.password, (next) =>
|
||||
setStore("editServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleEditPasswordChange = (value: string) => {
|
||||
if (editMutation.isPending) return
|
||||
setStore("editServer", { password: value, error: "" })
|
||||
void previewStatus(store.editServer.value, store.editServer.username, value, (next) =>
|
||||
setStore("editServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const mode = createMemo<"list" | "add-wsl" | "add" | "edit">(() => {
|
||||
@@ -554,7 +494,6 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
username: DEFAULT_USERNAME,
|
||||
password: "",
|
||||
error: "",
|
||||
status: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -568,7 +507,6 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
username: conn.http.username ?? "",
|
||||
password: conn.http.password ?? "",
|
||||
error: "",
|
||||
status: health(ServerConnection.key(conn))?.healthy,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -674,7 +612,6 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
placeholder={language.t("dialog.server.add.placeholder")}
|
||||
busy={formBusy()}
|
||||
error={isAddMode() ? store.addServer.error : store.editServer.error}
|
||||
status={isAddMode() ? store.addServer.status : store.editServer.status}
|
||||
onChange={isAddMode() ? handleAddChange : handleEditChange}
|
||||
onNameChange={isAddMode() ? handleAddNameChange : handleEditNameChange}
|
||||
onUsernameChange={isAddMode() ? handleAddUsernameChange : handleEditUsernameChange}
|
||||
@@ -735,7 +672,7 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
conn={i}
|
||||
dimmed={blocked()}
|
||||
status={health(key)}
|
||||
version={displayVersion(i)}
|
||||
version={wslCheck(i)?.version ?? undefined}
|
||||
class="flex items-center gap-3 min-w-0 flex-1"
|
||||
badge={
|
||||
<Show when={defaultKey() === ServerConnection.key(i)}>
|
||||
|
||||
@@ -51,14 +51,13 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
disposed = true
|
||||
})
|
||||
const busy = createMemo(() => !!current()?.job || store.adding)
|
||||
const selectedDistro = () => store.selectedDistro
|
||||
const selectedProbe = createMemo(() => {
|
||||
const distro = selectedDistro()
|
||||
const distro = store.selectedDistro
|
||||
if (!distro) return null
|
||||
return current()?.distroProbes[distro] ?? null
|
||||
})
|
||||
const selectedInstalled = createMemo(() => {
|
||||
const distro = selectedDistro()
|
||||
const distro = store.selectedDistro
|
||||
if (!distro) return null
|
||||
return (current()?.installed ?? []).find((item) => item.name === distro) ?? null
|
||||
})
|
||||
@@ -68,7 +67,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
const visibleOnlineDistros = createMemo(() => (current()?.online ?? []).filter((item) => !isHiddenDistro(item.name)))
|
||||
const defaultInstalledDistro = createMemo(() => visibleInstalledDistros().find((item) => item.isDefault) ?? null)
|
||||
const opencodeCheck = createMemo(() => {
|
||||
const distro = selectedDistro()
|
||||
const distro = store.selectedDistro
|
||||
if (!distro) return null
|
||||
return current()?.opencodeChecks[distro] ?? null
|
||||
})
|
||||
@@ -80,7 +79,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
})
|
||||
const distroUnavailableMessage = createMemo(() => {
|
||||
const probe = distroWarningProbe()
|
||||
const distro = selectedDistro()
|
||||
const distro = store.selectedDistro
|
||||
if (!probe || probe.canExecute || !distro) return null
|
||||
if (!selectedInstalled()) return `${distro} is not installed yet.`
|
||||
return `Open ${distro} once to finish setup.`
|
||||
@@ -91,10 +90,6 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
if (probe.hasBash && probe.hasCurl) return null
|
||||
return probe
|
||||
})
|
||||
const opencodeMismatchCheck = createMemo(() => {
|
||||
const check = opencodeCheck()
|
||||
return check?.matchesDesktop === false ? check : null
|
||||
})
|
||||
const existingServerDistros = createMemo(() => new Set((current()?.servers ?? []).map((item) => item.config.distro)))
|
||||
const addableInstalledDistros = createMemo(() => {
|
||||
return visibleInstalledDistros().filter((item) => !existingServerDistros().has(item.name))
|
||||
@@ -121,7 +116,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
const wslReady = createMemo(() => !!current()?.runtime?.available && !current()?.pendingRestart)
|
||||
const distroReady = createMemo(() => {
|
||||
const probe = selectedProbe()
|
||||
if (!probe || !selectedDistro()) return false
|
||||
if (!probe || !store.selectedDistro) return false
|
||||
if (selectedInstalled()?.version === 1) return false
|
||||
return probe.canExecute && probe.hasBash && probe.hasCurl
|
||||
})
|
||||
@@ -185,7 +180,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
if (!state.installed.length && !state.online.length) {
|
||||
return { key: "distros", run: () => refreshDistrosMutation.mutateAsync() }
|
||||
}
|
||||
const distro = selectedDistro()
|
||||
const distro = store.selectedDistro
|
||||
if (distro && !state.distroProbes[distro]) {
|
||||
return { key: `probe-distro:${distro}`, run: () => probeDistroMutation.mutateAsync(distro) }
|
||||
}
|
||||
@@ -220,7 +215,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
const state = current()
|
||||
const distro = defaultInstalledDistro()
|
||||
if (!state || !distro || busy()) return
|
||||
if (selectedDistro()) return
|
||||
if (store.selectedDistro) return
|
||||
if (existingServerDistros().has(distro.name)) return
|
||||
setStore("selectedDistro", distro.name)
|
||||
})
|
||||
@@ -246,7 +241,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
const distroMessage = createMemo(() => {
|
||||
const state = current()
|
||||
if (!state) return "Checking distros..."
|
||||
const distro = selectedDistro()
|
||||
const distro = store.selectedDistro
|
||||
if (state.job?.kind === "install-distro") return `Installing ${state.job.distro}...`
|
||||
if (state.job?.kind === "probe-distro") return `Checking ${state.job.distro}...`
|
||||
if (state.job?.kind === "distros") return "Listing distros..."
|
||||
@@ -259,7 +254,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
const opencodeMessage = createMemo(() => {
|
||||
const state = current()
|
||||
if (!state) return "Checking OpenCode..."
|
||||
const distro = selectedDistro()
|
||||
const distro = store.selectedDistro
|
||||
if (state.job?.kind === "probe-opencode" || state.job?.kind === "install-opencode") {
|
||||
return distro ? `Checking OpenCode in ${distro}...` : "Checking OpenCode..."
|
||||
}
|
||||
@@ -292,7 +287,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
}
|
||||
|
||||
const runSelectedDistro = (action: (distro: string) => Promise<unknown>) => {
|
||||
const distro = selectedDistro()
|
||||
const distro = store.selectedDistro
|
||||
if (!distro) return
|
||||
void run(() => action(distro))
|
||||
}
|
||||
@@ -303,7 +298,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
}
|
||||
|
||||
const finish = async () => {
|
||||
const distro = selectedDistro()
|
||||
const distro = store.selectedDistro
|
||||
if (!distro) return
|
||||
setStore("adding", true)
|
||||
try {
|
||||
@@ -402,7 +397,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
<div class="rounded-md bg-surface-base p-4 flex flex-col gap-3">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="text-14-medium text-text-strong">Choose a distro</div>
|
||||
<Show when={selectedDistro()}>
|
||||
<Show when={store.selectedDistro}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
@@ -433,7 +428,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-md border border-border-weak-base px-3 py-2 text-left transition-colors"
|
||||
classList={{ "bg-surface-raised-base": selectedDistro() === item.name }}
|
||||
classList={{ "bg-surface-raised-base": store.selectedDistro === item.name }}
|
||||
onClick={() => selectDistro(item.name)}
|
||||
>
|
||||
<div class="text-13-medium text-text-strong">{item.name}</div>
|
||||
@@ -543,7 +538,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="large"
|
||||
disabled={busy() || !selectedDistro()}
|
||||
disabled={busy() || !store.selectedDistro}
|
||||
onClick={() => runSelectedDistro((distro) => probeDistroMutation.mutateAsync(distro))}
|
||||
>
|
||||
Refresh
|
||||
@@ -554,7 +549,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="large"
|
||||
disabled={busy() || !selectedDistro() || !distroReady()}
|
||||
disabled={busy() || !store.selectedDistro || !distroReady()}
|
||||
onClick={() => setStore("step", "opencode")}
|
||||
>
|
||||
Next
|
||||
@@ -568,7 +563,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="text-14-medium text-text-strong">OpenCode</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Show when={selectedDistro()}>
|
||||
<Show when={store.selectedDistro}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="large"
|
||||
@@ -591,7 +586,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-12-regular text-text-weak whitespace-pre-wrap break-words">{opencodeMessage()}</div>
|
||||
<Show when={opencodeMismatchCheck()}>
|
||||
<Show when={opencodeCheck()?.matchesDesktop === false ? opencodeCheck() : null}>
|
||||
{(check) => (
|
||||
<div class="rounded-md border border-border-weak-base px-3 py-3 flex flex-col gap-1">
|
||||
<div class="text-12-regular text-text-weak">Path: {check().resolvedPath ?? "not found"}</div>
|
||||
@@ -652,7 +647,7 @@ export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show when={activeStep() === "opencode" && allReady() && selectedDistro()}>
|
||||
<Show when={activeStep() === "opencode" && allReady() && store.selectedDistro}>
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<Button variant="ghost" size="large" disabled={store.adding} onClick={() => dialog.close()}>
|
||||
Cancel
|
||||
|
||||
@@ -176,7 +176,6 @@ function setInitStep(step: InitStep) {
|
||||
|
||||
async function initialize() {
|
||||
const needsMigration = !sqliteFileExists()
|
||||
const sqliteDone = needsMigration ? defer<void>() : undefined
|
||||
let overlay: BrowserWindow | null = null
|
||||
|
||||
const port = await allocatePort()
|
||||
@@ -203,7 +202,6 @@ async function initialize() {
|
||||
setInitStep({ phase: "sqlite_waiting" })
|
||||
if (overlay) sendSqliteMigrationProgress(overlay, progress)
|
||||
if (mainWindow) sendSqliteMigrationProgress(mainWindow, progress)
|
||||
if (progress.type === "Done") sqliteDone?.resolve()
|
||||
})
|
||||
|
||||
if (needsMigration) {
|
||||
@@ -215,12 +213,6 @@ async function initialize() {
|
||||
},
|
||||
})
|
||||
initEmitter.emit("sqlite", { type: "Done" })
|
||||
|
||||
sqliteDone?.resolve()
|
||||
}
|
||||
|
||||
if (needsMigration) {
|
||||
await sqliteDone?.promise
|
||||
}
|
||||
|
||||
logger.log("spawning windows sidecar", { url })
|
||||
|
||||
@@ -14,7 +14,6 @@ import type {
|
||||
WslTranscriptLine,
|
||||
} from "../preload/types"
|
||||
import { LEGACY_LOCAL_SERVER_KEY, WSL_SERVERS_KEY } from "./constants"
|
||||
import { spawnWslSidecar } from "./server"
|
||||
import { getStore } from "./store"
|
||||
import type { WslCommandLine } from "./wsl"
|
||||
import {
|
||||
@@ -412,10 +411,9 @@ export function createWslServersController(appVersion: string, spawnSidecar: Spa
|
||||
|
||||
stopAll() {
|
||||
for (const item of state.servers) invalidateStartAttempt(item.config.id)
|
||||
for (const [id] of sidecars) {
|
||||
const existing = sidecars.get(id)
|
||||
for (const existing of sidecars.values()) {
|
||||
try {
|
||||
existing?.listener.stop()
|
||||
existing.listener.stop()
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@@ -436,11 +436,7 @@ render(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const [defaultServer] = createResource(() =>
|
||||
platform.getDefaultServer?.().then((url) => {
|
||||
if (url) return ServerConnection.Key.make(url)
|
||||
}),
|
||||
)
|
||||
const [defaultServer] = createResource(() => platform.getDefaultServer?.())
|
||||
const [locale] = createResource(loadLocale)
|
||||
const [storedServers] = createResource(async () => {
|
||||
const raw = await platform.storage?.("opencode.global.dat").getItem("server")
|
||||
|
||||
@@ -432,11 +432,7 @@ render(() => {
|
||||
// Fetch sidecar credentials from Rust (available immediately, before health check)
|
||||
const [sidecar] = createResource(() => commands.awaitInitialization(new Channel<InitStep>() as any))
|
||||
|
||||
const [defaultServer] = createResource(() =>
|
||||
platform.getDefaultServer?.().then((url) => {
|
||||
if (url) return ServerConnection.key({ type: "http", http: { url } })
|
||||
}),
|
||||
)
|
||||
const [defaultServer] = createResource(() => platform.getDefaultServer?.())
|
||||
const [locale] = createResource(loadLocale)
|
||||
|
||||
// Build the sidecar server connection once credentials arrive
|
||||
|
||||
@@ -29,33 +29,33 @@ const Context = createContext<ReturnType<typeof init>>()
|
||||
|
||||
function init() {
|
||||
const [active, setActive] = createSignal<Active | undefined>()
|
||||
const timer = { current: undefined as ReturnType<typeof setTimeout> | undefined }
|
||||
const lock = { value: false }
|
||||
let timer: ReturnType<typeof setTimeout> | undefined
|
||||
let locked = false
|
||||
|
||||
onCleanup(() => {
|
||||
if (timer.current === undefined) return
|
||||
clearTimeout(timer.current)
|
||||
timer.current = undefined
|
||||
if (timer === undefined) return
|
||||
clearTimeout(timer)
|
||||
timer = undefined
|
||||
})
|
||||
|
||||
const close = () => {
|
||||
const current = active()
|
||||
if (!current || lock.value) return
|
||||
lock.value = true
|
||||
if (!current || locked) return
|
||||
locked = true
|
||||
current.onClose?.()
|
||||
current.setClosing(true)
|
||||
|
||||
const id = current.id
|
||||
if (timer.current !== undefined) {
|
||||
clearTimeout(timer.current)
|
||||
timer.current = undefined
|
||||
if (timer !== undefined) {
|
||||
clearTimeout(timer)
|
||||
timer = undefined
|
||||
}
|
||||
|
||||
timer.current = setTimeout(() => {
|
||||
timer.current = undefined
|
||||
timer = setTimeout(() => {
|
||||
timer = undefined
|
||||
current.dispose()
|
||||
if (active()?.id === id) setActive(undefined)
|
||||
lock.value = false
|
||||
locked = false
|
||||
}, 100)
|
||||
}
|
||||
|
||||
@@ -80,11 +80,11 @@ function init() {
|
||||
setActive(undefined)
|
||||
}
|
||||
|
||||
if (timer.current !== undefined) {
|
||||
clearTimeout(timer.current)
|
||||
timer.current = undefined
|
||||
if (timer !== undefined) {
|
||||
clearTimeout(timer)
|
||||
timer = undefined
|
||||
}
|
||||
lock.value = false
|
||||
locked = false
|
||||
|
||||
const id = Math.random().toString(36).slice(2)
|
||||
let dispose: (() => void) | undefined
|
||||
|
||||
Reference in New Issue
Block a user