From 6570f276d235a9d0aebd17b74c64c68c50163c2c Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Fri, 27 Mar 2026 20:39:26 +0800 Subject: [PATCH] feat(web): add plugin install dropdown to sidebar with context-based action dispatch Add '+' dropdown menu to plugins sidebar category with three install options: marketplace, upload local, and install from GitHub. Use shared React context (pendingPluginInstallAction) instead of URL params to reliably trigger install actions across components. Add e.stopPropagation on all DropdownMenuItem handlers to prevent React portal event bubbling from triggering parent SidebarMenuButton navigation. --- .../components/home-sidebar/HomeSidebar.tsx | 162 +++++++++++++++--- .../home-sidebar/SidebarDataContext.tsx | 10 ++ web/src/app/home/plugins/page.tsx | 30 +++- web/src/i18n/locales/en-US.ts | 1 + web/src/i18n/locales/ja-JP.ts | 1 + web/src/i18n/locales/zh-Hans.ts | 1 + web/src/i18n/locales/zh-Hant.ts | 1 + 7 files changed, 177 insertions(+), 29 deletions(-) diff --git a/web/src/app/home/components/home-sidebar/HomeSidebar.tsx b/web/src/app/home/components/home-sidebar/HomeSidebar.tsx index 99dbc07d..725a7d00 100644 --- a/web/src/app/home/components/home-sidebar/HomeSidebar.tsx +++ b/web/src/app/home/components/home-sidebar/HomeSidebar.tsx @@ -24,6 +24,9 @@ import { ExternalLink, Trash, Bug, + Upload, + Store, + Github, } from 'lucide-react'; import { useTheme } from 'next-themes'; @@ -135,6 +138,7 @@ const CREATABLE_CATEGORIES: EntityCategoryId[] = [ 'pipelines', 'knowledge', 'mcp', + 'plugins', ]; // Categories where clicking the parent only toggles collapse (no list page) @@ -243,6 +247,7 @@ function NavItems({ const pathname = usePathname(); const searchParams = useSearchParams(); const sidebarData = useSidebarData(); + const { setPendingPluginInstallAction } = sidebarData; const { state: sidebarState, isMobile } = useSidebar(); const { t } = useTranslation(); // Track which entity categories have their full list expanded @@ -601,21 +606,78 @@ function NavItems({ >
{config.name} - {canCreate && ( - - )} + {canCreate && + (isPlugin ? ( + + + + + + {systemInfo.enable_marketplace && ( + { + e.stopPropagation(); + router.push('/home/market'); + setPopoverOpen((prev) => ({ + ...prev, + [config.id]: false, + })); + }} + > + + {t('plugins.goToMarketplace')} + + )} + { + e.stopPropagation(); + setPendingPluginInstallAction('local'); + router.push('/home/plugins'); + setPopoverOpen((prev) => ({ + ...prev, + [config.id]: false, + })); + }} + > + + {t('plugins.uploadLocal')} + + { + e.stopPropagation(); + setPendingPluginInstallAction('github'); + router.push('/home/plugins'); + setPopoverOpen((prev) => ({ + ...prev, + [config.id]: false, + })); + }} + > + + {t('plugins.installFromGithub')} + + + + ) : ( + + ))}
{renderEntityList(true)} @@ -651,18 +713,64 @@ function NavItems({ {config.icon} {config.name}
- {canCreate && ( - - )} + {canCreate && + (isPlugin ? ( + + + + + + {systemInfo.enable_marketplace && ( + { + e.stopPropagation(); + router.push('/home/market'); + }} + > + + {t('plugins.goToMarketplace')} + + )} + { + e.stopPropagation(); + setPendingPluginInstallAction('local'); + router.push('/home/plugins'); + }} + > + + {t('plugins.uploadLocal')} + + { + e.stopPropagation(); + setPendingPluginInstallAction('github'); + router.push('/home/plugins'); + }} + > + + {t('plugins.installFromGithub')} + + + + ) : ( + + ))}