fix(app): preserve provider dialog backdrop (#35370)
Co-authored-by: Jay <53023+jayair@users.noreply.github.com> Co-authored-by: Brendan Allan <git@brendonovich.dev>
This commit is contained in:
parent
e12cb7fb6b
commit
14df88eab5
7 changed files with 107 additions and 95 deletions
|
|
@ -17,19 +17,17 @@ import { useServerSync } from "@/context/server-sync"
|
|||
import { useLanguage } from "@/context/language"
|
||||
import { useProviders } from "@/hooks/use-providers"
|
||||
|
||||
export function DialogConnectProvider(props: { provider: string; directory?: Accessor<string | undefined> }) {
|
||||
export function DialogConnectProvider(props: {
|
||||
provider: string
|
||||
directory?: Accessor<string | undefined>
|
||||
onBack: () => void
|
||||
}) {
|
||||
const dialog = useDialog()
|
||||
const serverSync = useServerSync()
|
||||
const serverSDK = useServerSDK()
|
||||
const language = useLanguage()
|
||||
const providers = useProviders(props.directory)
|
||||
|
||||
const all = () => {
|
||||
void import("./dialog-select-provider").then((x) => {
|
||||
dialog.show(() => <x.DialogSelectProvider directory={props.directory} />)
|
||||
})
|
||||
}
|
||||
|
||||
const alive = { value: true }
|
||||
const timer = { current: undefined as ReturnType<typeof setTimeout> | undefined }
|
||||
|
||||
|
|
@ -364,19 +362,11 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
|||
}
|
||||
|
||||
function goBack() {
|
||||
if (methods().length === 1) {
|
||||
all()
|
||||
return
|
||||
}
|
||||
if (store.authorization) {
|
||||
if (methods().length > 1 && store.methodIndex !== undefined) {
|
||||
dispatch({ type: "method.reset" })
|
||||
return
|
||||
}
|
||||
if (store.methodIndex !== undefined) {
|
||||
dispatch({ type: "method.reset" })
|
||||
return
|
||||
}
|
||||
all()
|
||||
props.onBack()
|
||||
}
|
||||
|
||||
function MethodSelection() {
|
||||
|
|
@ -600,6 +590,7 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
|||
|
||||
return (
|
||||
<Dialog
|
||||
class="h-full"
|
||||
title={
|
||||
<IconButton
|
||||
tabIndex={-1}
|
||||
|
|
@ -609,6 +600,7 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
|||
aria-label={language.t("common.goBack")}
|
||||
/>
|
||||
}
|
||||
transition
|
||||
>
|
||||
<div class="flex flex-col gap-6 px-2.5 pb-3">
|
||||
<div class="px-2.5 flex gap-4 items-center">
|
||||
|
|
|
|||
|
|
@ -6,18 +6,16 @@ import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
|||
import { useMutation } from "@tanstack/solid-query"
|
||||
import { TextField } from "@opencode-ai/ui/text-field"
|
||||
import { showToast } from "@/utils/toast"
|
||||
import { type Accessor, batch, For } from "solid-js"
|
||||
import { batch, For } from "solid-js"
|
||||
import { createStore, produce } from "solid-js/store"
|
||||
import { Link } from "@/components/link"
|
||||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { type FormState, headerRow, modelRow, validateCustomProvider } from "./dialog-custom-provider-form"
|
||||
import { DialogSelectProvider } from "./dialog-select-provider"
|
||||
|
||||
type Props = {
|
||||
back?: "providers" | "close"
|
||||
directory?: Accessor<string | undefined>
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function DialogCustomProvider(props: Props) {
|
||||
|
|
@ -36,14 +34,6 @@ export function DialogCustomProvider(props: Props) {
|
|||
err: {},
|
||||
})
|
||||
|
||||
const goBack = () => {
|
||||
if (props.back === "close") {
|
||||
dialog.close()
|
||||
return
|
||||
}
|
||||
dialog.show(() => <DialogSelectProvider directory={props.directory} />)
|
||||
}
|
||||
|
||||
const addModel = () => {
|
||||
setForm(
|
||||
"models",
|
||||
|
|
@ -164,12 +154,13 @@ export function DialogCustomProvider(props: Props) {
|
|||
|
||||
return (
|
||||
<Dialog
|
||||
class="h-full"
|
||||
title={
|
||||
<IconButton
|
||||
tabIndex={-1}
|
||||
icon="arrow-left"
|
||||
variant="ghost"
|
||||
onClick={goBack}
|
||||
onClick={props.onBack}
|
||||
aria-label={language.t("common.goBack")}
|
||||
/>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export const DialogSelectModelUnpaid: Component<{ model?: ModelState }> = (props
|
|||
|
||||
const connect = (provider: string) => {
|
||||
void import("./dialog-connect-provider").then((x) => {
|
||||
dialog.show(() => <x.DialogConnectProvider provider={provider} directory={directory} />)
|
||||
dialog.show(() => <x.DialogConnectProvider provider={provider} directory={directory} onBack={all} />)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { type Accessor, Component, Show } from "solid-js"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import { type Accessor, Component, Match, Show, Switch } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { popularProviders, useProviders } from "@/hooks/use-providers"
|
||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||
import { List } from "@opencode-ai/ui/list"
|
||||
|
|
@ -12,9 +12,13 @@ import { DialogCustomProvider } from "./dialog-custom-provider"
|
|||
const CUSTOM_ID = "_custom"
|
||||
|
||||
export const DialogSelectProvider: Component<{ directory?: Accessor<string | undefined> }> = (props) => {
|
||||
const dialog = useDialog()
|
||||
const providers = useProviders(props.directory)
|
||||
const language = useLanguage()
|
||||
const [store, setStore] = createStore({ selected: undefined as string | undefined })
|
||||
|
||||
function showPicker() {
|
||||
setStore("selected", undefined)
|
||||
}
|
||||
|
||||
const popularGroup = () => language.t("dialog.provider.group.popular")
|
||||
const otherGroup = () => language.t("dialog.provider.group.other")
|
||||
|
|
@ -27,61 +31,67 @@ export const DialogSelectProvider: Component<{ directory?: Accessor<string | und
|
|||
}
|
||||
|
||||
return (
|
||||
<Dialog title={language.t("command.provider.connect")} transition>
|
||||
<List
|
||||
class="px-3"
|
||||
search={{ placeholder: language.t("dialog.provider.search.placeholder"), autofocus: true }}
|
||||
emptyMessage={language.t("dialog.provider.empty")}
|
||||
activeIcon="plus-small"
|
||||
key={(x) => x?.id}
|
||||
items={() => {
|
||||
language.locale()
|
||||
return [{ id: CUSTOM_ID, name: customLabel() }, ...providers.all().values()]
|
||||
}}
|
||||
filterKeys={["id", "name"]}
|
||||
groupBy={(x) => (popularProviders.includes(x.id) ? popularGroup() : otherGroup())}
|
||||
sortBy={(a, b) => {
|
||||
if (a.id === CUSTOM_ID) return -1
|
||||
if (b.id === CUSTOM_ID) return 1
|
||||
if (popularProviders.includes(a.id) && popularProviders.includes(b.id))
|
||||
return popularProviders.indexOf(a.id) - popularProviders.indexOf(b.id)
|
||||
return a.name.localeCompare(b.name)
|
||||
}}
|
||||
sortGroupsBy={(a, b) => {
|
||||
const popular = popularGroup()
|
||||
if (a.category === popular && b.category !== popular) return -1
|
||||
if (b.category === popular && a.category !== popular) return 1
|
||||
return 0
|
||||
}}
|
||||
onSelect={(x) => {
|
||||
if (!x) return
|
||||
if (x.id === CUSTOM_ID) {
|
||||
dialog.show(() => <DialogCustomProvider back="providers" directory={props.directory} />)
|
||||
return
|
||||
}
|
||||
dialog.show(() => <DialogConnectProvider provider={x.id} directory={props.directory} />)
|
||||
}}
|
||||
>
|
||||
{(i) => (
|
||||
<div class="px-1.25 w-full flex items-center gap-x-3">
|
||||
<ProviderIcon data-slot="list-item-extra-icon" id={i.id} />
|
||||
<span>{i.name}</span>
|
||||
<Show when={i.id === "opencode"}>
|
||||
<div class="text-14-regular text-text-weak">{language.t("dialog.provider.opencode.tagline")}</div>
|
||||
</Show>
|
||||
<Show when={i.id === CUSTOM_ID}>
|
||||
<Tag>{language.t("settings.providers.tag.custom")}</Tag>
|
||||
</Show>
|
||||
<Show when={i.id === "opencode"}>
|
||||
<Tag>{language.t("dialog.provider.tag.recommended")}</Tag>
|
||||
</Show>
|
||||
<Show when={note(i.id)}>{(value) => <div class="text-14-regular text-text-weak">{value()}</div>}</Show>
|
||||
<Show when={i.id === "opencode-go"}>
|
||||
<Tag>{language.t("dialog.provider.tag.recommended")}</Tag>
|
||||
</Show>
|
||||
</div>
|
||||
)}
|
||||
</List>
|
||||
</Dialog>
|
||||
<Switch>
|
||||
<Match when={store.selected === CUSTOM_ID}>
|
||||
<DialogCustomProvider onBack={showPicker} />
|
||||
</Match>
|
||||
<Match when={store.selected && store.selected !== CUSTOM_ID ? store.selected : undefined}>
|
||||
{(provider) => <DialogConnectProvider provider={provider()} directory={props.directory} onBack={showPicker} />}
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<Dialog class="h-full" transition title={language.t("command.provider.connect")}>
|
||||
<List
|
||||
class="px-3"
|
||||
search={{ placeholder: language.t("dialog.provider.search.placeholder"), autofocus: true }}
|
||||
emptyMessage={language.t("dialog.provider.empty")}
|
||||
activeIcon="plus-small"
|
||||
key={(x) => x?.id}
|
||||
items={() => {
|
||||
language.locale()
|
||||
return [{ id: CUSTOM_ID, name: customLabel() }, ...providers.all().values()]
|
||||
}}
|
||||
filterKeys={["id", "name"]}
|
||||
groupBy={(x) => (popularProviders.includes(x.id) ? popularGroup() : otherGroup())}
|
||||
sortBy={(a, b) => {
|
||||
if (a.id === CUSTOM_ID) return -1
|
||||
if (b.id === CUSTOM_ID) return 1
|
||||
if (popularProviders.includes(a.id) && popularProviders.includes(b.id))
|
||||
return popularProviders.indexOf(a.id) - popularProviders.indexOf(b.id)
|
||||
return a.name.localeCompare(b.name)
|
||||
}}
|
||||
sortGroupsBy={(a, b) => {
|
||||
const popular = popularGroup()
|
||||
if (a.category === popular && b.category !== popular) return -1
|
||||
if (b.category === popular && a.category !== popular) return 1
|
||||
return 0
|
||||
}}
|
||||
onSelect={(x) => {
|
||||
if (!x) return
|
||||
setStore("selected", x.id)
|
||||
}}
|
||||
>
|
||||
{(i) => (
|
||||
<div class="px-1.25 w-full flex items-center gap-x-3">
|
||||
<ProviderIcon data-slot="list-item-extra-icon" id={i.id} />
|
||||
<span>{i.name}</span>
|
||||
<Show when={i.id === "opencode"}>
|
||||
<div class="text-14-regular text-text-weak">{language.t("dialog.provider.opencode.tagline")}</div>
|
||||
</Show>
|
||||
<Show when={i.id === CUSTOM_ID}>
|
||||
<Tag>{language.t("settings.providers.tag.custom")}</Tag>
|
||||
</Show>
|
||||
<Show when={i.id === "opencode"}>
|
||||
<Tag>{language.t("dialog.provider.tag.recommended")}</Tag>
|
||||
</Show>
|
||||
<Show when={note(i.id)}>{(value) => <div class="text-14-regular text-text-weak">{value()}</div>}</Show>
|
||||
<Show when={i.id === "opencode-go"}>
|
||||
<Tag>{language.t("dialog.provider.tag.recommended")}</Tag>
|
||||
</Show>
|
||||
</div>
|
||||
)}
|
||||
</List>
|
||||
</Dialog>
|
||||
</Match>
|
||||
</Switch>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -211,7 +211,12 @@ const SettingsProvidersContent: Component = () => {
|
|||
variant="secondary"
|
||||
icon="plus-small"
|
||||
onClick={() => {
|
||||
dialog.show(() => <DialogConnectProvider provider={item.id} />)
|
||||
dialog.show(() => (
|
||||
<DialogConnectProvider
|
||||
provider={item.id}
|
||||
onBack={() => dialog.show(() => <DialogSelectProvider />)}
|
||||
/>
|
||||
))
|
||||
}}
|
||||
>
|
||||
{language.t("common.connect")}
|
||||
|
|
@ -239,7 +244,7 @@ const SettingsProvidersContent: Component = () => {
|
|||
variant="secondary"
|
||||
icon="plus-small"
|
||||
onClick={() => {
|
||||
dialog.show(() => <DialogCustomProvider back="close" />)
|
||||
dialog.show(() => <DialogCustomProvider onBack={dialog.close} />)
|
||||
}}
|
||||
>
|
||||
{language.t("common.connect")}
|
||||
|
|
|
|||
|
|
@ -211,7 +211,12 @@ export const SettingsProvidersV2: Component = () => {
|
|||
variant="neutral"
|
||||
icon="plus"
|
||||
onClick={() => {
|
||||
dialog.show(() => <DialogConnectProvider provider={item.id} />)
|
||||
dialog.show(() => (
|
||||
<DialogConnectProvider
|
||||
provider={item.id}
|
||||
onBack={() => dialog.show(() => <DialogSelectProvider />)}
|
||||
/>
|
||||
))
|
||||
}}
|
||||
>
|
||||
{language.t("common.connect")}
|
||||
|
|
@ -241,7 +246,7 @@ export const SettingsProvidersV2: Component = () => {
|
|||
variant="neutral"
|
||||
icon="plus"
|
||||
onClick={() => {
|
||||
dialog.show(() => <DialogCustomProvider back="close" />)
|
||||
dialog.show(() => <DialogCustomProvider onBack={dialog.close} />)
|
||||
}}
|
||||
>
|
||||
{language.t("common.connect")}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,16 @@ export function useUsageExceededDialogs() {
|
|||
if (dontShowAgain) setGoUpsellState(keys.dontShow, Date.now())
|
||||
else {
|
||||
void import("../../components/dialog-connect-provider").then((x) =>
|
||||
dialog.show(() => <x.DialogConnectProvider provider="opencode-go" />),
|
||||
dialog.show(() => (
|
||||
<x.DialogConnectProvider
|
||||
provider="opencode-go"
|
||||
onBack={() => {
|
||||
void import("../../components/dialog-select-provider").then((provider) => {
|
||||
dialog.show(() => <provider.DialogSelectProvider />)
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)),
|
||||
)
|
||||
}
|
||||
}}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue