Add pending extension indicator to extension panel (#5493)
Signed-off-by: Alex Holder <alexeeyre@gmail.com>
This commit is contained in:
@@ -48,11 +48,22 @@ export default function ExtensionsSection({
|
|||||||
const [showEnvVarsStateVar, setShowEnvVarsStateVar] = useState<boolean | undefined | null>(
|
const [showEnvVarsStateVar, setShowEnvVarsStateVar] = useState<boolean | undefined | null>(
|
||||||
showEnvVars
|
showEnvVars
|
||||||
);
|
);
|
||||||
|
const [pendingActivationExtensions, setPendingActivationExtensions] = useState<Set<string>>(
|
||||||
|
new Set()
|
||||||
|
);
|
||||||
|
|
||||||
// Update deep link state when props change
|
// Update deep link state when props change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDeepLinkConfigStateVar(deepLinkConfig);
|
setDeepLinkConfigStateVar(deepLinkConfig);
|
||||||
setShowEnvVarsStateVar(showEnvVars);
|
setShowEnvVarsStateVar(showEnvVars);
|
||||||
|
|
||||||
|
if (deepLinkConfig && !showEnvVars) {
|
||||||
|
setPendingActivationExtensions((prev) => {
|
||||||
|
const updated = new Set(prev);
|
||||||
|
updated.add(deepLinkConfig.name);
|
||||||
|
return updated;
|
||||||
|
});
|
||||||
|
}
|
||||||
}, [deepLinkConfig, showEnvVars]);
|
}, [deepLinkConfig, showEnvVars]);
|
||||||
|
|
||||||
// Process extensions from context - this automatically updates when extensionsList changes
|
// Process extensions from context - this automatically updates when extensionsList changes
|
||||||
@@ -102,6 +113,12 @@ export default function ExtensionsSection({
|
|||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setPendingActivationExtensions((prev) => {
|
||||||
|
const updated = new Set(prev);
|
||||||
|
updated.delete(extensionConfig.name);
|
||||||
|
return updated;
|
||||||
|
});
|
||||||
|
|
||||||
await fetchExtensions();
|
await fetchExtensions();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@@ -122,8 +139,21 @@ export default function ExtensionsSection({
|
|||||||
extensionConfig: extensionConfig,
|
extensionConfig: extensionConfig,
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
});
|
});
|
||||||
|
setPendingActivationExtensions((prev) => {
|
||||||
|
const updated = new Set(prev);
|
||||||
|
updated.delete(extensionConfig.name);
|
||||||
|
return updated;
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to activate extension:', 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 {
|
} finally {
|
||||||
await fetchExtensions();
|
await fetchExtensions();
|
||||||
if (onModalClose) {
|
if (onModalClose) {
|
||||||
@@ -202,6 +232,7 @@ export default function ExtensionsSection({
|
|||||||
onConfigure={handleConfigureClick}
|
onConfigure={handleConfigureClick}
|
||||||
disableConfiguration={disableConfiguration}
|
disableConfiguration={disableConfiguration}
|
||||||
searchTerm={searchTerm}
|
searchTerm={searchTerm}
|
||||||
|
pendingActivationExtensions={pendingActivationExtensions}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!hideButtons && (
|
{!hideButtons && (
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ interface ExtensionItemProps {
|
|||||||
onToggle: (extension: FixedExtensionEntry) => Promise<boolean | void> | void;
|
onToggle: (extension: FixedExtensionEntry) => Promise<boolean | void> | void;
|
||||||
onConfigure?: (extension: FixedExtensionEntry) => void;
|
onConfigure?: (extension: FixedExtensionEntry) => void;
|
||||||
isStatic?: boolean; // to not allow users to edit configuration
|
isStatic?: boolean; // to not allow users to edit configuration
|
||||||
|
isPendingActivation?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ExtensionItem({
|
export default function ExtensionItem({
|
||||||
@@ -18,6 +19,7 @@ export default function ExtensionItem({
|
|||||||
onToggle,
|
onToggle,
|
||||||
onConfigure,
|
onConfigure,
|
||||||
isStatic,
|
isStatic,
|
||||||
|
isPendingActivation = false,
|
||||||
}: ExtensionItemProps) {
|
}: ExtensionItemProps) {
|
||||||
// Add local state to track the visual toggle state
|
// Add local state to track the visual toggle state
|
||||||
const [visuallyEnabled, setVisuallyEnabled] = useState(extension.enabled);
|
const [visuallyEnabled, setVisuallyEnabled] = useState(extension.enabled);
|
||||||
@@ -79,7 +81,17 @@ export default function ExtensionItem({
|
|||||||
onClick={() => handleToggle(extension)}
|
onClick={() => handleToggle(extension)}
|
||||||
>
|
>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="">{getFriendlyTitle(extension)}</CardTitle>
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
{getFriendlyTitle(extension)}
|
||||||
|
{isPendingActivation && (
|
||||||
|
<span
|
||||||
|
className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400 border border-amber-300 dark:border-amber-700"
|
||||||
|
title="Extension will be activated when you start a new chat session"
|
||||||
|
>
|
||||||
|
Pending
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</CardTitle>
|
||||||
|
|
||||||
<CardAction onClick={(e) => e.stopPropagation()}>
|
<CardAction onClick={(e) => e.stopPropagation()}>
|
||||||
<div className="flex items-center justify-end gap-2">
|
<div className="flex items-center justify-end gap-2">
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ interface ExtensionListProps {
|
|||||||
isStatic?: boolean;
|
isStatic?: boolean;
|
||||||
disableConfiguration?: boolean;
|
disableConfiguration?: boolean;
|
||||||
searchTerm?: string;
|
searchTerm?: string;
|
||||||
|
pendingActivationExtensions?: Set<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ExtensionList({
|
export default function ExtensionList({
|
||||||
@@ -20,6 +21,7 @@ export default function ExtensionList({
|
|||||||
isStatic,
|
isStatic,
|
||||||
disableConfiguration: _disableConfiguration,
|
disableConfiguration: _disableConfiguration,
|
||||||
searchTerm = '',
|
searchTerm = '',
|
||||||
|
pendingActivationExtensions = new Set(),
|
||||||
}: ExtensionListProps) {
|
}: ExtensionListProps) {
|
||||||
const matchesSearch = (extension: FixedExtensionEntry): boolean => {
|
const matchesSearch = (extension: FixedExtensionEntry): boolean => {
|
||||||
if (!searchTerm) return true;
|
if (!searchTerm) return true;
|
||||||
@@ -63,6 +65,7 @@ export default function ExtensionList({
|
|||||||
onToggle={onToggle}
|
onToggle={onToggle}
|
||||||
onConfigure={onConfigure}
|
onConfigure={onConfigure}
|
||||||
isStatic={isStatic}
|
isStatic={isStatic}
|
||||||
|
isPendingActivation={pendingActivationExtensions.has(extension.name)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user