From b01c18ae7f1065d3ac839ebdcadbda7f2bac0904 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Thu, 21 Nov 2024 14:15:49 +0800 Subject: [PATCH 01/12] fix: version badge --- .../components/plugins/plugin-detail-panel/detail-header.tsx | 4 ++-- .../plugins/update-plugin/plugin-version-picker.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx index 0127c0059f..440b5de320 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx @@ -183,13 +183,13 @@ const DetailHeader = ({ className={cn( 'mx-1', isShow && 'bg-state-base-hover', - (isShow || isFromMarketplace) && 'hover:bg-state-base-hover', + (isShow || isFromMarketplace) && hasNewVersion && 'hover:bg-state-base-hover', )} uppercase={false} text={ <>
{isFromGitHub ? meta!.version : version}
- {isFromMarketplace && } + {isFromMarketplace && hasNewVersion && } } hasRedCornerMark={hasNewVersion} diff --git a/web/app/components/plugins/update-plugin/plugin-version-picker.tsx b/web/app/components/plugins/update-plugin/plugin-version-picker.tsx index 62f8f85233..b05ddc0062 100644 --- a/web/app/components/plugins/update-plugin/plugin-version-picker.tsx +++ b/web/app/components/plugins/update-plugin/plugin-version-picker.tsx @@ -67,7 +67,7 @@ const PluginVersionPicker: FC = ({ return onSelect({ version, unique_identifier }) onShowChange(false) - }, [currentVersion, onSelect]) + }, [currentVersion, onSelect, onShowChange]) return ( Date: Thu, 21 Nov 2024 14:24:43 +0800 Subject: [PATCH 02/12] feat: download package --- .../market-place-plugin/action.tsx | 27 +++++++++++++++++-- .../market-place-plugin/item.tsx | 1 + web/service/fetch.ts | 3 ++- web/service/use-plugins.ts | 9 +++++++ web/utils/format.ts | 11 ++++++++ 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/web/app/components/workflow/block-selector/market-place-plugin/action.tsx b/web/app/components/workflow/block-selector/market-place-plugin/action.tsx index 6f0c08eeca..f4a9668c3e 100644 --- a/web/app/components/workflow/block-selector/market-place-plugin/action.tsx +++ b/web/app/components/workflow/block-selector/market-place-plugin/action.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useRef } from 'react' +import React, { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { RiMoreFill } from '@remixicon/react' import ActionButton from '@/app/components/base/action-button' @@ -12,12 +12,15 @@ import { } from '@/app/components/base/portal-to-follow-elem' import cn from '@/utils/classnames' import { MARKETPLACE_URL_PREFIX } from '@/config' +import { useDownloadPlugin } from '@/service/use-plugins' +import { downloadFile } from '@/utils/format' type Props = { open: boolean onOpenChange: (v: boolean) => void author: string name: string + version: string } const OperationDropdown: FC = ({ @@ -25,6 +28,7 @@ const OperationDropdown: FC = ({ onOpenChange, author, name, + version, }) => { const { t } = useTranslation() const openRef = useRef(open) @@ -37,6 +41,25 @@ const OperationDropdown: FC = ({ setOpen(!openRef.current) }, [setOpen]) + const [needDownload, setNeedDownload] = useState(false) + const { data: blob, isLoading } = useDownloadPlugin({ + organization: author, + pluginName: name, + version, + }, needDownload) + const handleDownload = useCallback(() => { + if (isLoading) return + setNeedDownload(true) + }, [isLoading]) + + useEffect(() => { + if (blob) { + const fileName = `${author}-${name}_${version}.zip` + downloadFile({ data: blob, fileName }) + setNeedDownload(false) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [blob]) return ( = ({
-
{t('common.operation.download')}
+
{t('common.operation.download')}
{t('common.operation.viewDetails')}
diff --git a/web/app/components/workflow/block-selector/market-place-plugin/item.tsx b/web/app/components/workflow/block-selector/market-place-plugin/item.tsx index 7f2ae34083..ebe4da73f8 100644 --- a/web/app/components/workflow/block-selector/market-place-plugin/item.tsx +++ b/web/app/components/workflow/block-selector/market-place-plugin/item.tsx @@ -59,6 +59,7 @@ const Item: FC = ({ onOpenChange={setOpen} author={payload.org} name={payload.name} + version={payload.latest_version} /> {isShowInstallModal && ( diff --git a/web/service/fetch.ts b/web/service/fetch.ts index 666a3e2336..39f91f903f 100644 --- a/web/service/fetch.ts +++ b/web/service/fetch.ts @@ -12,6 +12,7 @@ export const ContentType = { audio: 'audio/mpeg', form: 'application/x-www-form-urlencoded; charset=UTF-8', download: 'application/octet-stream', // for download + downloadZip: 'application/zip', // for download upload: 'multipart/form-data', // for upload } @@ -193,7 +194,7 @@ async function base(url: string, options: FetchOptionType = {}, otherOptions: const contentType = res.headers.get('content-type') if ( contentType - && [ContentType.download, ContentType.audio].includes(contentType) + && [ContentType.download, ContentType.audio, ContentType.downloadZip].includes(contentType) ) return await res.blob() as T diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts index 0c05290e0f..78c4ff770f 100644 --- a/web/service/use-plugins.ts +++ b/web/service/use-plugins.ts @@ -344,3 +344,12 @@ export const useMutationCheckDependenciesBeforeImportDSL = () => { return mutation } + +export const useDownloadPlugin = (info: { organization: string; pluginName: string; version: string }, needDownload: boolean) => { + return useQuery({ + queryKey: [NAME_SPACE, 'downloadPlugin', info], + queryFn: () => getMarketplace(`/plugins/${info.organization}/${info.pluginName}/${info.version}/download`), + enabled: needDownload, + retry: 0, + }) +} diff --git a/web/utils/format.ts b/web/utils/format.ts index 1eeb6af807..45f0e51878 100644 --- a/web/utils/format.ts +++ b/web/utils/format.ts @@ -34,3 +34,14 @@ export const formatTime = (num: number) => { } return `${num.toFixed(2)} ${units[index]}` } + +export const downloadFile = ({ data, fileName }: { data: Blob; fileName: string }) => { + const url = window.URL.createObjectURL(data) + const a = document.createElement('a') + a.href = url + a.download = fileName + document.body.appendChild(a) + a.click() + a.remove() + window.URL.revokeObjectURL(url) +} From 351615fb9873d84493344cc97e1b0acf64163bdb Mon Sep 17 00:00:00 2001 From: Joel Date: Thu, 21 Nov 2024 15:14:21 +0800 Subject: [PATCH 03/12] fix: not the first time upload bundle error --- .../install-bundle/steps/install-multi.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx index 4ae0bccdab..07119357aa 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx @@ -6,7 +6,6 @@ import MarketplaceItem from '../item/marketplace-item' import GithubItem from '../item/github-item' import { useFetchPluginsInMarketPlaceByIds, useFetchPluginsInMarketPlaceByInfo } from '@/service/use-plugins' import produce from 'immer' -import { useGetState } from 'ahooks' import PackageItem from '../item/package-item' import LoadingError from '../../base/loading-error' @@ -25,7 +24,7 @@ const InstallByDSLList: FC = ({ }) => { const { isLoading: isFetchingMarketplaceDataFromDSL, data: marketplaceFromDSLRes } = useFetchPluginsInMarketPlaceByIds(allPlugins.filter(d => d.type === 'marketplace').map(d => (d as GitHubItemAndMarketPlaceDependency).value.plugin_unique_identifier!)) const { isLoading: isFetchingMarketplaceDataFromLocal, data: marketplaceResFromLocalRes } = useFetchPluginsInMarketPlaceByInfo(allPlugins.filter(d => d.type === 'marketplace').map(d => (d as GitHubItemAndMarketPlaceDependency).value!)) - const [plugins, setPlugins, getPlugins] = useGetState<(Plugin | undefined)[]>((() => { + const [plugins, doSetPlugins] = useState<(Plugin | undefined)[]>((() => { const hasLocalPackage = allPlugins.some(d => d.type === 'package') if (!hasLocalPackage) return [] @@ -42,17 +41,23 @@ const InstallByDSLList: FC = ({ }) return _plugins })()) + const pluginsRef = React.useRef<(Plugin | undefined)[]>(plugins) + + const setPlugins = useCallback((p: (Plugin | undefined)[]) => { + doSetPlugins(p) + pluginsRef.current = p + }, []) const [errorIndexes, setErrorIndexes] = useState([]) const handleGitHubPluginFetched = useCallback((index: number) => { return (p: Plugin) => { - const nextPlugins = produce(getPlugins(), (draft) => { + const nextPlugins = produce(pluginsRef.current, (draft) => { draft[index] = p }) setPlugins(nextPlugins) } - }, [getPlugins, setPlugins]) + }, [setPlugins]) const handleGitHubPluginFetchError = useCallback((index: number) => { return () => { @@ -73,7 +78,7 @@ const InstallByDSLList: FC = ({ if (!isFetchingMarketplaceDataFromDSL && marketplaceFromDSLRes?.data.plugins) { const payloads = marketplaceFromDSLRes?.data.plugins const failedIndex: number[] = [] - const nextPlugins = produce(getPlugins(), (draft) => { + const nextPlugins = produce(pluginsRef.current, (draft) => { marketPlaceInDSLIndex.forEach((index, i) => { if (payloads[i]) draft[index] = payloads[i] @@ -82,6 +87,7 @@ const InstallByDSLList: FC = ({ }) }) setPlugins(nextPlugins) + if (failedIndex.length > 0) setErrorIndexes([...errorIndexes, ...failedIndex]) } @@ -92,7 +98,7 @@ const InstallByDSLList: FC = ({ if (!isFetchingMarketplaceDataFromLocal && marketplaceResFromLocalRes?.data.list) { const payloads = marketplaceResFromLocalRes?.data.list const failedIndex: number[] = [] - const nextPlugins = produce(getPlugins(), (draft) => { + const nextPlugins = produce(pluginsRef.current, (draft) => { marketPlaceInDSLIndex.forEach((index, i) => { if (payloads[i]) { const item = payloads[i] From 2560d3edae89343ba8dead72b44b111cec339007 Mon Sep 17 00:00:00 2001 From: Joel Date: Thu, 21 Nov 2024 15:22:19 +0800 Subject: [PATCH 04/12] fix: bundle install title --- .../plugins/install-plugin/install-bundle/index.tsx | 4 +--- .../install-plugin/install-from-local-package/index.tsx | 4 +++- web/i18n/en-US/plugin.ts | 1 + web/i18n/zh-Hans/plugin.ts | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/web/app/components/plugins/install-plugin/install-bundle/index.tsx b/web/app/components/plugins/install-plugin/install-bundle/index.tsx index da5a9fbd6a..035de8b781 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/index.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/index.tsx @@ -34,9 +34,7 @@ const InstallBundle: FC = ({ if (step === InstallStep.uploadFailed) return t(`${i18nPrefix}.uploadFailed`) if (step === InstallStep.installed) - return t(`${i18nPrefix}.installedSuccessfully`) - if (step === InstallStep.installFailed) - return t(`${i18nPrefix}.installFailed`) + return t(`${i18nPrefix}.installComplete`) return t(`${i18nPrefix}.installPlugin`) }, [step, t]) diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx index 611a1ad5a1..c126a38a1d 100644 --- a/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx +++ b/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx @@ -34,13 +34,15 @@ const InstallFromLocalPackage: React.FC = ({ const getTitle = useCallback(() => { if (step === InstallStep.uploadFailed) return t(`${i18nPrefix}.uploadFailed`) + if (isBundle && step === InstallStep.installed) + return t(`${i18nPrefix}.installComplete`) if (step === InstallStep.installed) return t(`${i18nPrefix}.installedSuccessfully`) if (step === InstallStep.installFailed) return t(`${i18nPrefix}.installFailed`) return t(`${i18nPrefix}.installPlugin`) - }, [step, t]) + }, [isBundle, step, t]) const { getIconUrl } = useGetIcon() diff --git a/web/i18n/en-US/plugin.ts b/web/i18n/en-US/plugin.ts index bcbb1648f4..88c21c0dd3 100644 --- a/web/i18n/en-US/plugin.ts +++ b/web/i18n/en-US/plugin.ts @@ -94,6 +94,7 @@ const translation = { }, installModal: { installPlugin: 'Install Plugin', + installComplete: 'Installation complete', installedSuccessfully: 'Installation successful', installedSuccessfullyDesc: 'The plugin has been installed successfully.', uploadFailed: 'Upload failed', diff --git a/web/i18n/zh-Hans/plugin.ts b/web/i18n/zh-Hans/plugin.ts index b293f99f8f..94e1324bed 100644 --- a/web/i18n/zh-Hans/plugin.ts +++ b/web/i18n/zh-Hans/plugin.ts @@ -94,6 +94,7 @@ const translation = { }, installModal: { installPlugin: '安装插件', + installComplete: '安装完成', installedSuccessfully: '安装成功', installedSuccessfullyDesc: '插件已成功安装。', uploadFailed: '上传失败', From 56f573ecfbbb34ba3370e98542e212775f1c1bcf Mon Sep 17 00:00:00 2001 From: JzoNg Date: Thu, 21 Nov 2024 15:35:24 +0800 Subject: [PATCH 05/12] fix: update plugin handle --- .../plugin-detail-panel/action-list.tsx | 19 ++++++++++------ .../plugin-detail-panel/endpoint-list.tsx | 18 +++++++-------- .../plugins/plugin-detail-panel/index.tsx | 22 ++++++++++--------- .../plugin-detail-panel/model-list.tsx | 13 +++++++---- .../components/plugins/plugin-item/index.tsx | 8 +++---- .../plugins/plugin-page/context.tsx | 15 ++++++------- .../plugins/plugin-page/plugins-panel.tsx | 10 +++++++-- 7 files changed, 61 insertions(+), 44 deletions(-) diff --git a/web/app/components/plugins/plugin-detail-panel/action-list.tsx b/web/app/components/plugins/plugin-detail-panel/action-list.tsx index 609e7d1306..2d440c2702 100644 --- a/web/app/components/plugins/plugin-detail-panel/action-list.tsx +++ b/web/app/components/plugins/plugin-detail-panel/action-list.tsx @@ -1,6 +1,5 @@ import React, { useState } from 'react' import { useTranslation } from 'react-i18next' -import { usePluginPageContext } from '@/app/components/plugins/plugin-page/context' import { useAppContext } from '@/context/app-context' import Button from '@/app/components/base/button' import Toast from '@/app/components/base/toast' @@ -14,19 +13,25 @@ import { useRemoveProviderCredentials, useUpdateProviderCredentials, } from '@/service/use-tools' +import type { PluginDetail } from '@/app/components/plugins/types' -const ActionList = () => { +type Props = { + detail: PluginDetail +} + +const ActionList = ({ + detail, +}: Props) => { const { t } = useTranslation() const { isCurrentWorkspaceManager } = useAppContext() - const currentPluginDetail = usePluginPageContext(v => v.currentPluginDetail) - const { data: provider } = useBuiltinProviderInfo(`${currentPluginDetail.plugin_id}/${currentPluginDetail.name}`) + const { data: provider } = useBuiltinProviderInfo(`${detail.plugin_id}/${detail.name}`) const invalidateProviderInfo = useInvalidateBuiltinProviderInfo() - const { data } = useBuiltinTools(`${currentPluginDetail.plugin_id}/${currentPluginDetail.name}`) + const { data } = useBuiltinTools(`${detail.plugin_id}/${detail.name}`) const [showSettingAuth, setShowSettingAuth] = useState(false) const handleCredentialSettingUpdate = () => { - invalidateProviderInfo(`${currentPluginDetail.plugin_id}/${currentPluginDetail.name}`) + invalidateProviderInfo(`${detail.plugin_id}/${detail.name}`) Toast.notify({ type: 'success', message: t('common.api.actionSuccess'), @@ -74,7 +79,7 @@ const ActionList = () => {
{data.map(tool => ( { +const EndpointList = ({ detail }: Props) => { const { t } = useTranslation() - const pluginDetail = usePluginPageContext(v => v.currentPluginDetail) - const pluginUniqueID = pluginDetail.plugin_unique_identifier - const declaration = pluginDetail.declaration.endpoint - const { data } = useEndpointList(pluginDetail.plugin_id) + const pluginUniqueID = detail.plugin_unique_identifier + const declaration = detail.declaration.endpoint + const showTopBorder = detail.declaration.tool + const { data } = useEndpointList(detail.plugin_id) const invalidateEndpointList = useInvalidateEndpointList() const [isShowEndpointModal, { @@ -43,7 +43,7 @@ const EndpointList = ({ showTopBorder }: Props) => { const { mutate: createEndpoint } = useCreateEndpoint({ onSuccess: async () => { - await invalidateEndpointList(pluginDetail.plugin_id) + await invalidateEndpointList(detail.plugin_id) hideEndpointModal() }, onError: () => { @@ -101,7 +101,7 @@ const EndpointList = ({ showTopBorder }: Props) => { invalidateEndpointList(pluginDetail.plugin_id)} + handleChange={() => invalidateEndpointList(detail.plugin_id)} /> ))}
diff --git a/web/app/components/plugins/plugin-detail-panel/index.tsx b/web/app/components/plugins/plugin-detail-panel/index.tsx index cda554099b..c9e4213137 100644 --- a/web/app/components/plugins/plugin-detail-panel/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/index.tsx @@ -7,19 +7,21 @@ import ActionList from './action-list' import ModelList from './model-list' import Drawer from '@/app/components/base/drawer' import { usePluginPageContext } from '@/app/components/plugins/plugin-page/context' +import type { PluginDetail } from '@/app/components/plugins/types' import cn from '@/utils/classnames' type Props = { + detail?: PluginDetail onUpdate: () => void } const PluginDetailPanel: FC = ({ + detail, onUpdate, }) => { - const pluginDetail = usePluginPageContext(v => v.currentPluginDetail) - const setCurrentPluginDetail = usePluginPageContext(v => v.setCurrentPluginDetail) + const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID) - const handleHide = () => setCurrentPluginDetail(undefined) + const handleHide = () => setCurrentPluginID(undefined) const handleUpdate = (isDelete = false) => { if (isDelete) @@ -27,12 +29,12 @@ const PluginDetailPanel: FC = ({ onUpdate() } - if (!pluginDetail) + if (!detail) return null return ( = ({ positionCenter={false} panelClassname={cn('justify-start mt-[64px] mr-2 mb-2 !w-[420px] !max-w-[420px] !p-0 !bg-components-panel-bg rounded-2xl border-[0.5px] border-components-panel-border shadow-xl')} > - {pluginDetail && ( + {detail && ( <>
- {!!pluginDetail.declaration.tool && } - {!!pluginDetail.declaration.endpoint && } - {!!pluginDetail.declaration.model && } + {!!detail.declaration.tool && } + {!!detail.declaration.endpoint && } + {!!detail.declaration.model && }
)} diff --git a/web/app/components/plugins/plugin-detail-panel/model-list.tsx b/web/app/components/plugins/plugin-detail-panel/model-list.tsx index 7592126867..5989a75945 100644 --- a/web/app/components/plugins/plugin-detail-panel/model-list.tsx +++ b/web/app/components/plugins/plugin-detail-panel/model-list.tsx @@ -1,14 +1,19 @@ import React from 'react' import { useTranslation } from 'react-i18next' -import { usePluginPageContext } from '@/app/components/plugins/plugin-page/context' import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon' import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name' import { useModelProviderModelList } from '@/service/use-models' +import type { PluginDetail } from '@/app/components/plugins/types' -const ModelList = () => { +type Props = { + detail: PluginDetail +} + +const ModelList = ({ + detail, +}: Props) => { const { t } = useTranslation() - const currentPluginDetail = usePluginPageContext(v => v.currentPluginDetail) - const { data: res } = useModelProviderModelList(`${currentPluginDetail.plugin_id}/${currentPluginDetail.name}`) + const { data: res } = useModelProviderModelList(`${detail.plugin_id}/${detail.name}`) if (!res) return null diff --git a/web/app/components/plugins/plugin-item/index.tsx b/web/app/components/plugins/plugin-item/index.tsx index 13c8797358..d8bc72c158 100644 --- a/web/app/components/plugins/plugin-item/index.tsx +++ b/web/app/components/plugins/plugin-item/index.tsx @@ -37,8 +37,8 @@ const PluginItem: FC = ({ const locale = useLanguage() const { t } = useTranslation() const { categoriesMap } = useCategories() - const currentPluginDetail = usePluginPageContext(v => v.currentPluginDetail) - const setCurrentPluginDetail = usePluginPageContext(v => v.setCurrentPluginDetail) + const currentPluginID = usePluginPageContext(v => v.currentPluginID) + const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID) const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const { refreshModelProviders } = useProviderContext() @@ -66,13 +66,13 @@ const PluginItem: FC = ({
{ - setCurrentPluginDetail(plugin) + setCurrentPluginID(plugin.plugin_id) }} >
diff --git a/web/app/components/plugins/plugin-page/context.tsx b/web/app/components/plugins/plugin-page/context.tsx index e9e66849e9..6363bcae69 100644 --- a/web/app/components/plugins/plugin-page/context.tsx +++ b/web/app/components/plugins/plugin-page/context.tsx @@ -11,15 +11,14 @@ import { useContextSelector, } from 'use-context-selector' import { useSelector as useAppContextSelector } from '@/context/app-context' -import type { PluginDetail } from '../types' import type { FilterState } from './filter-management' import { useTranslation } from 'react-i18next' import { useTabSearchParams } from '@/hooks/use-tab-searchparams' export type PluginPageContextValue = { containerRef: React.RefObject - currentPluginDetail: PluginDetail | undefined - setCurrentPluginDetail: (plugin: PluginDetail) => void + currentPluginID: string | undefined + setCurrentPluginID: (pluginID?: string) => void filters: FilterState setFilters: (filter: FilterState) => void activeTab: string @@ -29,8 +28,8 @@ export type PluginPageContextValue = { export const PluginPageContext = createContext({ containerRef: { current: null }, - currentPluginDetail: undefined, - setCurrentPluginDetail: () => { }, + currentPluginID: undefined, + setCurrentPluginID: () => { }, filters: { categories: [], tags: [], @@ -60,7 +59,7 @@ export const PluginPageContextProvider = ({ tags: [], searchQuery: '', }) - const [currentPluginDetail, setCurrentPluginDetail] = useState() + const [currentPluginID, setCurrentPluginID] = useState() const { enable_marketplace } = useAppContextSelector(s => s.systemFeatures) const options = useMemo(() => { @@ -81,8 +80,8 @@ export const PluginPageContextProvider = ({ { const [filters, setFilters] = usePluginPageContext(v => [v.filters, v.setFilters]) as [FilterState, (filter: FilterState) => void] const { data: pluginList, isLoading: isPluginListLoading } = useInstalledPluginList() const invalidateInstalledPluginList = useInvalidateInstalledPluginList() + const currentPluginID = usePluginPageContext(v => v.currentPluginID) const { run: handleFilterChange } = useDebounceFn((filters: FilterState) => { setFilters(filters) @@ -31,6 +32,11 @@ const PluginsPanel = () => { return filteredList }, [pluginList, filters]) + const currentPluginDetail = useMemo(() => { + const detail = pluginList?.plugins.find(plugin => plugin.plugin_id === currentPluginID) + return detail + }, [currentPluginID, pluginList?.plugins]) + return ( <>
@@ -40,7 +46,7 @@ const PluginsPanel = () => { />
{isPluginListLoading ? : (filteredList?.length ?? 0) > 0 ? ( -
+
@@ -48,7 +54,7 @@ const PluginsPanel = () => { ) : ( )} - invalidateInstalledPluginList()}/> + invalidateInstalledPluginList()}/> ) } From ac42ba880a2b399394f13f3121593a1a49569a11 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Thu, 21 Nov 2024 17:11:22 +0800 Subject: [PATCH 06/12] fix: install handle of list refresh --- .../plugins/install-plugin/base/installed.tsx | 5 ----- .../install-from-github/index.tsx | 18 ++++++++++++++---- .../ready-to-install.tsx | 17 +++++++++++++---- .../install-from-marketplace/index.tsx | 16 +++++++++++++--- .../plugin-detail-panel/detail-header.tsx | 8 ++++++-- .../components/plugins/plugin-item/index.tsx | 6 +++++- web/service/use-tools.ts | 4 ++++ 7 files changed, 55 insertions(+), 19 deletions(-) diff --git a/web/app/components/plugins/install-plugin/base/installed.tsx b/web/app/components/plugins/install-plugin/base/installed.tsx index eba50a6b21..efe8d4af76 100644 --- a/web/app/components/plugins/install-plugin/base/installed.tsx +++ b/web/app/components/plugins/install-plugin/base/installed.tsx @@ -4,8 +4,6 @@ import React from 'react' import { useTranslation } from 'react-i18next' import Card from '../../card' import Button from '@/app/components/base/button' -import { useUpdateModelProviders } from '@/app/components/header/account-setting/model-provider-page/hooks' -import { PluginType } from '../../types' import type { Plugin, PluginDeclaration, PluginManifestInMarket } from '../../types' import { pluginManifestInMarketToPluginProps, pluginManifestToCardPluginProps } from '../utils' import Badge, { BadgeState } from '@/app/components/base/badge/index' @@ -26,12 +24,9 @@ const Installed: FC = ({ onCancel, }) => { const { t } = useTranslation() - const updateModelProviders = useUpdateModelProviders() const handleClose = () => { onCancel() - if (payload?.category === PluginType.model) - updateModelProviders() } return ( <> diff --git a/web/app/components/plugins/install-plugin/install-from-github/index.tsx b/web/app/components/plugins/install-plugin/install-from-github/index.tsx index c74071e808..d55ea4de85 100644 --- a/web/app/components/plugins/install-plugin/install-from-github/index.tsx +++ b/web/app/components/plugins/install-plugin/install-from-github/index.tsx @@ -7,7 +7,7 @@ import type { InstallState } from '@/app/components/plugins/types' import { useGitHubReleases } from '../hooks' import { convertRepoToUrl, parseGitHubUrl } from '../utils' import type { PluginDeclaration, UpdateFromGitHubPayload } from '../../types' -import { InstallStepFromGitHub } from '../../types' +import { InstallStepFromGitHub, PluginType } from '../../types' import Toast from '@/app/components/base/toast' import SetURL from './steps/setURL' import SelectPackage from './steps/selectPackage' @@ -15,6 +15,8 @@ import Installed from '../base/installed' import Loaded from './steps/loaded' import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' import { useTranslation } from 'react-i18next' +import { useUpdateModelProviders } from '@/app/components/header/account-setting/model-provider-page/hooks' +import { useInvalidateAllToolProviders } from '@/service/use-tools' const i18nPrefix = 'plugin.installFromGitHub' @@ -28,6 +30,8 @@ const InstallFromGitHub: React.FC = ({ updatePayload, on const { t } = useTranslation() const { getIconUrl } = useGetIcon() const { fetchReleases } = useGitHubReleases() + const updateModelProviders = useUpdateModelProviders() + const invalidateAllToolProviders = useInvalidateAllToolProviders() const [state, setState] = useState({ step: updatePayload ? InstallStepFromGitHub.selectPackage : InstallStepFromGitHub.setUrl, repoUrl: updatePayload?.originalPackageInfo?.repo @@ -63,7 +67,7 @@ const InstallFromGitHub: React.FC = ({ updatePayload, on return t(`${i18nPrefix}.installFailed`) return updatePayload ? t(`${i18nPrefix}.updatePlugin`) : t(`${i18nPrefix}.installPlugin`) - }, [state.step]) + }, [state.step, t, updatePayload]) const handleUrlSubmit = async () => { const { isValid, owner, repo } = parseGitHubUrl(state.repoUrl) @@ -111,8 +115,14 @@ const InstallFromGitHub: React.FC = ({ updatePayload, on const handleInstalled = useCallback(() => { setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.installed })) + if (!manifest) + return + if (PluginType.model.includes(manifest.category)) + updateModelProviders() + if (PluginType.tool.includes(manifest.category)) + invalidateAllToolProviders() onSuccess() - }, [onSuccess]) + }, [invalidateAllToolProviders, manifest, onSuccess, updateModelProviders]) const handleFailed = useCallback((errorMsg?: string) => { setState(prevState => ({ ...prevState, step: InstallStepFromGitHub.installFailed })) @@ -142,7 +152,7 @@ const InstallFromGitHub: React.FC = ({ updatePayload, on closable >
-
+
{getTitle()}
diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/ready-to-install.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/ready-to-install.tsx index 9f81b1e918..f41ecd3469 100644 --- a/web/app/components/plugins/install-plugin/install-from-local-package/ready-to-install.tsx +++ b/web/app/components/plugins/install-plugin/install-from-local-package/ready-to-install.tsx @@ -2,11 +2,12 @@ import type { FC } from 'react' import React, { useCallback } from 'react' import type { PluginDeclaration } from '../../types' -import { InstallStep } from '../../types' +import { InstallStep, PluginType } from '../../types' import Install from './steps/install' import Installed from '../base/installed' import { useInvalidateInstalledPluginList } from '@/service/use-plugins' - +import { useUpdateModelProviders } from '@/app/components/header/account-setting/model-provider-page/hooks' +import { useInvalidateAllToolProviders } from '@/service/use-tools' type Props = { step: InstallStep onStepChange: (step: InstallStep) => void, @@ -27,11 +28,19 @@ const ReadyToInstall: FC = ({ onError, }) => { const invalidateInstalledPluginList = useInvalidateInstalledPluginList() + const updateModelProviders = useUpdateModelProviders() + const invalidateAllToolProviders = useInvalidateAllToolProviders() const handleInstalled = useCallback(() => { - invalidateInstalledPluginList() onStepChange(InstallStep.installed) - }, [invalidateInstalledPluginList, onStepChange]) + invalidateInstalledPluginList() + if (!manifest) + return + if (PluginType.model.includes(manifest.category)) + updateModelProviders() + if (PluginType.tool.includes(manifest.category)) + invalidateAllToolProviders() + }, [invalidateAllToolProviders, invalidateInstalledPluginList, manifest, onStepChange, updateModelProviders]) const handleFailed = useCallback((errorMsg?: string) => { onStepChange(InstallStep.installFailed) diff --git a/web/app/components/plugins/install-plugin/install-from-marketplace/index.tsx b/web/app/components/plugins/install-plugin/install-from-marketplace/index.tsx index ad5b596b73..553c30f6be 100644 --- a/web/app/components/plugins/install-plugin/install-from-marketplace/index.tsx +++ b/web/app/components/plugins/install-plugin/install-from-marketplace/index.tsx @@ -3,10 +3,13 @@ import React, { useCallback, useState } from 'react' import Modal from '@/app/components/base/modal' import type { Plugin, PluginManifestInMarket } from '../../types' -import { InstallStep } from '../../types' +import { InstallStep, PluginType } from '../../types' import Install from './steps/install' import Installed from '../base/installed' import { useTranslation } from 'react-i18next' +import { useUpdateModelProviders } from '@/app/components/header/account-setting/model-provider-page/hooks' +import { useInvalidateInstalledPluginList } from '@/service/use-plugins' +import { useInvalidateAllToolProviders } from '@/service/use-tools' const i18nPrefix = 'plugin.installModal' @@ -27,7 +30,9 @@ const InstallFromMarketplace: React.FC = ({ // readyToInstall -> check installed -> installed/failed const [step, setStep] = useState(InstallStep.readyToInstall) const [errorMsg, setErrorMsg] = useState(null) - + const updateModelProviders = useUpdateModelProviders() + const invalidateAllToolProviders = useInvalidateAllToolProviders() + const invalidateInstalledPluginList = useInvalidateInstalledPluginList() // TODO: check installed in beta version. const getTitle = useCallback(() => { @@ -40,7 +45,12 @@ const InstallFromMarketplace: React.FC = ({ const handleInstalled = useCallback(() => { setStep(InstallStep.installed) - }, []) + invalidateInstalledPluginList() + if (PluginType.model.includes(manifest.category)) + updateModelProviders() + if (PluginType.tool.includes(manifest.category)) + invalidateAllToolProviders() + }, [invalidateAllToolProviders, invalidateInstalledPluginList, manifest.category, updateModelProviders]) const handleFailed = useCallback((errorMsg?: string) => { setStep(InstallStep.installFailed) diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx index 440b5de320..1037f4670e 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx @@ -31,6 +31,7 @@ import { uninstallPlugin } from '@/service/plugins' import { useGetLanguage } from '@/context/i18n' import { useModalContext } from '@/context/modal-context' import { useProviderContext } from '@/context/provider-context' +import { useInvalidateAllToolProviders } from '@/service/use-tools' import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config' import cn from '@/utils/classnames' @@ -52,6 +53,7 @@ const DetailHeader = ({ const { checkForUpdates, fetchReleases } = useGitHubReleases() const { setShowUpdatePluginModal } = useModalContext() const { refreshModelProviders } = useProviderContext() + const invalidateAllToolProviders = useInvalidateAllToolProviders() const { installation_id, @@ -150,10 +152,12 @@ const DetailHeader = ({ if (res.success) { hideDeleteConfirm() onUpdate(true) - if (category === PluginType.model) + if (PluginType.model.includes(category)) refreshModelProviders() + if (PluginType.tool.includes(category)) + invalidateAllToolProviders() } - }, [showDeleting, installation_id, hideDeleting, hideDeleteConfirm, onUpdate, category, refreshModelProviders]) + }, [showDeleting, installation_id, hideDeleting, hideDeleteConfirm, onUpdate, category, refreshModelProviders, invalidateAllToolProviders]) // #plugin TODO# used in apps // const usedInApps = 3 diff --git a/web/app/components/plugins/plugin-item/index.tsx b/web/app/components/plugins/plugin-item/index.tsx index d8bc72c158..430ceae7de 100644 --- a/web/app/components/plugins/plugin-item/index.tsx +++ b/web/app/components/plugins/plugin-item/index.tsx @@ -22,6 +22,7 @@ import cn from '@/utils/classnames' import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config' import { useLanguage } from '../../header/account-setting/model-provider-page/hooks' import { useInvalidateInstalledPluginList } from '@/service/use-plugins' +import { useInvalidateAllToolProviders } from '@/service/use-tools' import { useCategories } from '../hooks' import { useProviderContext } from '@/context/provider-context' @@ -40,6 +41,7 @@ const PluginItem: FC = ({ const currentPluginID = usePluginPageContext(v => v.currentPluginID) const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID) const invalidateInstalledPluginList = useInvalidateInstalledPluginList() + const invalidateAllToolProviders = useInvalidateAllToolProviders() const { refreshModelProviders } = useProviderContext() const { @@ -59,8 +61,10 @@ const PluginItem: FC = ({ const handleDelete = () => { invalidateInstalledPluginList() - if (category === PluginType.model) + if (PluginType.model.includes(category)) refreshModelProviders() + if (PluginType.tool.includes(category)) + invalidateAllToolProviders() } return (
{ }) } +export const useInvalidateAllToolProviders = () => { + return useInvalid(useAllToolProvidersKey) +} + const useAllBuiltInToolsKey = [NAME_SPACE, 'builtIn'] export const useAllBuiltInTools = () => { return useQuery({ From 02c598961296d0b688538a495bd86c8af9192f7a Mon Sep 17 00:00:00 2001 From: JzoNg Date: Thu, 21 Nov 2024 17:29:28 +0800 Subject: [PATCH 07/12] recover api tool create card --- web/app/components/tools/provider-list.tsx | 10 +++++++--- .../components/tools/provider/custom-create-card.tsx | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/web/app/components/tools/provider-list.tsx b/web/app/components/tools/provider-list.tsx index 2c0d52b0ba..863d4ed5f7 100644 --- a/web/app/components/tools/provider-list.tsx +++ b/web/app/components/tools/provider-list.tsx @@ -10,6 +10,8 @@ import LabelFilter from '@/app/components/tools/labels/filter' import Input from '@/app/components/base/input' import ProviderDetail from '@/app/components/tools/provider/detail' import Empty from '@/app/components/plugins/marketplace/empty' +import CustomCreateCard from '@/app/components/tools/provider/custom-create-card' +import WorkflowToolEmpty from '@/app/components/tools/add-tool-modal/empty' import Card from '@/app/components/plugins/card' import CardMoreInfo from '@/app/components/plugins/card/card-more-info' import { useSelector as useAppContextSelector } from '@/context/app-context' @@ -82,10 +84,11 @@ const ProviderList = () => { />
- {filteredCollectionList.length > 0 && ( + {(filteredCollectionList.length > 0 || activeTab !== 'builtin') && (
+ {activeTab === 'api' && } {filteredCollectionList.map(collection => (
{ />
))} + {!filteredCollectionList.length && activeTab === 'workflow' &&
}
)} - {!filteredCollectionList.length && ( + {!filteredCollectionList.length && activeTab === 'builtin' && ( )} { - enable_marketplace && ( + enable_marketplace && activeTab === 'builtin' && ( { containerRef.current?.scrollTo({ top: containerRef.current.scrollHeight, behavior: 'smooth' }) diff --git a/web/app/components/tools/provider/custom-create-card.tsx b/web/app/components/tools/provider/custom-create-card.tsx index d6aa9ab533..424a077527 100644 --- a/web/app/components/tools/provider/custom-create-card.tsx +++ b/web/app/components/tools/provider/custom-create-card.tsx @@ -45,7 +45,7 @@ const Contribute = ({ onRefreshData }: Props) => { return ( <> {isCurrentWorkspaceManager && ( -
+
setIsShowEditCustomCollectionModal(true)}>
From 8993a91f12deafde1ceeb366925e0e892959eb9b Mon Sep 17 00:00:00 2001 From: Joel Date: Thu, 21 Nov 2024 17:38:09 +0800 Subject: [PATCH 08/12] fix: upgrade sussces auto hide --- .../plugins/test/other/page.tsx | 20 ------------------- .../components/plugins/plugin-page/index.tsx | 2 ++ .../update-plugin/from-market-place.tsx | 6 ++---- 3 files changed, 4 insertions(+), 24 deletions(-) delete mode 100644 web/app/(commonLayout)/plugins/test/other/page.tsx diff --git a/web/app/(commonLayout)/plugins/test/other/page.tsx b/web/app/(commonLayout)/plugins/test/other/page.tsx deleted file mode 100644 index 3166e34ba3..0000000000 --- a/web/app/(commonLayout)/plugins/test/other/page.tsx +++ /dev/null @@ -1,20 +0,0 @@ -'use client' -import { useBoolean } from 'ahooks' -import UpdatePlugin from '@/app/components/plugins/update-plugin' - -const Page = () => { - const [isShowUpdateModal, { - setTrue: showUpdateModal, - setFalse: hideUpdateModal, - }] = useBoolean(false) - return ( -
-
Show Upgrade
- {isShowUpdateModal && ( - - )} -
- ) -} - -export default Page diff --git a/web/app/components/plugins/plugin-page/index.tsx b/web/app/components/plugins/plugin-page/index.tsx index e743e248bc..4c6e67a64d 100644 --- a/web/app/components/plugins/plugin-page/index.tsx +++ b/web/app/components/plugins/plugin-page/index.tsx @@ -77,6 +77,7 @@ const PluginPage = ({ if (packageId) { const { data } = await fetchManifestFromMarketPlace(encodeURIComponent(packageId)) const { plugin } = data + // TODO: wait api return current plugin version setManifest({ ...plugin, icon: `${marketplaceApiPrefix}/plugins/${plugin.org}/${plugin.name}/icon`, @@ -84,6 +85,7 @@ const PluginPage = ({ showInstallFromMarketplace() } })() + // eslint-disable-next-line react-hooks/exhaustive-deps }, [packageId]) const { diff --git a/web/app/components/plugins/update-plugin/from-market-place.tsx b/web/app/components/plugins/update-plugin/from-market-place.tsx index 20dfc294a7..071b143115 100644 --- a/web/app/components/plugins/update-plugin/from-market-place.tsx +++ b/web/app/components/plugins/update-plugin/from-market-place.tsx @@ -94,11 +94,9 @@ const UpdatePluginModal: FC = ({ } return } - if (uploadStep === UploadStep.installed) { + if (uploadStep === UploadStep.installed) onSave() - onCancel() - } - }, [onCancel, onSave, uploadStep, check, originalPackageInfo.id, handleRefetch, targetPackageInfo.id]) + }, [onSave, uploadStep, check, originalPackageInfo.id, handleRefetch, targetPackageInfo.id]) const usedInAppInfo = useMemo(() => { return (
From c59c696df2b1b015bb70d52f45d8250f4a29d815 Mon Sep 17 00:00:00 2001 From: Joel Date: Thu, 21 Nov 2024 17:43:41 +0800 Subject: [PATCH 09/12] chore: add version type in marketplace return --- .../install-plugin/install-from-marketplace/steps/install.tsx | 2 +- web/app/components/plugins/plugin-page/index.tsx | 1 - web/app/components/plugins/types.ts | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx b/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx index 27ae871d97..596dc1c05e 100644 --- a/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx +++ b/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx @@ -80,7 +80,7 @@ const Installed: FC = ({ return (<>{ payload.latest_version === toInstallVersion || !supportCheckInstalled ? ( - {payload.latest_version} + {payload.version || payload.latest_version} ) : ( <> diff --git a/web/app/components/plugins/plugin-page/index.tsx b/web/app/components/plugins/plugin-page/index.tsx index 4c6e67a64d..486f4f47ce 100644 --- a/web/app/components/plugins/plugin-page/index.tsx +++ b/web/app/components/plugins/plugin-page/index.tsx @@ -77,7 +77,6 @@ const PluginPage = ({ if (packageId) { const { data } = await fetchManifestFromMarketPlace(encodeURIComponent(packageId)) const { plugin } = data - // TODO: wait api return current plugin version setManifest({ ...plugin, icon: `${marketplaceApiPrefix}/plugins/${plugin.org}/${plugin.name}/icon`, diff --git a/web/app/components/plugins/types.ts b/web/app/components/plugins/types.ts index 34cd0c7308..4bf4f54c66 100644 --- a/web/app/components/plugins/types.ts +++ b/web/app/components/plugins/types.ts @@ -79,6 +79,7 @@ export type PluginManifestInMarket = { icon: string label: Record category: PluginType + version: string // TODO: wait api return current plugin version latest_version: string brief: Record introduction: string From 1b3f4f1f2a9ecdcbf15d829b985aeb1a24500978 Mon Sep 17 00:00:00 2001 From: Joel Date: Thu, 21 Nov 2024 18:00:13 +0800 Subject: [PATCH 10/12] feat: support icon size --- web/app/components/plugins/card/base/card-icon.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/web/app/components/plugins/card/base/card-icon.tsx b/web/app/components/plugins/card/base/card-icon.tsx index 34ff0d8fbc..3587f3fd0d 100644 --- a/web/app/components/plugins/card/base/card-icon.tsx +++ b/web/app/components/plugins/card/base/card-icon.tsx @@ -2,12 +2,19 @@ import { RiCheckLine, RiCloseLine } from '@remixicon/react' import AppIcon from '@/app/components/base/app-icon' import cn from '@/utils/classnames' +const iconSizeMap = { + xs: 'w-4 h-4 text-base', + tiny: 'w-6 h-6 text-base', + small: 'w-8 h-8', + medium: 'w-9 h-9', + large: 'w-10 h-10', +} const Icon = ({ className, src, installed = false, installFailed = false, - size, + size = 'large', }: { className?: string src: string | { @@ -23,7 +30,7 @@ const Icon = ({ return (
) } + return (
Date: Thu, 21 Nov 2024 17:38:05 +0800 Subject: [PATCH 11/12] fix: version switch --- .../plugins/plugin-detail-panel/detail-header.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx index 1037f4670e..767366938f 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx @@ -173,7 +173,7 @@ const DetailHeader = ({ {verified && <RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" />} <PluginVersionPicker - disabled={!isFromMarketplace || !hasNewVersion} + disabled={!isFromMarketplace} isShow={isShow} onShowChange={setIsShow} pluginID={plugin_id} @@ -187,13 +187,13 @@ const DetailHeader = ({ className={cn( 'mx-1', isShow && 'bg-state-base-hover', - (isShow || isFromMarketplace) && hasNewVersion && 'hover:bg-state-base-hover', + (isShow || isFromMarketplace) && 'hover:bg-state-base-hover', )} uppercase={false} text={ <> <div>{isFromGitHub ? meta!.version : version}</div> - {isFromMarketplace && hasNewVersion && <RiArrowLeftRightLine className='ml-1 w-3 h-3 text-text-tertiary' />} + {isFromMarketplace && <RiArrowLeftRightLine className='ml-1 w-3 h-3 text-text-tertiary' />} </> } hasRedCornerMark={hasNewVersion} From 78c867b9a3c45f46db45d3218342baa7043be8d6 Mon Sep 17 00:00:00 2001 From: JzoNg <jzongcode@gmail.com> Date: Thu, 21 Nov 2024 18:11:19 +0800 Subject: [PATCH 12/12] use plugin detail for builtin tool --- .../plugins/plugin-detail-panel/index.tsx | 13 +- .../plugins/plugin-page/plugins-panel.tsx | 9 +- web/app/components/tools/provider-list.tsx | 161 ++++++++++-------- web/app/components/tools/types.ts | 1 + 4 files changed, 102 insertions(+), 82 deletions(-) diff --git a/web/app/components/plugins/plugin-detail-panel/index.tsx b/web/app/components/plugins/plugin-detail-panel/index.tsx index c9e4213137..d42304742b 100644 --- a/web/app/components/plugins/plugin-detail-panel/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/index.tsx @@ -6,26 +6,23 @@ import EndpointList from './endpoint-list' import ActionList from './action-list' import ModelList from './model-list' import Drawer from '@/app/components/base/drawer' -import { usePluginPageContext } from '@/app/components/plugins/plugin-page/context' import type { PluginDetail } from '@/app/components/plugins/types' import cn from '@/utils/classnames' type Props = { detail?: PluginDetail onUpdate: () => void + onHide: () => void } const PluginDetailPanel: FC<Props> = ({ detail, onUpdate, + onHide, }) => { - const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID) - - const handleHide = () => setCurrentPluginID(undefined) - const handleUpdate = (isDelete = false) => { if (isDelete) - handleHide() + onHide() onUpdate() } @@ -36,7 +33,7 @@ const PluginDetailPanel: FC<Props> = ({ <Drawer isOpen={!!detail} clickOutsideNotOpen={false} - onClose={handleHide} + onClose={onHide} footer={null} mask={false} positionCenter={false} @@ -46,7 +43,7 @@ const PluginDetailPanel: FC<Props> = ({ <> <DetailHeader detail={detail} - onHide={handleHide} + onHide={onHide} onUpdate={handleUpdate} /> <div className='grow overflow-y-auto'> diff --git a/web/app/components/plugins/plugin-page/plugins-panel.tsx b/web/app/components/plugins/plugin-page/plugins-panel.tsx index 49153bbb53..76e3ea7eca 100644 --- a/web/app/components/plugins/plugin-page/plugins-panel.tsx +++ b/web/app/components/plugins/plugin-page/plugins-panel.tsx @@ -15,6 +15,7 @@ const PluginsPanel = () => { const { data: pluginList, isLoading: isPluginListLoading } = useInstalledPluginList() const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const currentPluginID = usePluginPageContext(v => v.currentPluginID) + const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID) const { run: handleFilterChange } = useDebounceFn((filters: FilterState) => { setFilters(filters) @@ -37,6 +38,8 @@ const PluginsPanel = () => { return detail }, [currentPluginID, pluginList?.plugins]) + const handleHide = () => setCurrentPluginID(undefined) + return ( <> <div className='flex flex-col pt-1 pb-3 px-12 justify-center items-start gap-3 self-stretch'> @@ -54,7 +57,11 @@ const PluginsPanel = () => { ) : ( <Empty /> )} - <PluginDetailPanel detail={currentPluginDetail} onUpdate={() => invalidateInstalledPluginList()}/> + <PluginDetailPanel + detail={currentPluginDetail} + onUpdate={() => invalidateInstalledPluginList()} + onHide={handleHide} + /> </> ) } diff --git a/web/app/components/tools/provider-list.tsx b/web/app/components/tools/provider-list.tsx index 863d4ed5f7..a6f5accec2 100644 --- a/web/app/components/tools/provider-list.tsx +++ b/web/app/components/tools/provider-list.tsx @@ -14,8 +14,10 @@ import CustomCreateCard from '@/app/components/tools/provider/custom-create-card import WorkflowToolEmpty from '@/app/components/tools/add-tool-modal/empty' import Card from '@/app/components/plugins/card' import CardMoreInfo from '@/app/components/plugins/card/card-more-info' +import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel' import { useSelector as useAppContextSelector } from '@/context/app-context' import { useAllToolProviders } from '@/service/use-tools' +import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins' const ProviderList = () => { const { t } = useTranslation() @@ -52,92 +54,105 @@ const ProviderList = () => { }, [activeTab, tagFilterValue, keywords, collectionList]) const [currentProvider, setCurrentProvider] = useState<Collection | undefined>() + const { data: pluginList } = useInstalledPluginList() + const invalidateInstalledPluginList = useInvalidateInstalledPluginList() + const currentPluginDetail = useMemo(() => { + const detail = pluginList?.plugins.find(plugin => plugin.plugin_id === currentProvider?.plugin_id) + return detail + }, [currentProvider?.plugin_id, pluginList?.plugins]) return ( - <div className='relative flex overflow-hidden bg-gray-100 shrink-0 h-0 grow'> - <div - ref={containerRef} - className='relative flex flex-col overflow-y-auto bg-gray-100 grow' - > - <div className={cn( - 'sticky top-0 flex justify-between items-center pt-4 px-12 pb-2 leading-[56px] bg-gray-100 z-20 flex-wrap gap-y-2', - currentProvider && 'pr-6', - )}> - <TabSliderNew - value={activeTab} - onChange={(state) => { - setActiveTab(state) - if (state !== activeTab) - setCurrentProvider(undefined) - }} - options={options} - /> - <div className='flex items-center gap-2'> - <LabelFilter value={tagFilterValue} onChange={handleTagsChange} /> - <Input - showLeftIcon - showClearIcon - wrapperClassName='w-[200px]' - value={keywords} - onChange={e => handleKeywordsChange(e.target.value)} - onClear={() => handleKeywordsChange('')} - /> - </div> - </div> - {(filteredCollectionList.length > 0 || activeTab !== 'builtin') && ( + <> + <div className='relative flex overflow-hidden bg-gray-100 shrink-0 h-0 grow'> + <div + ref={containerRef} + className='relative flex flex-col overflow-y-auto bg-gray-100 grow' + > <div className={cn( - 'relative grid content-start grid-cols-1 gap-4 px-12 pt-2 pb-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0', + 'sticky top-0 flex justify-between items-center pt-4 px-12 pb-2 leading-[56px] bg-gray-100 z-20 flex-wrap gap-y-2', + currentProvider && 'pr-6', )}> - {activeTab === 'api' && <CustomCreateCard onRefreshData={refetch} />} - {filteredCollectionList.map(collection => ( - <div - key={collection.id} - onClick={() => setCurrentProvider(collection)} - > - <Card - className={cn( - 'border-[1.5px] border-transparent cursor-pointer', - currentProvider?.id === collection.id && 'border-components-option-card-option-selected-border', - )} - hideCornerMark - payload={{ - ...collection, - brief: collection.description, - } as any} - footer={ - <CardMoreInfo - tags={collection.labels} - /> - } - /> - </div> - ))} - {!filteredCollectionList.length && activeTab === 'workflow' && <div className='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'><WorkflowToolEmpty /></div>} - </div> - )} - {!filteredCollectionList.length && activeTab === 'builtin' && ( - <Empty lightCard text={t('tools.noTools')} className='px-12' /> - )} - { - enable_marketplace && activeTab === 'builtin' && ( - <Marketplace - onMarketplaceScroll={() => { - containerRef.current?.scrollTo({ top: containerRef.current.scrollHeight, behavior: 'smooth' }) + <TabSliderNew + value={activeTab} + onChange={(state) => { + setActiveTab(state) + if (state !== activeTab) + setCurrentProvider(undefined) }} - searchPluginText={keywords} - filterPluginTags={tagFilterValue} + options={options} /> - ) - } + <div className='flex items-center gap-2'> + <LabelFilter value={tagFilterValue} onChange={handleTagsChange} /> + <Input + showLeftIcon + showClearIcon + wrapperClassName='w-[200px]' + value={keywords} + onChange={e => handleKeywordsChange(e.target.value)} + onClear={() => handleKeywordsChange('')} + /> + </div> + </div> + {(filteredCollectionList.length > 0 || activeTab !== 'builtin') && ( + <div className={cn( + 'relative grid content-start grid-cols-1 gap-4 px-12 pt-2 pb-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0', + )}> + {activeTab === 'api' && <CustomCreateCard onRefreshData={refetch} />} + {filteredCollectionList.map(collection => ( + <div + key={collection.id} + onClick={() => setCurrentProvider(collection)} + > + <Card + className={cn( + 'border-[1.5px] border-transparent cursor-pointer', + currentProvider?.id === collection.id && 'border-components-option-card-option-selected-border', + )} + hideCornerMark + payload={{ + ...collection, + brief: collection.description, + } as any} + footer={ + <CardMoreInfo + tags={collection.labels} + /> + } + /> + </div> + ))} + {!filteredCollectionList.length && activeTab === 'workflow' && <div className='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'><WorkflowToolEmpty /></div>} + </div> + )} + {!filteredCollectionList.length && activeTab === 'builtin' && ( + <Empty lightCard text={t('tools.noTools')} className='px-12' /> + )} + { + enable_marketplace && activeTab === 'builtin' && ( + <Marketplace + onMarketplaceScroll={() => { + containerRef.current?.scrollTo({ top: containerRef.current.scrollHeight, behavior: 'smooth' }) + }} + searchPluginText={keywords} + filterPluginTags={tagFilterValue} + /> + ) + } + </div> </div> - {currentProvider && ( + {currentProvider && !currentProvider.plugin_id && ( <ProviderDetail collection={currentProvider} onHide={() => setCurrentProvider(undefined)} onRefreshData={refetch} /> )} - </div> + <PluginDetailPanel + detail={currentPluginDetail} + onUpdate={() => invalidateInstalledPluginList()} + onHide={() => setCurrentProvider(undefined)} + /> + </> ) } ProviderList.displayName = 'ToolProviderList' diff --git a/web/app/components/tools/types.ts b/web/app/components/tools/types.ts index 34cc491481..27f187d1f7 100644 --- a/web/app/components/tools/types.ts +++ b/web/app/components/tools/types.ts @@ -48,6 +48,7 @@ export type Collection = { is_team_authorization: boolean allow_delete: boolean labels: string[] + plugin_id?: string } export type ToolParameter = {