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.
This commit is contained in:
Junyan Qin
2026-03-27 20:39:26 +08:00
parent 42e1e038bd
commit 6570f276d2
7 changed files with 177 additions and 29 deletions
+28 -2
View File
@@ -94,7 +94,11 @@ export default function PluginConfigPage() {
function PluginListView() {
const { t } = useTranslation();
const router = useRouter();
const { refreshPlugins } = useSidebarData();
const {
refreshPlugins,
pendingPluginInstallAction,
setPendingPluginInstallAction,
} = useSidebarData();
const [modalOpen, setModalOpen] = useState(false);
const [installSource, setInstallSource] = useState<string>('local');
const [installInfo] = useState<Record<string, any>>({}); // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -408,6 +412,28 @@ function PluginListView() {
[uploadPluginFile, isPluginSystemReady, t],
);
// Auto-trigger install action from sidebar via shared context
useEffect(() => {
if (!pendingPluginInstallAction || statusLoading || !isPluginSystemReady)
return;
// Consume the action immediately
const action = pendingPluginInstallAction;
setPendingPluginInstallAction(null);
if (action === 'local') {
// Small delay to ensure file input ref is ready
setTimeout(() => fileInputRef.current?.click(), 100);
} else if (action === 'github') {
setInstallSource('github');
setPluginInstallStatus(PluginInstallStatus.WAIT_INPUT);
setInstallError(null);
resetGithubState();
setModalOpen(true);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pendingPluginInstallAction, statusLoading, isPluginSystemReady]);
const handleShowDebugInfo = async () => {
try {
const info = await httpClient.getPluginDebugInfo();
@@ -627,7 +653,7 @@ function PluginListView() {
}}
>
<StoreIcon className="w-4 h-4" />
{t('plugins.marketplace')}
{t('plugins.goToMarketplace')}
</DropdownMenuItem>
)}
<DropdownMenuItem onClick={handleFileSelect}>