/** * ApiKeysPanel — grid of saved API keys with add button and empty state. */ import { useEffect, useState } from 'react'; import { Button } from '@renderer/components/ui/button'; import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip'; import { useStore } from '@renderer/store'; import { useShallow } from 'zustand/react/shallow'; import { AlertTriangle, Info, Key, Plus } from 'lucide-react'; import { ApiKeyCard } from './ApiKeyCard'; import { ApiKeyFormDialog } from './ApiKeyFormDialog'; import type { ApiKeyEntry } from '@shared/types/extensions'; export const ApiKeysPanel = (): React.JSX.Element => { const { apiKeys, apiKeysLoading, apiKeysError, storageStatus, fetchStorageStatus } = useStore( useShallow((s) => ({ apiKeys: s.apiKeys, apiKeysLoading: s.apiKeysLoading, apiKeysError: s.apiKeysError, storageStatus: s.apiKeyStorageStatus, fetchStorageStatus: s.fetchApiKeyStorageStatus, })) ); const [dialogOpen, setDialogOpen] = useState(false); const [editingKey, setEditingKey] = useState(null); useEffect(() => { void fetchStorageStatus(); }, [fetchStorageStatus]); const handleAdd = () => { setEditingKey(null); setDialogOpen(true); }; const handleEdit = (key: ApiKeyEntry) => { setEditingKey(key); setDialogOpen(true); }; const handleDialogClose = () => { setDialogOpen(false); setEditingKey(null); }; const isOsKeychain = storageStatus?.encryptionMethod === 'os-keychain'; return (
{/* Header row */}

Securely store API keys for auto-filling when installing MCP servers. {storageStatus && ( {isOsKeychain ? ( ) : ( )} {isOsKeychain ? (

Keys are encrypted via {storageStatus.backend} and stored with restricted file permissions (owner-only).

) : (

OS keychain unavailable — keys are encrypted locally with AES-256. For stronger protection, install a keyring service (gnome-keyring, kwallet).

)} )}

{/* Error */} {apiKeysError && (
{apiKeysError}
)} {/* Skeleton loading */} {apiKeysLoading && apiKeys.length === 0 && (
{Array.from({ length: 3 }, (_, i) => (
))}
)} {/* Empty state */} {!apiKeysLoading && apiKeys.length === 0 && (

No API keys saved

Add keys to auto-fill environment variables when installing MCP servers.

)} {/* Key cards grid */} {apiKeys.length > 0 && (
{apiKeys.map((key) => ( ))}
)} {/* Form dialog */}
); };