diff --git a/apps/web/src/components/settings/ProviderSettingsPanel.tsx b/apps/web/src/components/settings/ProviderSettingsPanel.tsx index 3356f7c2b3c1..6c0064a08fbe 100644 --- a/apps/web/src/components/settings/ProviderSettingsPanel.tsx +++ b/apps/web/src/components/settings/ProviderSettingsPanel.tsx @@ -33,10 +33,9 @@ import { } from "@t3tools/shared/backgroundActivitySettings"; import * as Arr from "effect/Array"; import * as Duration from "effect/Duration"; -import * as Equal from "effect/Equal"; import * as Result from "effect/Result"; import { PlusIcon } from "lucide-react"; -import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react"; +import { useRef, useState, type ReactNode } from "react"; import { isDesktopLocalConnectionTarget } from "../../connection/desktopLocal"; import { @@ -292,10 +291,10 @@ function ProviderSettingsPanelContent(target: ProviderSettingsTarget) { const { environments, isReady } = useEnvironments(); const primaryEnvironmentId = usePrimaryEnvironmentId(); const searchTargetId = useSettingsSearchTargetId(); - const options = useMemo( - () => - buildProviderEnvironmentOptions(environments, primaryEnvironmentId, target.environmentIds), - [environments, primaryEnvironmentId, target.environmentIds], + const options = buildProviderEnvironmentOptions( + environments, + primaryEnvironmentId, + target.environmentIds, ); const [selectedEnvironmentId, setSelectedEnvironmentId] = useState( target.environmentId ?? primaryEnvironmentId, @@ -330,7 +329,9 @@ function ProviderSettingsPanelContent(target: ProviderSettingsTarget) { hasServerConfig: true, }), )?.environmentId; - useEffect(() => { + // A search jump to a setting the selected device cannot show switches to one that can. + let searchRedirectEnvironmentId: EnvironmentId | undefined; + { if ( !target.scoped && searchTargetId === searchableSetting("cursor-keychain-usage").id && @@ -338,26 +339,23 @@ function ProviderSettingsPanelContent(target: ProviderSettingsTarget) { selectedEnvironment?.serverConfig?.environment.platform.os !== "darwin") && searchableCursorEnvironmentId !== undefined ) { - setSelectedEnvironmentId(searchableCursorEnvironmentId); - return; - } - if ( + searchRedirectEnvironmentId = searchableCursorEnvironmentId; + } else if ( !target.scoped && (searchTargetId === searchableSetting("provider-health-check-interval").id || searchTargetId === searchableSetting("usage-providers").id) && !selectedEnvironmentCanRenderSettings && searchableEnvironmentId !== undefined ) { - setSelectedEnvironmentId(searchableEnvironmentId); + searchRedirectEnvironmentId = searchableEnvironmentId; } - }, [ - searchTargetId, - searchableCursorEnvironmentId, - searchableEnvironmentId, - selectedEnvironment, - selectedEnvironmentCanRenderSettings, - target.scoped, - ]); + } + if ( + searchRedirectEnvironmentId !== undefined && + searchRedirectEnvironmentId !== selectedEnvironmentId + ) { + setSelectedEnvironmentId(searchRedirectEnvironmentId); + } const onlyPrimaryDevice = options.length === 1 && options[0]?.entry.target._tag === "PrimaryConnectionTarget"; const deviceTabs = @@ -538,13 +536,15 @@ function AccessGatedProviderSettings({ ); } +const NO_PROJECT_IDS: readonly ProjectId[] = []; + export function EnvironmentProviderSettings({ environmentId, environmentLabel, readOnly = false, deviceTabs, targetInstanceId, - projectIds = [], + projectIds = NO_PROJECT_IDS, }: { readonly environmentId: EnvironmentId; readonly environmentLabel: string; @@ -593,42 +593,38 @@ export function EnvironmentProviderSettings({ const refreshingRef = useRef(false); const updatingInstanceIdsRef = useRef>(new Set()); - const acceptUrlAuthentication = useCallback( - (instanceId: ProviderInstanceId, action: AcpRegistryUrlAuthAction) => { - void acceptAcpRegistryUrlAuth({ - environmentId, - input: { instanceId, elicitationId: action.elicitationId }, - }).then((result) => { - if (result._tag === "Success" && !result.value.accepted) { - toastManager.add({ - type: "warning", - title: "Authentication request expired", - description: "Refresh the provider and start the authentication flow again.", - }); - return; - } - if (result._tag === "Failure" && !isAtomCommandInterrupted(result)) { - const error = squashAtomCommandFailure(result); - toastManager.add({ - type: "error", - title: "Could not continue authentication", - description: - error instanceof Error ? error.message : "The authentication request expired.", - }); - } - }); - }, - [acceptAcpRegistryUrlAuth, environmentId], - ); + const acceptUrlAuthentication = ( + instanceId: ProviderInstanceId, + action: AcpRegistryUrlAuthAction, + ) => { + void acceptAcpRegistryUrlAuth({ + environmentId, + input: { instanceId, elicitationId: action.elicitationId }, + }).then((result) => { + if (result._tag === "Success" && !result.value.accepted) { + toastManager.add({ + type: "warning", + title: "Authentication request expired", + description: "Refresh the provider and start the authentication flow again.", + }); + return; + } + if (result._tag === "Failure" && !isAtomCommandInterrupted(result)) { + const error = squashAtomCommandFailure(result); + toastManager.add({ + type: "error", + title: "Could not continue authentication", + description: + error instanceof Error ? error.message : "The authentication request expired.", + }); + } + }); + }; - const providerUpdateCandidateByInstanceId = useMemo( - () => - new Map( - serverProviders - .filter(isProviderSettingsUpdateCandidate) - .map((candidate) => [candidate.instanceId, candidate]), - ), - [serverProviders], + const providerUpdateCandidateByInstanceId = new Map( + serverProviders + .filter(isProviderSettingsUpdateCandidate) + .map((candidate) => [candidate.instanceId, candidate]), ); const visibleProviderSettings = PROVIDER_SETTINGS.filter( (providerSettings) => @@ -656,7 +652,7 @@ export function EnvironmentProviderSettings({ ) : null; - const refreshProviders = useCallback(() => { + const refreshProviders = () => { if (refreshingRef.current || !readEnvironmentScope(environmentId, AuthOrchestrationReadScope)) return; refreshingRef.current = true; @@ -676,55 +672,52 @@ export function EnvironmentProviderSettings({ }); } })(); - }, [environmentId, refreshServerProviders]); + }; - const runProviderUpdate = useCallback( - async ( - candidate: Pick, - targetVersion?: string, - ) => { - if (!readEnvironmentScope(environmentId, AuthProvidersManageScope)) return; - // Ref-based re-entry guard, mirroring refreshProviders: a state updater - // may run after this function returns, so it cannot gate the dispatch. - if (updatingInstanceIdsRef.current.has(candidate.instanceId)) { - return; - } - updatingInstanceIdsRef.current.add(candidate.instanceId); - setUpdatingProviderInstanceIds((previous) => new Set(previous).add(candidate.instanceId)); + const runProviderUpdate = async ( + candidate: Pick, + targetVersion?: string, + ) => { + if (!readEnvironmentScope(environmentId, AuthProvidersManageScope)) return; + // Ref-based re-entry guard, mirroring refreshProviders: a state updater + // may run after this function returns, so it cannot gate the dispatch. + if (updatingInstanceIdsRef.current.has(candidate.instanceId)) { + return; + } + updatingInstanceIdsRef.current.add(candidate.instanceId); + setUpdatingProviderInstanceIds((previous) => new Set(previous).add(candidate.instanceId)); - const result = await updateProvider({ - environmentId, - input: { - provider: candidate.driver, - instanceId: candidate.instanceId, - ...(targetVersion ? { targetVersion } : {}), - }, - }); - if (result._tag === "Failure" && !isAtomCommandInterrupted(result)) { - const error = squashAtomCommandFailure(result); - toastManager.add( - stackedThreadToast({ - type: "error", - title: `Could not update ${PROVIDER_DISPLAY_NAMES[candidate.driver] ?? candidate.driver}`, - description: - error instanceof Error - ? error.message - : "The provider update command could not be started.", - }), - ); + const result = await updateProvider({ + environmentId, + input: { + provider: candidate.driver, + instanceId: candidate.instanceId, + ...(targetVersion ? { targetVersion } : {}), + }, + }); + if (result._tag === "Failure" && !isAtomCommandInterrupted(result)) { + const error = squashAtomCommandFailure(result); + toastManager.add( + stackedThreadToast({ + type: "error", + title: `Could not update ${PROVIDER_DISPLAY_NAMES[candidate.driver] ?? candidate.driver}`, + description: + error instanceof Error + ? error.message + : "The provider update command could not be started.", + }), + ); + } + updatingInstanceIdsRef.current.delete(candidate.instanceId); + setUpdatingProviderInstanceIds((previous) => { + if (!previous.has(candidate.instanceId)) { + return previous; } - updatingInstanceIdsRef.current.delete(candidate.instanceId); - setUpdatingProviderInstanceIds((previous) => { - if (!previous.has(candidate.instanceId)) { - return previous; - } - const next = new Set(previous); - next.delete(candidate.instanceId); - return next; - }); - }, - [environmentId, updateProvider], - ); + const next = new Set(previous); + next.delete(candidate.instanceId); + return next; + }); + }; interface InstanceRow { readonly instanceId: ProviderInstanceId;