From ec6faf92a34ed59573d17649b6c9cb68b2c31aa6 Mon Sep 17 00:00:00 2001 From: Alex Holder Date: Thu, 6 Nov 2025 04:30:34 +0000 Subject: [PATCH] Add pending extension indicator to extension panel (#5493) Signed-off-by: Alex Holder --- .../settings/extensions/ExtensionsSection.tsx | 31 +++++++++++++++++++ .../subcomponents/ExtensionItem.tsx | 14 ++++++++- .../subcomponents/ExtensionList.tsx | 3 ++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/ui/desktop/src/components/settings/extensions/ExtensionsSection.tsx b/ui/desktop/src/components/settings/extensions/ExtensionsSection.tsx index d1a72be9..172f0794 100644 --- a/ui/desktop/src/components/settings/extensions/ExtensionsSection.tsx +++ b/ui/desktop/src/components/settings/extensions/ExtensionsSection.tsx @@ -48,11 +48,22 @@ export default function ExtensionsSection({ const [showEnvVarsStateVar, setShowEnvVarsStateVar] = useState( showEnvVars ); + const [pendingActivationExtensions, setPendingActivationExtensions] = useState>( + new Set() + ); // Update deep link state when props change useEffect(() => { setDeepLinkConfigStateVar(deepLinkConfig); setShowEnvVarsStateVar(showEnvVars); + + if (deepLinkConfig && !showEnvVars) { + setPendingActivationExtensions((prev) => { + const updated = new Set(prev); + updated.add(deepLinkConfig.name); + return updated; + }); + } }, [deepLinkConfig, showEnvVars]); // Process extensions from context - this automatically updates when extensionsList changes @@ -102,6 +113,12 @@ export default function ExtensionsSection({ sessionId: sessionId, }); + setPendingActivationExtensions((prev) => { + const updated = new Set(prev); + updated.delete(extensionConfig.name); + return updated; + }); + await fetchExtensions(); return true; }; @@ -122,8 +139,21 @@ export default function ExtensionsSection({ extensionConfig: extensionConfig, sessionId: sessionId, }); + setPendingActivationExtensions((prev) => { + const updated = new Set(prev); + updated.delete(extensionConfig.name); + return updated; + }); } catch (error) { console.error('Failed to activate extension:', error); + // If activation fails, mark as pending if it's enabled in config + if (formData.enabled) { + setPendingActivationExtensions((prev) => { + const updated = new Set(prev); + updated.add(extensionConfig.name); + return updated; + }); + } } finally { await fetchExtensions(); if (onModalClose) { @@ -202,6 +232,7 @@ export default function ExtensionsSection({ onConfigure={handleConfigureClick} disableConfiguration={disableConfiguration} searchTerm={searchTerm} + pendingActivationExtensions={pendingActivationExtensions} /> {!hideButtons && ( diff --git a/ui/desktop/src/components/settings/extensions/subcomponents/ExtensionItem.tsx b/ui/desktop/src/components/settings/extensions/subcomponents/ExtensionItem.tsx index 872142df..f6772603 100644 --- a/ui/desktop/src/components/settings/extensions/subcomponents/ExtensionItem.tsx +++ b/ui/desktop/src/components/settings/extensions/subcomponents/ExtensionItem.tsx @@ -11,6 +11,7 @@ interface ExtensionItemProps { onToggle: (extension: FixedExtensionEntry) => Promise | void; onConfigure?: (extension: FixedExtensionEntry) => void; isStatic?: boolean; // to not allow users to edit configuration + isPendingActivation?: boolean; } export default function ExtensionItem({ @@ -18,6 +19,7 @@ export default function ExtensionItem({ onToggle, onConfigure, isStatic, + isPendingActivation = false, }: ExtensionItemProps) { // Add local state to track the visual toggle state const [visuallyEnabled, setVisuallyEnabled] = useState(extension.enabled); @@ -79,7 +81,17 @@ export default function ExtensionItem({ onClick={() => handleToggle(extension)} > - {getFriendlyTitle(extension)} + + {getFriendlyTitle(extension)} + {isPendingActivation && ( + + Pending + + )} + e.stopPropagation()}>
diff --git a/ui/desktop/src/components/settings/extensions/subcomponents/ExtensionList.tsx b/ui/desktop/src/components/settings/extensions/subcomponents/ExtensionList.tsx index a3913707..a2ab0b12 100644 --- a/ui/desktop/src/components/settings/extensions/subcomponents/ExtensionList.tsx +++ b/ui/desktop/src/components/settings/extensions/subcomponents/ExtensionList.tsx @@ -11,6 +11,7 @@ interface ExtensionListProps { isStatic?: boolean; disableConfiguration?: boolean; searchTerm?: string; + pendingActivationExtensions?: Set; } export default function ExtensionList({ @@ -20,6 +21,7 @@ export default function ExtensionList({ isStatic, disableConfiguration: _disableConfiguration, searchTerm = '', + pendingActivationExtensions = new Set(), }: ExtensionListProps) { const matchesSearch = (extension: FixedExtensionEntry): boolean => { if (!searchTerm) return true; @@ -63,6 +65,7 @@ export default function ExtensionList({ onToggle={onToggle} onConfigure={onConfigure} isStatic={isStatic} + isPendingActivation={pendingActivationExtensions.has(extension.name)} /> ))}