diff --git a/packages/client/ui-models/src/client/ModelsSection.tsx b/packages/client/ui-models/src/client/ModelsSection.tsx
index b5a09021ff..b5a2801bf5 100644
--- a/packages/client/ui-models/src/client/ModelsSection.tsx
+++ b/packages/client/ui-models/src/client/ModelsSection.tsx
@@ -18,7 +18,7 @@ import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-web-react'
import { CustomProviderCard } from './CustomProviderCard.tsx'
import { deriveKeyRef, messageOf, protocolChoices } from './store.ts'
import type { ModelsSettingsState, ModelsSettingsStore, ProviderRow } from './store.ts'
-import { ProviderEditor } from './ProviderEditor.tsx'
+import { ProviderEditor, type ProviderEditorProps } from './ProviderEditor.tsx'
import type { en } from './locales.ts'
import styles from './ModelsSection.module.css'
@@ -56,6 +56,26 @@ interface EditorTarget extends ProviderIdentity {
credentialRef?: string
}
+/** Values that vary around the shared provider-editor rendering. */
+interface ProviderEditorRenderProps extends Pick<
+ ProviderEditorProps,
+ 'namespace' | 'api' | 't' | 'readOnly' | 'onClose'
+> {
+ target: EditorTarget
+}
+
+/** Render an editor for either the setup posture or an expanded provider row. */
+function renderProviderEditor({ target, ...props }: ProviderEditorRenderProps): ReactNode {
+ return (
+
+ )
+}
+
/**
* Remove one user-added provider and its page-managed credential. Credential
* removal comes first so a second-step failure leaves the provider row visible
@@ -232,16 +252,14 @@ function Loaded({ injected }: { injected: ModelsSectionInjected }): ReactNode {
// setup card IS its presence on the page.
return (
- { closeEditor(changed, target) }}
- />
+ {renderProviderEditor({
+ target,
+ namespace,
+ api,
+ t,
+ readOnly: !state.writable,
+ onClose: (changed) => { closeEditor(changed, target) },
+ })}
)
}
@@ -312,18 +330,14 @@ function Loaded({ injected }: { injected: ModelsSectionInjected }): ReactNode {
{open
- ? (
- { closeEditor(changed, target) }}
- />
- )
+ ? renderProviderEditor({
+ target,
+ namespace,
+ api,
+ t,
+ readOnly: !state.writable,
+ onClose: (changed) => { closeEditor(changed, target) },
+ })
: null}
)
diff --git a/packages/client/ui-models/tests/provider-form.spec.tsx b/packages/client/ui-models/tests/provider-form.spec.tsx
index 99e85b0d10..367be642d0 100644
--- a/packages/client/ui-models/tests/provider-form.spec.tsx
+++ b/packages/client/ui-models/tests/provider-form.spec.tsx
@@ -142,7 +142,7 @@ async function mountSection(options: Parameters[0] = {}) {
t,
}
render()
- return scripted
+ return { ...scripted, controller }
}
/** Open the editor of one configured row and expand its customized fold. */
@@ -862,4 +862,20 @@ describe('hand-declared providers', () => {
await waitFor(() => { expect(screen.queryByText(en.customTitle)).toBeNull() })
expect(screen.getByRole('button', { name: en.customAdd })).toBeTruthy()
})
+
+ it('reloads the section after creating a hand-declared provider', async () => {
+ const { controller, mutate } = await mountSection()
+ const load = vi.spyOn(controller, 'load')
+
+ fireEvent.click(screen.getByRole('button', { name: en.customAdd }))
+ fireEvent.change(screen.getByLabelText(en.customRoute), { target: { value: 'acme' } })
+ fireEvent.change(screen.getByLabelText(en.baseUrl), { target: { value: 'https://acme.test/v1' } })
+ fireEvent.click(screen.getByRole('button', { name: en.addModel }))
+ fireEvent.change(screen.getByLabelText(`${en.modelId} 1`), { target: { value: 'm' } })
+ fireEvent.click(screen.getByText(en.create))
+
+ await waitFor(() => { expect(mutate).toHaveBeenCalledOnce() })
+ await waitFor(() => { expect(load).toHaveBeenCalledOnce() })
+ expect(screen.queryByText(en.customTitle)).toBeNull()
+ })
})