import MarketPage from '@/app/home/plugins/components/plugin-market/PluginMarketComponent'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Download } from 'lucide-react'; import React, { useState, useCallback, useEffect } from 'react'; import { httpClient } from '@/app/infra/http/HttpClient'; import { systemInfo } from '@/app/infra/http/HttpClient'; import { toast } from 'sonner'; import { useTranslation } from 'react-i18next'; import { PluginV4 } from '@/app/infra/entities/plugin'; import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext'; import { usePluginInstallTasks } from '@/app/home/plugins/components/plugin-install-task'; enum PluginInstallStatus { ASK_CONFIRM = 'ask_confirm', INSTALLING = 'installing', ERROR = 'error', } export default function MarketplacePage() { const { t } = useTranslation(); if (!systemInfo?.enable_marketplace) { return (

{t('plugins.marketplace')}

); } return ; } function MarketplaceContent() { const { t } = useTranslation(); const { refreshPlugins } = useSidebarData(); const { addTask, setSelectedTaskId, registerOnTaskComplete, unregisterOnTaskComplete, } = usePluginInstallTasks(); const [modalOpen, setModalOpen] = useState(false); const [installInfo, setInstallInfo] = useState>({}); const [pluginInstallStatus, setPluginInstallStatus] = useState(PluginInstallStatus.ASK_CONFIRM); const [installError, setInstallError] = useState(null); async function checkExtensionsLimit(): Promise { const maxExtensions = systemInfo.limitation?.max_extensions ?? -1; if (maxExtensions < 0) return true; try { const [pluginsResp, mcpResp] = await Promise.all([ httpClient.getPlugins(), httpClient.getMCPServers(), ]); const total = (pluginsResp.plugins?.length ?? 0) + (mcpResp.servers?.length ?? 0); if (total >= maxExtensions) { toast.error( t('limitation.maxExtensionsReached', { max: maxExtensions }), ); return false; } } catch { // If we can't check, let backend handle it } return true; } // Register task completion callback for toast and plugin list refresh useEffect(() => { const onComplete = (_taskId: number, success: boolean, error?: string) => { if (success) { toast.success(t('plugins.installSuccess')); refreshPlugins(); } else { toast.error(error || t('plugins.installFailed')); } }; registerOnTaskComplete(onComplete); return () => { unregisterOnTaskComplete(onComplete); }; }, [registerOnTaskComplete, unregisterOnTaskComplete, refreshPlugins, t]); const handleInstallPlugin = useCallback( async (plugin: PluginV4) => { if (!(await checkExtensionsLimit())) return; setInstallInfo({ plugin_author: plugin.author, plugin_name: plugin.name, plugin_version: plugin.latest_version, }); setPluginInstallStatus(PluginInstallStatus.ASK_CONFIRM); setInstallError(null); setModalOpen(true); }, [t], ); function handleModalConfirm() { setPluginInstallStatus(PluginInstallStatus.INSTALLING); const pluginDisplayName = `${installInfo.plugin_author}/${installInfo.plugin_name}`; httpClient .installPluginFromMarketplace( installInfo.plugin_author, installInfo.plugin_name, installInfo.plugin_version, ) .then((resp) => { const taskId = resp.task_id; const taskKey = `marketplace-${taskId}`; addTask({ taskId, pluginName: pluginDisplayName, source: 'marketplace', }); setSelectedTaskId(taskKey); setModalOpen(false); }) .catch((err) => { setInstallError(err.msg); setPluginInstallStatus(PluginInstallStatus.ERROR); }); } return ( <>
{ setModalOpen(open); if (!open) { setInstallError(null); } }} > {t('plugins.installPlugin')} {pluginInstallStatus === PluginInstallStatus.ASK_CONFIRM && (

{t('plugins.askConfirm', { name: installInfo.plugin_name, version: installInfo.plugin_version, })}

)} {pluginInstallStatus === PluginInstallStatus.INSTALLING && (

{t('plugins.installing')}

)} {pluginInstallStatus === PluginInstallStatus.ERROR && (

{t('plugins.installFailed')}

{installError}

)} {pluginInstallStatus === PluginInstallStatus.ASK_CONFIRM && ( <> )} {pluginInstallStatus === PluginInstallStatus.ERROR && ( )}
); }