import { useDialog } from "@opencode-ai/ui/context/dialog" import { showToast } from "@opencode-ai/ui/toast" import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2" import { Dialog, DialogBody, DialogFooter, DialogHeader, DialogTitle } from "@opencode-ai/ui/v2/dialog-v2" import { DividerV2 } from "@opencode-ai/ui/v2/divider-v2" import { LoaderV2 } from "@opencode-ai/ui/v2/loader-v2" import { RadioGroupV2, RadioItemV2 } from "@opencode-ai/ui/v2/radio-v2" import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2" import { createMemo, For, Show } from "solid-js" import { createStore } from "solid-js/store" import { useLanguage } from "@/context/language" import { usePlatform } from "@/context/platform" import { useWslAddServerProbes } from "./add-server-probes" import { useWslServers } from "./context" import { addServerViewModel, type AddServerText } from "./settings-model" import "./dialog-add-wsl-server.css" function isWslRuntimeMissing(error: string | null | undefined) { if (!error) return true return /WSL is not installed|not been installed|wsl(?:\.exe)? --install/i.test(error) } function translate(language: ReturnType, value: AddServerText) { if (value.params) return language.t(value.key, value.params) return language.t(value.key) } interface DialogWslServerProps { onAdded?: (distro: string) => void | Promise } export function DialogAddWslServer(props: DialogWslServerProps = {}) { const language = useLanguage() const controller = useWslAddServerController(props) const model = controller.model const primaryButton = () => model().primaryButton const primaryButtonStyle = () => { const width = primaryButton().width if (!width) return undefined return { width } } return ( {controller.loadError()}} >
} > } >
} > {controller.view() === "main" ? language.t("wsl.server.add") : language.t("wsl.onboarding.installDistro")} controller.setCatalogSearch(event.currentTarget.value)} />
{(item) => ( {item.label}} /> )}
{language.t("common.cancel")} } >
{language.t("wsl.onboarding.installedDistros")} {language.t("wsl.onboarding.checkAgain")}
0} fallback={
{model().visibleInstalledDistros.length ? language.t("wsl.onboarding.allDistrosAdded") : language.t("wsl.onboarding.noDistros")}
} >
{(item) => { const status = () => model().distroStatuses[item.name] ?? null return ( {item.name}} description={ {(value) => ( {translate(language, value().label)} )} } /> ) }}
0}>
{language.t("common.cancel")}
) } function useWslAddServerController(props: DialogWslServerProps) { const language = useLanguage() const platform = usePlatform() const dialog = useDialog() const wslServers = useWslServers() const api = platform.wslServers! const [store, setStore] = createStore({ view: "main" as "main" | "catalog", selectedDistro: null as string | null, catalogSearch: "", catalogTarget: null as string | null, adding: false, }) const current = () => wslServers.data const viewModel = (probingAddable: boolean) => addServerViewModel({ state: current(), view: store.view, selectedDistro: store.selectedDistro, catalogSearch: store.catalogSearch, catalogTarget: store.catalogTarget, adding: store.adding, probingAddable, }) const baseModel = createMemo(() => viewModel(false)) const probes = useWslAddServerProbes({ state: current, api, view: () => store.view, adding: () => store.adding, busy: () => baseModel().busy, selectedDistro: () => baseModel().selectedDistro, addableInstalledDistros: () => baseModel().addableInstalledDistros, onError: (error) => requestError(language, error), }) const model = createMemo(() => viewModel(probes.probingAddable())) const openCatalog = () => { const first = model().installableDistros[0] setStore({ view: "catalog", catalogSearch: "", catalogTarget: first?.name ?? null, }) } const run = async (action: () => Promise) => { try { await action() } catch (err) { requestError(language, err) } } const refreshDistros = () => { void run(async () => { probes.resetProbeFailure() await api.refreshDistros() }) } const installDistro = (name: string) => { void run(async () => { probes.resetProbeFailure() await api.installDistro(name) setStore("view", "main") }) } const installCatalogDistro = () => { if (model().installingCatalogDistro) return const name = model().catalogTarget if (!name) return installDistro(name) } const closeCatalog = () => { probes.resetProbeFailure() setStore({ view: "main", catalogSearch: "", catalogTarget: null }) } const runPrimary = async () => { const button = model().primaryButton if (button.loading) return const distro = model().selectedDistro const action = button.action if (!distro || !action) return if (action === "install-opencode") { await run(() => api.installOpencode(distro)) return } setStore("adding", true) try { await api.addServer(distro) if (props.onAdded) { await props.onAdded(distro) } else { dialog.close() } } catch (err) { requestError(language, err) } finally { setStore("adding", false) } } const loadError = () => { const error = wslServers.error if (!error) return language.t("wsl.onboarding.loadFailed") return error instanceof Error ? error.message : String(error) } return { wslServers, model, loadError, runtimeError: () => current()?.runtime?.error ?? null, view: () => store.view, catalogSearch: () => store.catalogSearch, adding: () => store.adding, setCatalogSearch: (value: string) => setStore("catalogSearch", value), setCatalogTarget: (value: string) => setStore("catalogTarget", value), setSelectedDistro: (value: string) => setStore("selectedDistro", value), openCatalog, closeCatalog, refreshDistros, installCatalogDistro, installWsl: () => void run(() => api.installWsl()), runPrimary: () => void runPrimary(), close: () => dialog.close(), } } function DialogWslSetup(props: { state: string error: string | null installable: boolean busy: boolean onInstall: () => void }) { const language = useLanguage() const dialog = useDialog() const title = () => props.state === "pendingRestart" ? language.t("wsl.onboarding.restartRequired") : props.installable ? language.t("wsl.onboarding.wslNotInstalled.title") : language.t("wsl.onboarding.wslUnavailable.title") const description = () => { if (props.state === "pendingRestart") return language.t("wsl.onboarding.windowsRestartRequired") if (!props.installable) return language.t("wsl.onboarding.wslUnavailable.description") return language.t("wsl.onboarding.wslNotInstalled.description") } return (

{title()}

{description()}

{props.error}

{language.t("wsl.onboarding.installWsl")} dialog.close()}> {language.t("common.close")}
) } function requestError(language: ReturnType, err: unknown) { console.error("WSL servers request failed", err instanceof Error ? (err.stack ?? err.message) : String(err)) showToast({ variant: "error", title: language.t("common.requestFailed"), description: err instanceof Error ? err.message : String(err), }) }