diff --git a/ui/desktop/src/components/ConfigContext.tsx b/ui/desktop/src/components/ConfigContext.tsx index f676deb1..434cad8c 100644 --- a/ui/desktop/src/components/ConfigContext.tsx +++ b/ui/desktop/src/components/ConfigContext.tsx @@ -9,7 +9,7 @@ import { removeExtension as apiRemoveExtension, providers, } from '../api'; -import { syncBundledExtensions } from './settings/extensions'; +import { pruneDeprecatedBundledExtensions, syncBundledExtensions } from './settings/extensions'; import type { ConfigResponse, UpsertConfigQuery, @@ -88,16 +88,19 @@ export const ConfigProvider: React.FC = ({ children }) => { [reloadConfig] ); - const read = useCallback(async (key: string, is_secret: boolean = false, options?: { throwOnError?: boolean }) => { - const query: ConfigKeyQuery = { key: key, is_secret: is_secret }; - const response = await readConfig({ - body: query, - }); - if (options?.throwOnError && response.error) { - throw response.error; - } - return response.data; - }, []); + const read = useCallback( + async (key: string, is_secret: boolean = false, options?: { throwOnError?: boolean }) => { + const query: ConfigKeyQuery = { key: key, is_secret: is_secret }; + const response = await readConfig({ + body: query, + }); + if (options?.throwOnError && response.error) { + throw response.error; + } + return response.data; + }, + [] + ); const remove = useCallback( async (key: string, is_secret: boolean) => { @@ -226,6 +229,10 @@ export const ConfigProvider: React.FC = ({ children }) => { const query: ExtensionQuery = { name, config, enabled }; await apiAddExtension({ body: query }); }; + const removeExtensionForSync = async (name: string) => { + await apiRemoveExtension({ path: { name } }); + }; + extensions = await pruneDeprecatedBundledExtensions(extensions, removeExtensionForSync); await syncBundledExtensions(extensions, addExtensionForSync); // Reload extensions after sync const refreshedResponse = await apiGetExtensions(); diff --git a/ui/desktop/src/components/bottom_menu/BottomMenuExtensionSelection.tsx b/ui/desktop/src/components/bottom_menu/BottomMenuExtensionSelection.tsx index e7b2758c..2128b134 100644 --- a/ui/desktop/src/components/bottom_menu/BottomMenuExtensionSelection.tsx +++ b/ui/desktop/src/components/bottom_menu/BottomMenuExtensionSelection.tsx @@ -7,6 +7,7 @@ import { Switch } from '../ui/switch'; import { FixedExtensionEntry, useConfig } from '../ConfigContext'; import { toastService } from '../../toasts'; import { formatExtensionName } from '../settings/extensions/subcomponents/ExtensionList'; +import { nameToKey } from '../settings/extensions/utils'; import { ExtensionConfig, getSessionExtensions } from '../../api'; import { addToAgent, removeFromAgent } from '../settings/extensions/agent-api'; import { @@ -230,15 +231,29 @@ export const BottomMenuExtensionSelection = ({ sessionId }: BottomMenuExtensionS ); } - const sessionExtensionNames = new Set(sessionExtensions.map((ext) => ext.name)); + const sessionExtensionKeys = new Set(sessionExtensions.map((ext) => nameToKey(ext.name))); + const globalExtensionKeys = new Set(allExtensions.map((ext) => nameToKey(ext.name))); - return allExtensions.map( + const mergedExtensions = allExtensions.map( (ext) => ({ ...ext, - enabled: sessionExtensionNames.has(ext.name), + enabled: sessionExtensionKeys.has(nameToKey(ext.name)), }) as FixedExtensionEntry ); + + for (const sessionExtension of sessionExtensions) { + if (globalExtensionKeys.has(nameToKey(sessionExtension.name))) { + continue; + } + + mergedExtensions.push({ + ...sessionExtension, + enabled: true, + }); + } + + return mergedExtensions; // eslint-disable-next-line react-hooks/exhaustive-deps }, [allExtensions, sessionExtensions, isHubView, hubUpdateTrigger]); @@ -266,6 +281,9 @@ export const BottomMenuExtensionSelection = ({ sessionId }: BottomMenuExtensionS return extensionsList.filter((ext) => ext.enabled).length; }, [extensionsList]); + const shouldHideTrigger = + extensionsList.length === 0 || (!isHubView && !isSessionExtensionsLoaded); + return (