diff --git a/web/app/(commonLayout)/plugins/test/card/page.tsx b/web/app/(commonLayout)/plugins/test/card/page.tsx
index efc569a733..85a00d5af0 100644
--- a/web/app/(commonLayout)/plugins/test/card/page.tsx
+++ b/web/app/(commonLayout)/plugins/test/card/page.tsx
@@ -1,6 +1,6 @@
'use client'
import Card from '@/app/components/plugins/card'
-import { customTool, extensionDallE, modelGPT4, toolNeko, toolNotion } from '@/app/components/plugins/card/card-mock'
+import { customTool, extensionDallE, modelGPT4, toolNotion } from '@/app/components/plugins/card/card-mock'
// import PluginItem from '@/app/components/plugins/plugin-item'
import CardMoreInfo from '@/app/components/plugins/card/card-more-info'
// import ProviderCard from '@/app/components/plugins/provider-card'
@@ -12,7 +12,23 @@ const PluginList = () => {
return (
-
{ }} plugins={[toolNeko, { ...toolNeko, plugin_unique_identifier: `${toolNeko.plugin_unique_identifier}xxx` }]} />
+ { }} fromDSLPayload={[
+ {
+ type: 'marketplace',
+ value: {
+ plugin_unique_identifier: 'langgenius/google:0.0.2@dcb354c9d0fee60e6e9c9eb996e1e485bbef343ba8cd545c0cfb3ec80970f6f1',
+ },
+ },
+ {
+ type: 'github',
+ value: {
+ repo: 'YIXIAO0/test',
+ version: '1.11.5',
+ package: 'test.difypkg',
+ github_plugin_unique_identifier: 'yixiao0/test:0.0.1@3592166c87afcf944b4f13f27467a5c8f9e00bd349cb42033a072734a37431b4',
+ },
+ },
+ ]} />
{/*
Dify Plugin list
*/}
{/*
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 6f20db7725..54f00d68a4 100644
--- a/web/app/components/plugins/install-plugin/install-bundle/index.tsx
+++ b/web/app/components/plugins/install-plugin/install-bundle/index.tsx
@@ -3,7 +3,7 @@ import type { FC } from 'react'
import Modal from '@/app/components/base/modal'
import React, { useCallback, useState } from 'react'
import { InstallStep } from '../../types'
-import type { PluginDeclaration } from '../../types'
+import type { Dependency } from '../../types'
import Install from './steps/install'
import { useTranslation } from 'react-i18next'
@@ -17,13 +17,14 @@ export enum InstallType {
type Props = {
installType?: InstallType
- plugins?: PluginDeclaration[]
+ fromDSLPayload: Dependency[]
+ // plugins?: PluginDeclaration[]
onClose: () => void
}
const InstallBundle: FC
= ({
installType = InstallType.fromMarketplace,
- plugins = [],
+ fromDSLPayload,
onClose,
}) => {
const { t } = useTranslation()
@@ -54,7 +55,7 @@ const InstallBundle: FC = ({
{step === InstallStep.readyToInstall && (
)}
diff --git a/web/app/components/plugins/install-plugin/install-bundle/item/github-item.tsx b/web/app/components/plugins/install-plugin/install-bundle/item/github-item.tsx
new file mode 100644
index 0000000000..1a773ca6a8
--- /dev/null
+++ b/web/app/components/plugins/install-plugin/install-bundle/item/github-item.tsx
@@ -0,0 +1,47 @@
+'use client'
+import type { FC } from 'react'
+import React, { useEffect } from 'react'
+import type { Dependency, Plugin } from '../../../types'
+import { pluginManifestToCardPluginProps } from '../../utils'
+import { useUploadGitHub } from '@/service/use-plugins'
+import Loading from './loading'
+import LoadedItem from './loaded-item'
+
+type Props = {
+ checked: boolean
+ onCheckedChange: (plugin: Plugin) => void
+ dependency: Dependency
+ onFetchedPayload: (payload: Plugin) => void
+}
+
+const Item: FC
= ({
+ checked,
+ onCheckedChange,
+ dependency,
+ onFetchedPayload,
+}) => {
+ const info = dependency.value
+ const { data } = useUploadGitHub({
+ repo: info.repo!,
+ version: info.version!,
+ package: info.package!,
+ })
+ const [payload, setPayload] = React.useState(null)
+ useEffect(() => {
+ if (data) {
+ const payload = pluginManifestToCardPluginProps(data.manifest)
+ onFetchedPayload(payload)
+ setPayload(payload)
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [data])
+ if (!payload) return
+ return (
+
+ )
+}
+export default React.memo(Item)
diff --git a/web/app/components/plugins/install-plugin/install-bundle/item/loaded-item.tsx b/web/app/components/plugins/install-plugin/install-bundle/item/loaded-item.tsx
new file mode 100644
index 0000000000..94a9071dae
--- /dev/null
+++ b/web/app/components/plugins/install-plugin/install-bundle/item/loaded-item.tsx
@@ -0,0 +1,36 @@
+'use client'
+import type { FC } from 'react'
+import React from 'react'
+import type { Plugin } from '../../../types'
+import Card from '../../../card'
+import Checkbox from '@/app/components/base/checkbox'
+import Badge, { BadgeState } from '@/app/components/base/badge/index'
+
+type Props = {
+ checked: boolean
+ onCheckedChange: (plugin: Plugin) => void
+ payload: Plugin
+}
+
+const LoadedItem: FC = ({
+ checked,
+ onCheckedChange,
+ payload,
+}) => {
+ return (
+
+ onCheckedChange(payload)}
+ />
+ {payload.version}}
+ />
+
+ )
+}
+
+export default React.memo(LoadedItem)
diff --git a/web/app/components/plugins/install-plugin/install-bundle/item/loading.tsx b/web/app/components/plugins/install-plugin/install-bundle/item/loading.tsx
new file mode 100644
index 0000000000..e89022aad6
--- /dev/null
+++ b/web/app/components/plugins/install-plugin/install-bundle/item/loading.tsx
@@ -0,0 +1,12 @@
+'use client'
+import React from 'react'
+
+const Loading = () => {
+ return (
+
+ Loading...
+
+ )
+}
+
+export default React.memo(Loading)
diff --git a/web/app/components/plugins/install-plugin/install-bundle/item/marketplace-item.tsx b/web/app/components/plugins/install-plugin/install-bundle/item/marketplace-item.tsx
new file mode 100644
index 0000000000..5f2e9433a1
--- /dev/null
+++ b/web/app/components/plugins/install-plugin/install-bundle/item/marketplace-item.tsx
@@ -0,0 +1,29 @@
+'use client'
+import type { FC } from 'react'
+import React from 'react'
+import type { Plugin } from '../../../types'
+import Loading from './loading'
+import LoadedItem from './loaded-item'
+
+type Props = {
+ checked: boolean
+ onCheckedChange: (plugin: Plugin) => void
+ payload?: Plugin
+}
+
+const MarketPlaceItem: FC = ({
+ checked,
+ onCheckedChange,
+ payload,
+}) => {
+ if (!payload) return
+ return (
+
+ )
+}
+
+export default React.memo(MarketPlaceItem)
diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/install-by-dsl-list.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/install-by-dsl-list.tsx
new file mode 100644
index 0000000000..eae9394dc1
--- /dev/null
+++ b/web/app/components/plugins/install-plugin/install-bundle/steps/install-by-dsl-list.tsx
@@ -0,0 +1,83 @@
+'use client'
+import type { FC } from 'react'
+import React, { useCallback, useEffect, useMemo } from 'react'
+import type { Dependency, Plugin } from '../../../types'
+import MarketplaceItem from '../item/marketplace-item'
+import GithubItem from '../item/github-item'
+import { useFetchPluginsInMarketPlaceByIds } from '@/service/use-plugins'
+import produce from 'immer'
+import { useGetState } from 'ahooks'
+
+type Props = {
+ fromDSLPayload: Dependency[]
+ selectedPlugins: Plugin[]
+ handleSelect: (plugin: Plugin) => void
+ onLoadedAllPlugin: () => void
+}
+
+const InstallByDSLList: FC = ({
+ fromDSLPayload,
+ selectedPlugins,
+ handleSelect,
+ onLoadedAllPlugin,
+}) => {
+ const { isLoading: isFetchingMarketplaceData, data: marketplaceRes } = useFetchPluginsInMarketPlaceByIds(fromDSLPayload.filter(d => d.type === 'marketplace').map(d => d.value.plugin_unique_identifier!))
+ const [plugins, setPlugins, getPlugins] = useGetState([])
+ const handlePlugInFetched = useCallback((index: number) => {
+ return (p: Plugin) => {
+ setPlugins(plugins.map((item, i) => i === index ? p : item))
+ }
+ }, [plugins])
+
+ const marketPlaceInDSLIndex = useMemo(() => {
+ const res: number[] = []
+ fromDSLPayload.forEach((d, index) => {
+ if (d.type === 'marketplace')
+ res.push(index)
+ })
+ return res
+ }, [fromDSLPayload])
+
+ useEffect(() => {
+ if (!isFetchingMarketplaceData && marketplaceRes?.data.plugins && marketplaceRes?.data.plugins.length > 0) {
+ const payloads = marketplaceRes?.data.plugins
+
+ const nextPlugins = produce(getPlugins(), (draft) => {
+ marketPlaceInDSLIndex.forEach((index, i) => {
+ draft[index] = payloads[i]
+ })
+ })
+ setPlugins(nextPlugins)
+ // marketplaceRes?.data.plugins
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isFetchingMarketplaceData])
+
+ const isLoadedAllData = fromDSLPayload.length === plugins.length && plugins.every(p => !!p)
+ useEffect(() => {
+ if (isLoadedAllData)
+ onLoadedAllPlugin()
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isLoadedAllData])
+ return (
+ <>
+ {fromDSLPayload.map((d, index) => (
+ d.type === 'github'
+ ? p.plugin_id === d.value.plugin_unique_identifier)}
+ onCheckedChange={handleSelect}
+ dependency={d}
+ onFetchedPayload={handlePlugInFetched(index)}
+ />
+ : p.plugin_id === d.value.plugin_unique_identifier)}
+ onCheckedChange={handleSelect}
+ payload={plugins[index] as Plugin}
+ />
+ ))}
+ >
+ )
+}
+export default React.memo(InstallByDSLList)
diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx
index 9b0e996f82..1423e399b8 100644
--- a/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx
+++ b/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx
@@ -1,41 +1,42 @@
'use client'
import type { FC } from 'react'
-import React from 'react'
-import type { PluginDeclaration } from '../../../types'
-import Card from '../../../card'
+import React, { useCallback } from 'react'
+import type { Dependency, Plugin } from '../../../types'
import Button from '@/app/components/base/button'
import { RiLoader2Line } from '@remixicon/react'
-import Badge, { BadgeState } from '@/app/components/base/badge/index'
-import { pluginManifestToCardPluginProps } from '../../utils'
import { useTranslation } from 'react-i18next'
-import Checkbox from '@/app/components/base/checkbox'
+import InstallByDSLList from './install-by-dsl-list'
const i18nPrefix = 'plugin.installModal'
type Props = {
- plugins: PluginDeclaration[],
+ fromDSLPayload: Dependency[]
onCancel: () => void
}
const Install: FC = ({
- plugins,
+ fromDSLPayload,
onCancel,
}) => {
const { t } = useTranslation()
- const [selectedPlugins, setSelectedPlugins] = React.useState([])
+ const [selectedPlugins, setSelectedPlugins] = React.useState([])
const selectedPluginsNum = selectedPlugins.length
- const handleSelect = (plugin: PluginDeclaration) => {
+
+ const handleSelect = (plugin: Plugin) => {
return () => {
- const isSelected = !!selectedPlugins.find(p => p.plugin_unique_identifier === plugin.plugin_unique_identifier)
+ const isSelected = !!selectedPlugins.find(p => p.plugin_id === plugin.plugin_id)
let nextSelectedPlugins
if (isSelected)
- nextSelectedPlugins = selectedPlugins.filter(p => p.plugin_unique_identifier !== plugin.plugin_unique_identifier)
+ nextSelectedPlugins = selectedPlugins.filter(p => p.plugin_id !== plugin.plugin_id)
else
nextSelectedPlugins = [...selectedPlugins, plugin]
setSelectedPlugins(nextSelectedPlugins)
}
}
- const [isInstalling, setIsInstalling] = React.useState(false)
+ const [canInstall, setCanInstall] = React.useState(false)
+ const handleLoadedAllPlugin = useCallback(() => {
+ setCanInstall(true)
+ }, [])
const handleInstall = () => {
}
@@ -46,25 +47,17 @@ const Install: FC = ({
{t(`${i18nPrefix}.${selectedPluginsNum > 1 ? 'readyToInstallPackages' : 'readyToInstallPackage'}`, { num: selectedPluginsNum })}
- {plugins.map(plugin => (
-
- p.plugin_unique_identifier === plugin.plugin_unique_identifier)}
- onCheck={handleSelect(plugin)}
- />
- {plugin.version}}
- />
-
- ))}
+
{/* Action Buttons */}
- {!isInstalling && (
+ {!canInstall && (
@@ -72,11 +65,11 @@ const Install: FC
= ({
>
diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts
index c9f8058e8f..196f9bd5ff 100644
--- a/web/service/use-plugins.ts
+++ b/web/service/use-plugins.ts
@@ -7,6 +7,7 @@ import type {
Permissions,
PluginTask,
PluginsFromMarketplaceResponse,
+ uploadGitHubResponse,
} from '@/app/components/plugins/types'
import type {
PluginsSearchParams,
@@ -60,6 +61,19 @@ export const useInstallPackageFromLocal = () => {
})
}
+export const useUploadGitHub = (payload: {
+ repo: string
+ version: string
+ package: string
+}) => {
+ return useQuery({
+ queryKey: [NAME_SPACE, 'uploadGitHub', payload],
+ queryFn: () => post('/workspaces/current/plugin/upload/github', {
+ body: payload,
+ }),
+ })
+}
+
export const useDebugKey = () => {
return useQuery({
queryKey: [NAME_SPACE, 'debugKey'],
@@ -123,6 +137,17 @@ export const useMutationPluginsFromMarketplace = () => {
})
}
+export const useFetchPluginsInMarketPlaceByIds = (unique_identifiers: string[]) => {
+ return useQuery({
+ queryKey: [NAME_SPACE, 'fetchPluginsInMarketPlaceByIds', unique_identifiers],
+ queryFn: () => postMarketplace<{ data: PluginsFromMarketplaceResponse }>('/plugins/identifier/batch', {
+ body: {
+ unique_identifiers,
+ },
+ }),
+ })
+}
+
const usePluginTaskListKey = [NAME_SPACE, 'pluginTaskList']
export const usePluginTaskList = () => {
const [enabled, setEnabled] = useState(true)