diff --git a/packages/app/src/components/dialog-connect-provider.tsx b/packages/app/src/components/dialog-connect-provider.tsx index a0ccc161f25f..472f3bec5270 100644 --- a/packages/app/src/components/dialog-connect-provider.tsx +++ b/packages/app/src/components/dialog-connect-provider.tsx @@ -17,7 +17,7 @@ import { useServerSync } from "@/context/server-sync" import { useLanguage } from "@/context/language" import { useProviders } from "@/hooks/use-providers" -export function DialogConnectProvider(props: { provider: string }) { +export function DialogConnectProvider(props: { provider: string; onComplete?: () => void }) { const dialog = useDialog() const serverSync = useServerSync() const serverSDK = useServerSDK() @@ -332,6 +332,7 @@ export function DialogConnectProvider(props: { provider: string }) { async function complete() { await serverSDK.client.global.dispose() dialog.close() + props.onComplete?.() showToast({ variant: "success", icon: "circle-check", diff --git a/packages/app/src/components/dialog-custom-provider-form.ts b/packages/app/src/components/dialog-custom-provider-form.ts index e26dcb09710d..1a9697d0a0f2 100644 --- a/packages/app/src/components/dialog-custom-provider-form.ts +++ b/packages/app/src/components/dialog-custom-provider-form.ts @@ -46,6 +46,7 @@ type ValidateArgs = { t: Translator disabledProviders: string[] existingProviderIDs: Set + editingProviderID?: string } export function validateCustomProvider(input: ValidateArgs) { @@ -73,7 +74,7 @@ export function validateCustomProvider(input: ValidateArgs) { const disabled = input.disabledProviders.includes(providerID) const existsError = idError ? undefined - : input.existingProviderIDs.has(providerID) && !disabled + : input.existingProviderIDs.has(providerID) && !disabled && providerID !== input.editingProviderID ? input.t("provider.custom.error.providerID.exists") : undefined diff --git a/packages/app/src/components/dialog-custom-provider.tsx b/packages/app/src/components/dialog-custom-provider.tsx index ad30236b0410..65c9f362efdf 100644 --- a/packages/app/src/components/dialog-custom-provider.tsx +++ b/packages/app/src/components/dialog-custom-provider.tsx @@ -17,6 +17,8 @@ import { DialogSelectProvider } from "./dialog-select-provider" type Props = { back?: "providers" | "close" + initialConfig?: Partial + originalProviderID?: string } export function DialogCustomProvider(props: Props) { @@ -26,12 +28,12 @@ export function DialogCustomProvider(props: Props) { const language = useLanguage() const [form, setForm] = createStore({ - providerID: "", - name: "", - baseURL: "", - apiKey: "", - models: [modelRow()], - headers: [headerRow()], + providerID: props.initialConfig?.providerID ?? "", + name: props.initialConfig?.name ?? "", + baseURL: props.initialConfig?.baseURL ?? "", + apiKey: props.initialConfig?.apiKey ?? "", + models: props.initialConfig?.models ?? [modelRow()], + headers: props.initialConfig?.headers ?? [headerRow()], err: {}, }) @@ -107,6 +109,7 @@ export function DialogCustomProvider(props: Props) { t: language.t, disabledProviders: serverSync.data.config.disabled_providers ?? [], existingProviderIDs: new Set(serverSync.data.provider.all.keys()), + editingProviderID: props.originalProviderID, }) batch(() => { setForm("err", output.err) @@ -199,6 +202,7 @@ export function DialogCustomProvider(props: Props) { onChange={(v) => setField("providerID", v)} validationState={form.err.providerID ? "invalid" : undefined} error={form.err.providerID} + disabled={!!props.originalProviderID} /> { if (editMutation.isPending) return setStore("editServer", { name: value, error: "" }) + void previewStatus(store.editServer.value, value, store.editServer.password, (next) => + setStore("editServer", { status: next }), + ) } const handleEditUsernameChange = (value: string) => { diff --git a/packages/app/src/components/settings-providers.tsx b/packages/app/src/components/settings-providers.tsx index ffd85f97dce1..bb593408104c 100644 --- a/packages/app/src/components/settings-providers.tsx +++ b/packages/app/src/components/settings-providers.tsx @@ -11,6 +11,7 @@ import { useServerSync } from "@/context/server-sync" import { DialogConnectProvider } from "./dialog-connect-provider" import { DialogSelectProvider } from "./dialog-select-provider" import { DialogCustomProvider } from "./dialog-custom-provider" +import { headerRow, modelRow, type FormState } from "./dialog-custom-provider-form" import { SettingsList } from "./settings-list" type ProviderSource = "env" | "api" | "config" | "custom" @@ -81,6 +82,28 @@ export const SettingsProviders: Component = () => { return true } + const customProviderFormState = (providerID: string): Partial => { + const config = serverSync.data.config.provider?.[providerID] + if (!config) return { providerID } + const rawHeaders = config.options?.headers as Record | undefined + return { + providerID, + name: config.name ?? "", + baseURL: config.options?.baseURL ?? "", + apiKey: config.env?.[0] ? `{env:${config.env[0]}}` : "", + models: Object.entries(config.models ?? {}).map(([id, m]) => ({ + ...modelRow(), + id, + name: (m as { name?: string }).name ?? "", + })), + headers: Object.entries(rawHeaders ?? {}).map(([key, value]) => ({ + ...headerRow(), + key, + value, + })), + } + } + const disableProvider = async (providerID: string, name: string) => { const before = serverSync.data.config.disabled_providers ?? [] const next = before.includes(providerID) ? before : [...before, providerID] @@ -162,9 +185,31 @@ export const SettingsProviders: Component = () => { } > - +
+ + +
)}