Add support for custom AI providers and refresh the built-in model catalog from settings. - Recognize custom provider configuration in provider status and onboarding readiness. - Add bounded model-catalog refresh endpoints and settings controls with localized copy. - Cover provider discovery, cache refresh, routes, and authentication UI behavior with tests. Files changed: .changeset/fn-019-provider-catalog.md | 7 ++ docs/dashboard-guide.md | 6 ++ .../dashboard/app/__tests__/api-models.test.ts | 29 +++++++++ .../dashboard/app/api/settings/provider-status.ts | 15 +++++ ...thenticationSection.modelsCacheRefresh.test.tsx | 46 +++++++++++++- .../__tests__/AuthenticationSection.test.tsx | 10 +++ .../settings/sections/AuthenticationSection.css | 20 ++++++ .../settings/sections/AuthenticationSection.tsx | 37 ++++++++++- .../app/hooks/__tests__/useAuthOnboarding.test.ts | 29 +++++++++ .../app/hooks/__tests__/useSetupReadiness.test.ts | 14 ++++ packages/dashboard/app/hooks/useAuthOnboarding.ts | 9 +-- packages/dashboard/app/hooks/useSetupReadiness.ts | 8 ++- .../register-model-routes-refresh-bounding.test.ts | 74 ++++++++++++++++++++++ .../dashboard/src/__tests__/routes-auth.test.ts | 18 ++++++ .../dashboard/src/routes/register-auth-routes.ts | 17 ++++- .../dashboard/src/routes/register-model-routes.ts | 22 ++++++- packages/i18n/locales/en/app.json | 6 ++ packages/i18n/locales/es/app.json | 6 ++ packages/i18n/locales/fr/app.json | 6 ++ packages/i18n/locales/ko/app.json | 6 ++ packages/i18n/locales/pt-BR/app.json | 6 ++ packages/i18n/locales/zh-CN/app.json | 6 ++ packages/i18n/locales/zh-TW/app.json | 6 ++ packages/i18n/src/resources.d.ts | 6 ++ 24 files changed, 397 insertions(+), 12 deletions(-) Fusion-Task-Id: FN-019 Fusion-Task-Lineage: 1e9daf0e-8aa8-4205-9fc7-686fbdd5bd35 Co-authored-by: Fusion <noreply@runfusion.ai>
66 lines
2.7 KiB
CSS
66 lines
2.7 KiB
CSS
/*
|
|
FNXC:ProviderAuth 2026-08-01-06:25:
|
|
Credential instance actions remain compact within the existing provider card and wrap on a narrow
|
|
Settings modal so account controls stay reachable without introducing a second card system.
|
|
*/
|
|
.auth-instance-controls { display: flex; flex-direction: column; gap: var(--space-sm); margin-top: var(--space-sm); }
|
|
.auth-instance-list { display: flex; flex-direction: column; gap: var(--space-xs); }
|
|
.auth-instance-row, .auth-instance-pending { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-xs); }
|
|
.auth-instance-pending { gap: var(--space-sm); padding: var(--space-sm); }
|
|
.auth-pending-instance-field { display: flex; flex: 1 1 var(--space-2xl); flex-direction: column; gap: var(--space-xs); }
|
|
.auth-pending-instance-label { color: var(--text-muted); font-size: var(--font-size-xs); font-weight: 600; }
|
|
.auth-instance-row > span { flex: 1; min-width: 0; }
|
|
|
|
.auth-model-refresh {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-sm);
|
|
margin: var(--space-md) 0;
|
|
}
|
|
.auth-model-refresh-feedback {
|
|
flex: 1 1 var(--space-2xl);
|
|
min-width: 0;
|
|
font-size: var(--font-size-xs);
|
|
line-height: 1.4;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
.auth-model-refresh-feedback--success { color: var(--color-success); }
|
|
.auth-model-refresh-feedback--warning { color: var(--color-warning); }
|
|
.auth-model-refresh-feedback--error { color: var(--color-error); }
|
|
|
|
/*
|
|
FNXC:ProviderAuth 2026-08-15-22:08:
|
|
OAuth loginError is a durable card banner, not an inline hint. A wrapping block
|
|
with a defined width keeps the Anthropic expiry sentence inside the provider
|
|
card on a phone-width Settings modal; an inline <small> sized the actions
|
|
flex item to min-content and painted a broken per-line border past the card.
|
|
Inset matches .auth-provider-header so Login and the banner share one edge.
|
|
*/
|
|
.auth-provider-login-error {
|
|
display: block;
|
|
box-sizing: border-box;
|
|
width: auto;
|
|
max-width: 100%;
|
|
min-width: 0;
|
|
margin: 0 var(--space-md) var(--space-sm);
|
|
padding: var(--space-sm);
|
|
border: 1px solid var(--color-error);
|
|
border-radius: var(--radius-md);
|
|
background: color-mix(in srgb, var(--color-error) 10%, transparent);
|
|
color: var(--color-error);
|
|
font-size: var(--font-size-xs);
|
|
line-height: 1.4;
|
|
overflow-wrap: anywhere;
|
|
word-break: break-word;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.auth-instance-row, .auth-instance-pending { align-items: stretch; }
|
|
.auth-instance-row > .btn, .auth-instance-pending > .btn { flex: 1; }
|
|
.auth-pending-instance-field { flex-basis: 100%; }
|
|
.auth-provider-login-error { margin-inline: var(--space-sm); }
|
|
.auth-model-refresh { align-items: stretch; }
|
|
.auth-model-refresh > .btn { flex: 1 1 100%; }
|
|
}
|