diff --git a/src/features/runtime-provider-management/renderer/hooks/useRuntimeProviderManagement.ts b/src/features/runtime-provider-management/renderer/hooks/useRuntimeProviderManagement.ts
index 525f4acb..61edae80 100644
--- a/src/features/runtime-provider-management/renderer/hooks/useRuntimeProviderManagement.ts
+++ b/src/features/runtime-provider-management/renderer/hooks/useRuntimeProviderManagement.ts
@@ -356,7 +356,7 @@ export function useRuntimeProviderManagement(
}, [options.enabled, refresh]);
useEffect(() => {
- if (!options.enabled || !directoryOpen || !directorySupported) {
+ if (!options.enabled || !directorySupported) {
return;
}
@@ -376,7 +376,6 @@ export function useRuntimeProviderManagement(
}, [
directoryFilter,
directoryLoaded,
- directoryOpen,
directoryQuery,
directorySupported,
loadDirectoryPage,
@@ -572,6 +571,18 @@ export function useRuntimeProviderManagement(
setSuccessMessage(null);
}, []);
+ const updateProviderQuery = useCallback(
+ (value: string): void => {
+ setProviderQuery(value);
+ if (!directorySupported) {
+ return;
+ }
+ setDirectoryQuery(value);
+ setDirectoryNextCursor(null);
+ },
+ [directorySupported]
+ );
+
const cancelConnect = useCallback((): void => {
setActiveFormProviderId(null);
setApiKeyValue('');
@@ -612,12 +623,7 @@ export function useRuntimeProviderManagement(
setApiKeyValue('');
void Promise.resolve(options.onProviderChanged?.())
.then(() => refresh())
- .then(() => {
- if (directoryOpen) {
- return loadDirectoryPage({ refresh: true, cursor: null });
- }
- return undefined;
- })
+ .then(() => loadDirectoryPage({ refresh: true, cursor: null }))
.catch((refreshError) => {
setError(
refreshError instanceof Error ? refreshError.message : 'Failed to refresh providers'
@@ -631,7 +637,7 @@ export function useRuntimeProviderManagement(
setSavingProviderId(null);
}
},
- [apiKeyValue, directoryOpen, loadDirectoryPage, options, refresh]
+ [apiKeyValue, loadDirectoryPage, options, refresh]
);
const forgetProvider = useCallback(
@@ -659,12 +665,7 @@ export function useRuntimeProviderManagement(
setSavingProviderId(null);
void Promise.resolve(options.onProviderChanged?.())
.then(() => refresh())
- .then(() => {
- if (directoryOpen) {
- return loadDirectoryPage({ refresh: true, cursor: null });
- }
- return undefined;
- })
+ .then(() => loadDirectoryPage({ refresh: true, cursor: null }))
.catch((refreshError) => {
setError(
refreshError instanceof Error ? refreshError.message : 'Failed to refresh providers'
@@ -678,7 +679,7 @@ export function useRuntimeProviderManagement(
setSavingProviderId(null);
}
},
- [directoryOpen, loadDirectoryPage, options, refresh]
+ [loadDirectoryPage, options, refresh]
);
const openModelPicker = useCallback(
@@ -882,7 +883,7 @@ export function useRuntimeProviderManagement(
() => ({
refresh,
selectProvider,
- setProviderQuery,
+ setProviderQuery: updateProviderQuery,
openDirectory,
closeDirectory,
setDirectoryQuery: updateDirectoryQuery,
@@ -923,6 +924,7 @@ export function useRuntimeProviderManagement(
submitConnect,
testModel,
updateDirectoryQuery,
+ updateProviderQuery,
useModelForNewTeams,
]
);
diff --git a/src/features/runtime-provider-management/renderer/ui/RuntimeProviderManagementPanelView.tsx b/src/features/runtime-provider-management/renderer/ui/RuntimeProviderManagementPanelView.tsx
index 9f7063c2..c8b8c57c 100644
--- a/src/features/runtime-provider-management/renderer/ui/RuntimeProviderManagementPanelView.tsx
+++ b/src/features/runtime-provider-management/renderer/ui/RuntimeProviderManagementPanelView.tsx
@@ -113,6 +113,44 @@ function getDirectoryModelsLabel(provider: RuntimeProviderDirectoryEntryDto): st
return `${provider.modelCount} model${provider.modelCount === 1 ? '' : 's'}`;
}
+function directoryEntryMatchesQuery(
+ provider: RuntimeProviderDirectoryEntryDto,
+ query: string
+): boolean {
+ if (!query) {
+ return true;
+ }
+ return [
+ provider.providerId,
+ provider.displayName,
+ provider.detail ?? '',
+ provider.defaultModelId ?? '',
+ provider.sourceLabel ?? '',
+ provider.providerSource ?? '',
+ getDirectoryModelsLabel(provider),
+ formatDirectorySetupKind(provider),
+ ...provider.authMethods,
+ ]
+ .join(' ')
+ .toLowerCase()
+ .includes(query);
+}
+
+function directorySetupKindClassName(provider: RuntimeProviderDirectoryEntryDto): string {
+ switch (provider.setupKind) {
+ case 'connected':
+ return 'border-emerald-400/35 bg-emerald-400/10 text-emerald-200';
+ case 'connect-api-key':
+ case 'available-readonly':
+ return 'border-sky-400/30 bg-sky-400/10 text-sky-200';
+ case 'configure-manually':
+ case 'requires-environment':
+ return 'border-white/10 bg-white/[0.04] text-[var(--color-text-muted)]';
+ case 'unsupported':
+ return 'border-red-400/25 bg-red-400/10 text-red-200';
+ }
+}
+
function directoryEntryToProviderConnection(
provider: RuntimeProviderDirectoryEntryDto
): RuntimeProviderConnectionDto {
@@ -593,13 +631,19 @@ function ProviderRow({
function DirectoryProviderRow({
provider,
+ state,
active,
+ formOpen,
+ apiKeyValue,
disabled,
busy,
actions,
}: {
readonly provider: RuntimeProviderDirectoryEntryDto;
+ readonly state: RuntimeProviderManagementState;
readonly active: boolean;
+ readonly formOpen: boolean;
+ readonly apiKeyValue: string;
readonly disabled: boolean;
readonly busy: boolean;
readonly actions: RuntimeProviderManagementActions;
@@ -636,11 +680,7 @@ function DirectoryProviderRow({
{provider.recommended ?