import { useState, useEffect, useMemo } from 'react'; import { EditorPluginManager, IEditorPluginMetadata, EditorPluginCategory } from '@esengine/editor-core'; import * as LucideIcons from 'lucide-react'; import { Package, CheckCircle, XCircle, Search, Grid, List, ChevronDown, ChevronRight, X, RefreshCw, ShoppingCart } from 'lucide-react'; import { PluginMarketPanel } from './PluginMarketPanel'; import { PluginMarketService } from '../services/PluginMarketService'; import { GitHubService } from '../services/GitHubService'; import '../styles/PluginManagerWindow.css'; interface PluginManagerWindowProps { pluginManager: EditorPluginManager; githubService: GitHubService; onClose: () => void; onRefresh?: () => Promise; onOpen?: () => void; locale: string; projectPath: string | null; } const categoryIcons: Record = { [EditorPluginCategory.Tool]: 'Wrench', [EditorPluginCategory.Window]: 'LayoutGrid', [EditorPluginCategory.Inspector]: 'Search', [EditorPluginCategory.System]: 'Settings', [EditorPluginCategory.ImportExport]: 'Package' }; export function PluginManagerWindow({ pluginManager, githubService, onClose, onRefresh, onOpen, locale, projectPath }: PluginManagerWindowProps) { const t = (key: string) => { const translations: Record> = { zh: { title: '插件管理器', searchPlaceholder: '搜索插件...', enabled: '已启用', disabled: '已禁用', enable: '启用', disable: '禁用', enablePlugin: '启用插件', disablePlugin: '禁用插件', refresh: '刷新', refreshPluginList: '刷新插件列表', close: '关闭', listView: '列表视图', gridView: '网格视图', noPlugins: '未安装插件', installed: '安装于', categoryTools: '工具', categoryWindows: '窗口', categoryInspectors: '检查器', categorySystem: '系统', categoryImportExport: '导入/导出', tabInstalled: '已安装', tabMarketplace: '插件市场' }, en: { title: 'Plugin Manager', searchPlaceholder: 'Search plugins...', enabled: 'Enabled', disabled: 'Disabled', enable: 'Enable', disable: 'Disable', enablePlugin: 'Enable plugin', disablePlugin: 'Disable plugin', refresh: 'Refresh', refreshPluginList: 'Refresh plugin list', close: 'Close', listView: 'List view', gridView: 'Grid view', noPlugins: 'No plugins installed', installed: 'Installed', categoryTools: 'Tools', categoryWindows: 'Windows', categoryInspectors: 'Inspectors', categorySystem: 'System', categoryImportExport: 'Import/Export', tabInstalled: 'Installed', tabMarketplace: 'Marketplace' } }; return translations[locale]?.[key] || translations.en?.[key] || key; }; const getCategoryName = (category: EditorPluginCategory): string => { const categoryKeys: Record = { [EditorPluginCategory.Tool]: 'categoryTools', [EditorPluginCategory.Window]: 'categoryWindows', [EditorPluginCategory.Inspector]: 'categoryInspectors', [EditorPluginCategory.System]: 'categorySystem', [EditorPluginCategory.ImportExport]: 'categoryImportExport' }; return t(categoryKeys[category]); }; const [activeTab, setActiveTab] = useState<'installed' | 'marketplace'>('installed'); const [plugins, setPlugins] = useState([]); const [filter, setFilter] = useState(''); const [viewMode, setViewMode] = useState<'grid' | 'list'>('list'); const [expandedCategories, setExpandedCategories] = useState>( new Set(Object.values(EditorPluginCategory)) ); const [isRefreshing, setIsRefreshing] = useState(false); const marketService = useMemo(() => new PluginMarketService(pluginManager), [pluginManager]); // 设置项目路径到 marketService useEffect(() => { marketService.setProjectPath(projectPath); }, [projectPath, marketService]); const updatePluginList = () => { const allPlugins = pluginManager.getAllPluginMetadata(); setPlugins(allPlugins); }; useEffect(() => { if (onOpen) { onOpen(); } updatePluginList(); }, [pluginManager]); // 监听 locale 变化,重新获取插件列表(以刷新插件的 displayName 和 description) useEffect(() => { updatePluginList(); }, [locale]); const handleRefresh = async () => { if (!onRefresh || isRefreshing) return; setIsRefreshing(true); try { await onRefresh(); updatePluginList(); } catch (error) { console.error('Failed to refresh plugins:', error); } finally { setIsRefreshing(false); } }; const togglePlugin = async (name: string, enabled: boolean) => { try { if (enabled) { await pluginManager.disablePlugin(name); } else { await pluginManager.enablePlugin(name); } const allPlugins = pluginManager.getAllPluginMetadata(); setPlugins(allPlugins); } catch (error) { console.error(`Failed to toggle plugin ${name}:`, error); } }; const toggleCategory = (category: EditorPluginCategory) => { const newExpanded = new Set(expandedCategories); if (newExpanded.has(category)) { newExpanded.delete(category); } else { newExpanded.add(category); } setExpandedCategories(newExpanded); }; const filteredPlugins = plugins.filter((plugin) => { if (!filter) return true; const searchLower = filter.toLowerCase(); return ( plugin.name.toLowerCase().includes(searchLower) || plugin.displayName.toLowerCase().includes(searchLower) || plugin.description?.toLowerCase().includes(searchLower) ); }); const pluginsByCategory = filteredPlugins.reduce( (acc, plugin) => { if (!acc[plugin.category]) { acc[plugin.category] = []; } acc[plugin.category].push(plugin); return acc; }, {} as Record ); const enabledCount = plugins.filter((p) => p.enabled).length; const disabledCount = plugins.filter((p) => !p.enabled).length; const renderPluginCard = (plugin: IEditorPluginMetadata) => { const IconComponent = plugin.icon ? (LucideIcons as any)[plugin.icon] : null; return (
{IconComponent ? : }
{plugin.displayName}
v{plugin.version}
{plugin.description &&
{plugin.description}
}
{(() => { const CategoryIcon = (LucideIcons as any)[categoryIcons[plugin.category]]; return CategoryIcon ? : null; })()} {getCategoryName(plugin.category)} {plugin.installedAt && ( {t('installed')}: {new Date(plugin.installedAt).toLocaleDateString()} )}
); }; const renderPluginList = (plugin: IEditorPluginMetadata) => { const IconComponent = plugin.icon ? (LucideIcons as any)[plugin.icon] : null; return (
{IconComponent ? : }
{plugin.displayName} v{plugin.version}
{plugin.description &&
{plugin.description}
}
{plugin.enabled ? ( {t('enabled')} ) : ( {t('disabled')} )}
); }; return (
e.stopPropagation()}>

{t('title')}

{activeTab === 'installed' && ( <>
setFilter(e.target.value)} />
{enabledCount} {t('enabled')} {disabledCount} {t('disabled')}
{onRefresh && ( )}
{plugins.length === 0 ? (

{t('noPlugins')}

) : (
{Object.entries(pluginsByCategory).map(([category, categoryPlugins]) => { const cat = category as EditorPluginCategory; const isExpanded = expandedCategories.has(cat); return (
toggleCategory(cat)} > {(() => { const CategoryIcon = (LucideIcons as any)[ categoryIcons[cat] ]; return CategoryIcon ? : null; })()} {getCategoryName(cat)} {categoryPlugins.length}
{isExpanded && (
{viewMode === 'grid' ? categoryPlugins.map(renderPluginCard) : categoryPlugins.map(renderPluginList)}
)}
); })}
)}
)} {activeTab === 'marketplace' && ( )}
); }