/** * @license * Copyright 2026 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import type React from 'react'; import { useState } from 'react'; import { Box, Text } from 'ink'; import type { RegistryExtension } from '../../../config/extensionRegistryClient.js'; import { useKeypress } from '../../hooks/useKeypress.js'; import { Command } from '../../key/keyMatchers.js'; import { useKeyMatchers } from '../../hooks/useKeyMatchers.js'; import { theme } from '../../semantic-colors.js'; export interface ExtensionDetailsProps { extension: RegistryExtension; onBack: () => void; onInstall: ( requestConsentOverride: (consent: string) => Promise, ) => void | Promise; isInstalled: boolean; } export function ExtensionDetails({ extension, onBack, onInstall, isInstalled, }: ExtensionDetailsProps): React.JSX.Element { const keyMatchers = useKeyMatchers(); const [consentRequest, setConsentRequest] = useState<{ prompt: string; resolve: (value: boolean) => void; } | null>(null); const [isInstalling, setIsInstalling] = useState(false); useKeypress( (key) => { if (consentRequest) { if (keyMatchers[Command.ESCAPE](key)) { consentRequest.resolve(false); setConsentRequest(null); setIsInstalling(false); return true; } if (keyMatchers[Command.RETURN](key)) { consentRequest.resolve(true); setConsentRequest(null); return true; } return false; } if (keyMatchers[Command.ESCAPE](key)) { onBack(); return true; } if (keyMatchers[Command.RETURN](key) && !isInstalled && !isInstalling) { setIsInstalling(true); void onInstall( (prompt: string) => new Promise((resolve) => { setConsentRequest({ prompt, resolve }); }), ); return true; } return false; }, { isActive: true, priority: true }, ); if (consentRequest) { return ( {consentRequest.prompt} [Esc] Cancel [Enter] Accept ); } if (isInstalling) { return ( Installing {extension.extensionName}... ); } return ( {/* Header Row */} {'>'} Extensions {'>'}{' '} {extension.extensionName} {extension.extensionVersion ? `v${extension.extensionVersion}` : ''}{' '} |{' '} {String(extension.stars || 0)} |{' '} {extension.isGoogleOwned && ( [G] )} {extension.fullName} {/* Description */} {extension.extensionDescription || extension.repoDescription} {/* Features List */} {[ extension.hasMCP && { label: 'MCP', color: theme.text.primary }, extension.hasContext && { label: 'Context file', color: theme.status.error, }, extension.hasHooks && { label: 'Hooks', color: theme.status.warning }, extension.hasSkills && { label: 'Skills', color: theme.status.success, }, extension.hasCustomCommands && { label: 'Commands', color: theme.text.primary, }, ] .filter((f): f is { label: string; color: string } => !!f) .map((feature, index, array) => ( {feature.label} {index < array.length - 1 && ( | )} ))} {/* Details about MCP / Context */} {extension.hasMCP && ( This extension will run the following MCP servers: * {extension.extensionName} (local) )} {extension.hasContext && ( This extension will append info to your gemini.md context using gemini.md )} {/* Spacer to push warning to bottom */} {/* Warning Box */} {!isInstalled && ( The extension you are about to install may have been created by a third-party developer and sourced{'\n'} from a public repository. Google does not vet, endorse, or guarantee the functionality or security{'\n'} of extensions. Please carefully inspect any extension and its source code before installing to{'\n'} understand the permissions it requires and the actions it may perform. [{'Enter'}] Install )} {isInstalled && ( Already Installed )} ); }