'use client' import type { Dependency, PluginDeclaration, PluginManifestInMarket } from '../types' import { RiBookOpenLine, RiDragDropLine, RiEqualizer2Line, } from '@remixicon/react' import { useBoolean } from 'ahooks' import { noop } from 'es-toolkit/function' import Link from 'next/link' import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Button from '@/app/components/base/button' import TabSlider from '@/app/components/base/tab-slider' import Tooltip from '@/app/components/base/tooltip' import ReferenceSettingModal from '@/app/components/plugins/reference-setting-modal/modal' import { getDocsUrl } from '@/app/components/plugins/utils' import { MARKETPLACE_API_PREFIX, SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config' import { useGlobalPublicStore } from '@/context/global-public-context' import I18n from '@/context/i18n' import useDocumentTitle from '@/hooks/use-document-title' import { usePluginInstallation } from '@/hooks/use-query-params' import { fetchBundleInfoFromMarketPlace, fetchManifestFromMarketPlace } from '@/service/plugins' import { sleep } from '@/utils' import { cn } from '@/utils/classnames' import { PLUGIN_PAGE_TABS_MAP } from '../hooks' import InstallFromLocalPackage from '../install-plugin/install-from-local-package' import InstallFromMarketplace from '../install-plugin/install-from-marketplace' import { PLUGIN_TYPE_SEARCH_MAP } from '../marketplace/plugin-type-switch' import { PluginPageContextProvider, usePluginPageContext, } from './context' import DebugInfo from './debug-info' import InstallPluginDropdown from './install-plugin-dropdown' import PluginTasks from './plugin-tasks' import useReferenceSetting from './use-reference-setting' import { useUploader } from './use-uploader' export type PluginPageProps = { plugins: React.ReactNode marketplace: React.ReactNode } const PluginPage = ({ plugins, marketplace, }: PluginPageProps) => { const { t } = useTranslation() const { locale } = useContext(I18n) useDocumentTitle(t('metadata.title', { ns: 'plugin' })) // Use nuqs hook for installation state const [{ packageId, bundleInfo }, setInstallState] = usePluginInstallation() const [uniqueIdentifier, setUniqueIdentifier] = useState(null) const [dependencies, setDependencies] = useState([]) const [isShowInstallFromMarketplace, { setTrue: showInstallFromMarketplace, setFalse: doHideInstallFromMarketplace, }] = useBoolean(false) const hideInstallFromMarketplace = () => { doHideInstallFromMarketplace() setInstallState(null) } const [manifest, setManifest] = useState(null) useEffect(() => { (async () => { setUniqueIdentifier(null) await sleep(100) if (packageId) { const { data } = await fetchManifestFromMarketPlace(encodeURIComponent(packageId)) const { plugin, version } = data setManifest({ ...plugin, version: version.version, icon: `${MARKETPLACE_API_PREFIX}/plugins/${plugin.org}/${plugin.name}/icon`, }) setUniqueIdentifier(packageId) showInstallFromMarketplace() return } if (bundleInfo) { try { const { data } = await fetchBundleInfoFromMarketPlace(bundleInfo) setDependencies(data.version.dependencies) showInstallFromMarketplace() } catch (error) { console.error('Failed to load bundle info:', error) } } })() }, [packageId, bundleInfo, showInstallFromMarketplace]) const { referenceSetting, canManagement, canDebugger, canSetPermissions, setReferenceSettings, } = useReferenceSetting() const [showPluginSettingModal, { setTrue: setShowPluginSettingModal, setFalse: setHidePluginSettingModal, }] = useBoolean(false) const [currentFile, setCurrentFile] = useState(null) const containerRef = usePluginPageContext(v => v.containerRef) const options = usePluginPageContext(v => v.options) const activeTab = usePluginPageContext(v => v.activeTab) const setActiveTab = usePluginPageContext(v => v.setActiveTab) const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) const isPluginsTab = useMemo(() => activeTab === PLUGIN_PAGE_TABS_MAP.plugins, [activeTab]) const isExploringMarketplace = useMemo(() => { const values = Object.values(PLUGIN_TYPE_SEARCH_MAP) return activeTab === PLUGIN_PAGE_TABS_MAP.marketplace || values.includes(activeTab) }, [activeTab]) const handleFileChange = (file: File | null) => { if (!file || !file.name.endsWith('.difypkg')) { setCurrentFile(null) return } setCurrentFile(file) } const uploaderProps = useUploader({ onFileChange: handleFileChange, containerRef, enabled: isPluginsTab && canManagement, }) const { dragging, fileUploader, fileChangeHandle, removeFile } = uploaderProps return (
{ isExploringMarketplace && ( <>
) } {canManagement && ( setActiveTab('discover')} /> )} { canDebugger && ( ) } { canSetPermissions && ( ) }
{isPluginsTab && ( <> {plugins} {dragging && (
)}
{t('installModal.dropPluginToInstall', { ns: 'plugin' })}
{currentFile && ( )} )} { isExploringMarketplace && enable_marketplace && marketplace } {showPluginSettingModal && ( )} { isShowInstallFromMarketplace && uniqueIdentifier && ( ) }
) } const PluginPageWithContext = (props: PluginPageProps) => { return ( ) } export default PluginPageWithContext