import { PluginCardVO } from '@/app/home/plugins/components/plugin-installed/PluginCardVO'; import { useState } from 'react'; import { Badge } from '@/components/ui/badge'; import { useTranslation } from 'react-i18next'; import { BugIcon, ExternalLink, Ellipsis, Trash, ArrowUp, Settings, FileText, } from 'lucide-react'; import { getCloudServiceClientSync } from '@/app/infra/http'; import { httpClient } from '@/app/infra/http/HttpClient'; import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import PluginComponentList from '@/app/home/plugins/components/plugin-installed/PluginComponentList'; export default function PluginCardComponent({ cardVO, onCardClick, onDeleteClick, onUpgradeClick, onViewReadme, }: { cardVO: PluginCardVO; onCardClick: () => void; onDeleteClick: (cardVO: PluginCardVO) => void; onUpgradeClick: (cardVO: PluginCardVO) => void; onViewReadme: (cardVO: PluginCardVO) => void; }) { const { t } = useTranslation(); const [dropdownOpen, setDropdownOpen] = useState(false); const [isHovered, setIsHovered] = useState(false); return ( <>
setIsHovered(true)} onMouseLeave={() => { if (!dropdownOpen) { setIsHovered(false); } }} >
{/* Icon - fixed width */} plugin icon {/* Content area - flexible width with min-width to prevent overflow */}
{/* Top content area - allows overflow with max height */}
{cardVO.author} / {cardVO.name}
{cardVO.label}
v{cardVO.version} {cardVO.debug && ( {t('plugins.debugging')} )} {!cardVO.debug && ( <> {cardVO.install_source === 'github' && ( {t('plugins.fromGithub')} )} {cardVO.install_source === 'local' && ( {t('plugins.fromLocal')} )} {cardVO.install_source === 'marketplace' && ( {t('plugins.fromMarketplace')} )} )}
{cardVO.description}
{/* Components list - fixed at bottom */}
{ const componentKindCount: Record = {}; for (const component of cardVO.components) { const kind = component.manifest.manifest.kind; if (componentKindCount[kind]) { componentKindCount[kind]++; } else { componentKindCount[kind] = 1; } } return componentKindCount; })()} showComponentName={false} showTitle={true} useBadge={false} t={t} />
{/* Menu button - fixed width and position */}
{ setDropdownOpen(open); if (!open) { setIsHovered(false); } }} >
{cardVO.hasUpdate && (
)}
{/**upgrade */} {cardVO.install_source === 'marketplace' && ( { e.stopPropagation(); onUpgradeClick(cardVO); setDropdownOpen(false); }} > {t('plugins.update')} {cardVO.hasUpdate && ( {t('plugins.new')} )} )} {/**view source */} {(cardVO.install_source === 'github' || cardVO.install_source === 'marketplace') && ( { e.stopPropagation(); if (cardVO.install_source === 'github') { window.open(cardVO.install_info.github_url, '_blank'); } else if (cardVO.install_source === 'marketplace') { window.open( getCloudServiceClientSync().getPluginMarketplaceURL( cardVO.author, cardVO.name, ), '_blank', ); } setDropdownOpen(false); }} > {t('plugins.viewSource')} )} { e.stopPropagation(); onDeleteClick(cardVO); setDropdownOpen(false); }} > {t('plugins.delete')}
{/* Hover overlay with action buttons */}
); }