diff --git a/web/app/(commonLayout)/plugins/test/update/page.tsx b/web/app/(commonLayout)/plugins/test/update/page.tsx
new file mode 100644
index 0000000000..9d78b45979
--- /dev/null
+++ b/web/app/(commonLayout)/plugins/test/update/page.tsx
@@ -0,0 +1,67 @@
+'use client'
+import { toolNeko } from '@/app/components/plugins/card/card-mock'
+import { PluginSource } from '@/app/components/plugins/types'
+import { useModalContext } from '@/context/modal-context'
+import React from 'react'
+
+const UpdatePlugin = () => {
+ const { setShowUpdatePluginModal } = useModalContext()
+ const handleUpdateFromMarketPlace = () => {
+ setShowUpdatePluginModal({
+ payload: {
+ type: PluginSource.marketplace,
+ marketPlace: {
+ originalPackageInfo: {
+ id: 'langgenius/neko:0.0.1@9e57d693739287c0efdc96847d7ed959ca93f70aa704471f2eb7ed3313821824',
+ payload: toolNeko as any,
+ },
+ targetPackageInfo: {
+ id: 'target_xxx',
+ version: '1.2.3',
+ },
+ },
+ },
+ onCancelCallback: () => {
+ console.log('canceled')
+ },
+ onSaveCallback: () => {
+ console.log('saved')
+ },
+ })
+ }
+ const handleUpdateFromGithub = () => {
+ setShowUpdatePluginModal({
+ payload: {
+ type: PluginSource.github,
+ github: {
+ originalPackageInfo: {
+ id: '111',
+ repo: 'aaa/bbb',
+ version: 'xxx',
+ url: 'aaa/bbb',
+ currVersion: '1.2.3',
+ currPackage: 'pack1',
+ } as any,
+ },
+ },
+ onCancelCallback: () => {
+ console.log('canceled')
+ },
+ onSaveCallback: () => {
+ console.log('saved')
+ },
+ })
+ }
+
+ return (
+
+
更新组件
+
+
从 Marketplace
+
从 GitHub
+
+
+ )
+}
+
+export default React.memo(UpdatePlugin)
diff --git a/web/app/components/plugins/card/card-mock.ts b/web/app/components/plugins/card/card-mock.ts
index 2ecd59a12b..4217c4d33a 100644
--- a/web/app/components/plugins/card/card-mock.ts
+++ b/web/app/components/plugins/card/card-mock.ts
@@ -1,6 +1,64 @@
import type { PluginDeclaration } from '../types'
import { PluginType } from '../types'
+export const toolNeko: PluginDeclaration = {
+ version: '0.0.1',
+ author: 'langgenius',
+ name: 'neko',
+ description: {
+ en_US: 'Neko is a cute cat.',
+ zh_Hans: '这是一只可爱的小猫。',
+ pt_BR: 'Neko is a cute cat.',
+ ja_JP: 'Neko is a cute cat.',
+ },
+ icon: '241e5209ecc8b5ce6b7a29a8e50388e9c75b89c3047c6ecd8e552f26de758883.svg',
+ label: {
+ en_US: 'Neko',
+ zh_Hans: 'Neko',
+ pt_BR: 'Neko',
+ ja_JP: 'Neko',
+ },
+ category: 'extension' as any,
+ created_at: '2024-07-12T08:03:44.658609Z',
+ resource: {
+ memory: 1048576,
+ permission: {
+ tool: {
+ enabled: true,
+ },
+ model: {
+ enabled: true,
+ llm: true,
+ text_embedding: false,
+ rerank: false,
+ tts: false,
+ speech2text: false,
+ moderation: false,
+ },
+ node: null,
+ endpoint: {
+ enabled: true,
+ },
+ storage: {
+ enabled: true,
+ size: 1048576,
+ },
+ },
+ },
+ plugins: {
+ tools: null,
+ models: null,
+ endpoints: [
+ 'provider/neko.yaml',
+ ],
+ },
+ tags: [],
+ verified: false,
+ tool: null,
+ model: null,
+ endpoint: null,
+}
+
export const toolNotion = {
type: PluginType.tool,
org: 'Notion',
diff --git a/web/app/components/plugins/hooks.ts b/web/app/components/plugins/hooks.ts
new file mode 100644
index 0000000000..ef69cf5f23
--- /dev/null
+++ b/web/app/components/plugins/hooks.ts
@@ -0,0 +1,72 @@
+import { useTranslation } from 'react-i18next'
+
+export const useTags = () => {
+ const { t } = useTranslation()
+
+ return [
+ {
+ name: 'search',
+ label: t('pluginTags.search'),
+ },
+ {
+ name: 'image',
+ label: t('pluginTags.image'),
+ },
+ {
+ name: 'videos',
+ label: t('pluginTags.videos'),
+ },
+ {
+ name: 'weather',
+ label: t('pluginTags.weather'),
+ },
+ {
+ name: 'finance',
+ label: t('pluginTags.finance'),
+ },
+ {
+ name: 'design',
+ label: t('pluginTags.design'),
+ },
+ {
+ name: 'travel',
+ label: t('pluginTags.travel'),
+ },
+ {
+ name: 'social',
+ label: t('pluginTags.social'),
+ },
+ {
+ name: 'news',
+ label: t('pluginTags.news'),
+ },
+ {
+ name: 'medical',
+ label: t('pluginTags.medical'),
+ },
+ {
+ name: 'productivity',
+ label: t('pluginTags.productivity'),
+ },
+ {
+ name: 'education',
+ label: t('pluginTags.education'),
+ },
+ {
+ name: 'business',
+ label: t('pluginTags.business'),
+ },
+ {
+ name: 'entertainment',
+ label: t('pluginTags.entertainment'),
+ },
+ {
+ name: 'utilities',
+ label: t('pluginTags.utilities'),
+ },
+ {
+ name: 'other',
+ label: t('pluginTags.other'),
+ },
+ ]
+}
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 c60dd73765..ea6ab315ba 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
@@ -6,7 +6,7 @@ import type { Item } from '@/app/components/base/select'
import type { InstallState } from '@/app/components/plugins/types'
import { useGitHubReleases, useGitHubUpload } from '../hooks'
import { parseGitHubUrl } from '../utils'
-import type { PluginDeclaration } from '../../types'
+import type { PluginDeclaration, UpdateFromGitHubPayload } from '../../types'
import { InstallStepFromGitHub } from '../../types'
import Toast from '@/app/components/base/toast'
import SetURL from './steps/setURL'
@@ -16,16 +16,18 @@ import Loaded from './steps/loaded'
import { useTranslation } from 'react-i18next'
type InstallFromGitHubProps = {
+ updatePayload?: UpdateFromGitHubPayload
onClose: () => void
+ onSuccess: () => void
}
-const InstallFromGitHub: React.FC = ({ onClose }) => {
+const InstallFromGitHub: React.FC = ({ updatePayload, onClose }) => {
const { t } = useTranslation()
const [state, setState] = useState({
- step: InstallStepFromGitHub.setUrl,
- repoUrl: '',
- selectedVersion: '',
- selectedPackage: '',
+ step: updatePayload ? InstallStepFromGitHub.selectPackage : InstallStepFromGitHub.setUrl,
+ repoUrl: updatePayload?.url || '',
+ selectedVersion: updatePayload?.currVersion || '',
+ selectedPackage: updatePayload?.currPackage || '',
releases: [],
})
const [uniqueIdentifier, setUniqueIdentifier] = useState(null)
@@ -139,6 +141,7 @@ const InstallFromGitHub: React.FC = ({ onClose }) => {
)}
{state.step === InstallStepFromGitHub.selectPackage && (
setState(prevState => ({ ...prevState, selectedVersion: item.value as string }))}
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 5ecf9d27f3..d53be9e49b 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
@@ -40,7 +40,7 @@ const InstallFromLocalPackage: React.FC = ({
return t(`${i18nPrefix}.installFailed`)
return t(`${i18nPrefix}.installPlugin`)
- }, [step])
+ }, [step, t])
const { getIconUrl } = useGetIcon()
@@ -59,7 +59,7 @@ const InstallFromLocalPackage: React.FC = ({
icon,
})
setStep(InstallStep.readyToInstall)
- }, [])
+ }, [getIconUrl])
const handleUploadFail = useCallback((errorMsg: string) => {
setErrorMsg(errorMsg)
diff --git a/web/app/components/plugins/install-plugin/utils.ts b/web/app/components/plugins/install-plugin/utils.ts
index 7677bf5b73..9f9508490e 100644
--- a/web/app/components/plugins/install-plugin/utils.ts
+++ b/web/app/components/plugins/install-plugin/utils.ts
@@ -3,6 +3,7 @@ import type { GitHubUrlInfo } from '@/app/components/plugins/types'
export const pluginManifestToCardPluginProps = (pluginManifest: PluginDeclaration): Plugin => {
return {
+ plugin_id: pluginManifest.plugin_unique_identifier,
type: pluginManifest.category,
category: pluginManifest.category,
name: pluginManifest.name,
@@ -25,6 +26,7 @@ export const pluginManifestToCardPluginProps = (pluginManifest: PluginDeclaratio
export const pluginManifestInMarketToPluginProps = (pluginManifest: PluginManifestInMarket): Plugin => {
return {
+ plugin_id: pluginManifest.plugin_unique_identifier,
type: pluginManifest.category,
category: pluginManifest.category,
name: pluginManifest.name,
diff --git a/web/app/components/plugins/marketplace/hooks.ts b/web/app/components/plugins/marketplace/hooks.ts
index 89a0908ee6..83b7ee5435 100644
--- a/web/app/components/plugins/marketplace/hooks.ts
+++ b/web/app/components/plugins/marketplace/hooks.ts
@@ -40,7 +40,7 @@ export const useMarketplaceCollectionsAndPlugins = () => {
export const useMarketplacePlugins = () => {
const [isLoading, setIsLoading] = useState(false)
- const [plugins, setPlugins] = useState([])
+ const [plugins, setPlugins] = useState()
const queryPlugins = useCallback(async (query: PluginsSearchParams) => {
setIsLoading(true)
diff --git a/web/app/components/plugins/marketplace/search-box/tags-filter.tsx b/web/app/components/plugins/marketplace/search-box/tags-filter.tsx
index 5114765544..c7a1a4e57e 100644
--- a/web/app/components/plugins/marketplace/search-box/tags-filter.tsx
+++ b/web/app/components/plugins/marketplace/search-box/tags-filter.tsx
@@ -14,6 +14,7 @@ import {
import Checkbox from '@/app/components/base/checkbox'
import cn from '@/utils/classnames'
import Input from '@/app/components/base/input'
+import { useTags } from '@/app/components/plugins/hooks'
type TagsFilterProps = {
tags: string[]
@@ -27,17 +28,8 @@ const TagsFilter = ({
}: TagsFilterProps) => {
const [open, setOpen] = useState(false)
const [searchText, setSearchText] = useState('')
- const options = [
- {
- value: 'search',
- text: 'Search',
- },
- {
- value: 'image',
- text: 'Image',
- },
- ]
- const filteredOptions = options.filter(option => option.text.toLowerCase().includes(searchText.toLowerCase()))
+ const options = useTags()
+ const filteredOptions = options.filter(option => option.label.toLowerCase().includes(searchText.toLowerCase()))
const handleCheck = (id: string) => {
if (tags.includes(id))
onTagsChange(tags.filter((tag: string) => tag !== id))
@@ -115,16 +107,16 @@ const TagsFilter = ({
{
filteredOptions.map(option => (
handleCheck(option.value)}
+ onClick={() => handleCheck(option.name)}
>
- {option.text}
+ {option.label}
))
diff --git a/web/app/components/plugins/marketplace/utils.ts b/web/app/components/plugins/marketplace/utils.ts
index 99b34fa570..a8e50b5e20 100644
--- a/web/app/components/plugins/marketplace/utils.ts
+++ b/web/app/components/plugins/marketplace/utils.ts
@@ -14,13 +14,13 @@ export const getMarketplaceCollectionsAndPlugins = async (query?: CollectionsAnd
let marketplaceCollections = [] as MarketplaceCollection[]
let marketplaceCollectionPluginsMap = {} as Record
try {
- const marketplaceCollectionsData = await globalThis.fetch(`${MARKETPLACE_API_PREFIX}/collections`, { cache: 'no-store' })
+ const marketplaceCollectionsData = await globalThis.fetch(`${MARKETPLACE_API_PREFIX}/collections?page=1&page_size=100`, { cache: 'no-store' })
const marketplaceCollectionsDataJson = await marketplaceCollectionsData.json()
marketplaceCollections = marketplaceCollectionsDataJson.data.collections
await Promise.all(marketplaceCollections.map(async (collection: MarketplaceCollection) => {
- let url = `${MARKETPLACE_API_PREFIX}/collections/${collection.name}/plugins`
+ let url = `${MARKETPLACE_API_PREFIX}/collections/${collection.name}/plugins?page=1&page_size=100`
if (query?.category)
- url += `?category=${query.category}`
+ url += `&category=${query.category}`
const marketplaceCollectionPluginsData = await globalThis.fetch(url, { cache: 'no-store' })
const marketplaceCollectionPluginsDataJson = await marketplaceCollectionPluginsData.json()
const plugins = marketplaceCollectionPluginsDataJson.data.plugins.map((plugin: Plugin) => {
diff --git a/web/app/components/plugins/plugin-page/install-info.tsx b/web/app/components/plugins/plugin-page/install-info.tsx
index 4d3b076883..bb0a31f4be 100644
--- a/web/app/components/plugins/plugin-page/install-info.tsx
+++ b/web/app/components/plugins/plugin-page/install-info.tsx
@@ -59,7 +59,7 @@ const InstallInfo = () => {
-
+
3 plugins failed to install
diff --git a/web/app/components/plugins/types.ts b/web/app/components/plugins/types.ts
index 8ccba6ef30..051f93906e 100644
--- a/web/app/components/plugins/types.ts
+++ b/web/app/components/plugins/types.ts
@@ -54,6 +54,7 @@ export type EndpointListItem = {
// Plugin manifest
export type PluginDeclaration = {
+ plugin_unique_identifier: string
version: string
author: string
icon: string
@@ -71,6 +72,7 @@ export type PluginDeclaration = {
}
export type PluginManifestInMarket = {
+ plugin_unique_identifier: string
name: string
org: string
icon: string
@@ -134,6 +136,36 @@ export type Permissions = {
debug_permission: PermissionType
}
+export type UpdateFromMarketPlacePayload = {
+ originalPackageInfo: {
+ id: string
+ payload: PluginDeclaration
+ },
+ targetPackageInfo: {
+ id: string
+ version: string
+ }
+}
+
+export type UpdateFromGitHubPayload = {
+ originalPackageInfo: {
+ id: string
+ repo: string
+ version: string
+ }
+}
+
+export type UpdatePluginPayload = {
+ type: PluginSource
+ marketPlace?: UpdateFromMarketPlacePayload
+ github?: UpdateFromGitHubPayload
+}
+
+export type UpdatePluginModalType = UpdatePluginPayload & {
+ onCancel: () => void
+ onSave: () => void
+}
+
export enum InstallStepFromGitHub {
setUrl = 'url',
selectPackage = 'selecting',
diff --git a/web/app/components/plugins/update-plugin/from-github.tsx b/web/app/components/plugins/update-plugin/from-github.tsx
new file mode 100644
index 0000000000..9bc2f2ad3e
--- /dev/null
+++ b/web/app/components/plugins/update-plugin/from-github.tsx
@@ -0,0 +1,26 @@
+'use client'
+import type { FC } from 'react'
+import React from 'react'
+import type { UpdateFromGitHubPayload } from '../types'
+import InstallFromGitHub from '../install-plugin/install-from-github'
+
+type Props = {
+ payload: UpdateFromGitHubPayload
+ onSave: () => void
+ onCancel: () => void
+}
+
+const FromGitHub: FC
= ({
+ payload,
+ onSave,
+ onCancel,
+}) => {
+ return (
+
+ )
+}
+export default React.memo(FromGitHub)
diff --git a/web/app/components/plugins/update-plugin/from-market-place.tsx b/web/app/components/plugins/update-plugin/from-market-place.tsx
new file mode 100644
index 0000000000..e0f9e2bc01
--- /dev/null
+++ b/web/app/components/plugins/update-plugin/from-market-place.tsx
@@ -0,0 +1,121 @@
+'use client'
+import type { FC } from 'react'
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import { RiInformation2Line } from '@remixicon/react'
+import { useTranslation } from 'react-i18next'
+import Card from '@/app/components/plugins/card'
+import Modal from '@/app/components/base/modal'
+import Button from '@/app/components/base/button'
+import Badge, { BadgeState } from '@/app/components/base/badge/index'
+import type { UpdateFromMarketPlacePayload } from '../types'
+import { pluginManifestToCardPluginProps } from '@/app/components/plugins/install-plugin/utils'
+import useGetIcon from '../install-plugin/base/use-get-icon'
+
+const i18nPrefix = 'plugin.upgrade'
+
+type Props = {
+ payload: UpdateFromMarketPlacePayload
+ onSave: () => void
+ onCancel: () => void
+}
+
+enum UploadStep {
+ notStarted = 'notStarted',
+ upgrading = 'upgrading',
+ installed = 'installed',
+}
+
+const UpdatePluginModal: FC = ({
+ payload,
+ onSave,
+ onCancel,
+}) => {
+ const {
+ originalPackageInfo,
+ targetPackageInfo,
+ } = payload
+ const { t } = useTranslation()
+ const { getIconUrl } = useGetIcon()
+ const [icon, setIcon] = useState(originalPackageInfo.payload.icon)
+ useEffect(() => {
+ (async () => {
+ const icon = await getIconUrl(originalPackageInfo.payload.icon)
+ setIcon(icon)
+ })()
+ }, [originalPackageInfo, getIconUrl])
+ const [uploadStep, setUploadStep] = useState(UploadStep.notStarted)
+ const configBtnText = useMemo(() => {
+ return ({
+ [UploadStep.notStarted]: t(`${i18nPrefix}.upgrade`),
+ [UploadStep.upgrading]: t(`${i18nPrefix}.upgrading`),
+ [UploadStep.installed]: t(`${i18nPrefix}.close`),
+ })[uploadStep]
+ }, [t, uploadStep])
+
+ const handleConfirm = useCallback(() => {
+ if (uploadStep === UploadStep.notStarted) {
+ setUploadStep(UploadStep.upgrading)
+ setTimeout(() => {
+ setUploadStep(UploadStep.installed)
+ }, 1500)
+ return
+ }
+ if (uploadStep === UploadStep.installed) {
+ onSave()
+ onCancel()
+ }
+ }, [onCancel, onSave, uploadStep])
+ return (
+
+
+ {t(`${i18nPrefix}.description`)}
+
+
+
+
+ {`${originalPackageInfo.payload.version} -> ${targetPackageInfo.version}`}
+
+
+
{t(`${i18nPrefix}.usedInApps`, { num: 3 })}
+ {/* show the used apps */}
+
+
+ >
+ }
+ />
+
+
+ {uploadStep === UploadStep.notStarted && (
+
+ )}
+
+
+
+ )
+}
+export default React.memo(UpdatePluginModal)
diff --git a/web/app/components/plugins/update-plugin/index.tsx b/web/app/components/plugins/update-plugin/index.tsx
index 0a358debac..f9b49a6073 100644
--- a/web/app/components/plugins/update-plugin/index.tsx
+++ b/web/app/components/plugins/update-plugin/index.tsx
@@ -1,97 +1,33 @@
'use client'
import type { FC } from 'react'
-import React, { useCallback, useMemo, useState } from 'react'
-import { RiInformation2Line } from '@remixicon/react'
-import { useTranslation } from 'react-i18next'
-import Card from '@/app/components/plugins/card'
-import Modal from '@/app/components/base/modal'
-import Button from '@/app/components/base/button'
-import Badge, { BadgeState } from '@/app/components/base/badge/index'
-import { toolNotion } from '@/app/components/plugins/card/card-mock'
+import React from 'react'
+import type { UpdatePluginModalType } from '../types'
+import { PluginSource } from '../types'
+import UpdateFromGitHub from './from-github'
+import UpdateFromMarketplace from './from-market-place'
-const i18nPrefix = 'plugin.upgrade'
-
-type Props = {
- onHide: () => void
-}
-
-enum UploadStep {
- notStarted = 'notStarted',
- upgrading = 'upgrading',
- installed = 'installed',
-}
-
-const UpdatePluginModal: FC = ({
- onHide,
+const UpdatePlugin: FC = ({
+ type,
+ marketPlace,
+ github,
+ onCancel,
+ onSave,
}) => {
- const { t } = useTranslation()
- const [uploadStep, setUploadStep] = useState(UploadStep.notStarted)
- const configBtnText = useMemo(() => {
- return ({
- [UploadStep.notStarted]: t(`${i18nPrefix}.upgrade`),
- [UploadStep.upgrading]: t(`${i18nPrefix}.upgrading`),
- [UploadStep.installed]: t(`${i18nPrefix}.close`),
- })[uploadStep]
- }, [uploadStep])
- const handleConfirm = useCallback(() => {
- if (uploadStep === UploadStep.notStarted) {
- setUploadStep(UploadStep.upgrading)
- setTimeout(() => {
- setUploadStep(UploadStep.installed)
- }, 1500)
- return
- }
- if (uploadStep === UploadStep.installed)
- onHide()
- }, [uploadStep])
+ if (type === PluginSource.github) {
+ return (
+
+ )
+ }
return (
-
-
- {t(`${i18nPrefix}.description`)}
-
-
-
-
- {'1.2.0 -> 1.3.2'}
-
-
-
{t(`${i18nPrefix}.usedInApps`, { num: 3 })}
- {/* show the used apps */}
-
-
- >
- }
- />
-
-
- {uploadStep === UploadStep.notStarted && (
-
- )}
-
-
-
+
)
}
-export default React.memo(UpdatePluginModal)
+export default React.memo(UpdatePlugin)
diff --git a/web/app/components/tools/labels/constant.ts b/web/app/components/tools/labels/constant.ts
index 3f073859d9..ad4836e6a8 100644
--- a/web/app/components/tools/labels/constant.ts
+++ b/web/app/components/tools/labels/constant.ts
@@ -1,6 +1,4 @@
-import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations'
export type Label = {
name: string
- icon: string
- label: TypeWithI18N
+ label: string
}
diff --git a/web/app/components/tools/labels/filter.tsx b/web/app/components/tools/labels/filter.tsx
index 36c1c1ba83..f33a63f480 100644
--- a/web/app/components/tools/labels/filter.tsx
+++ b/web/app/components/tools/labels/filter.tsx
@@ -1,10 +1,8 @@
import type { FC } from 'react'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { useContext } from 'use-context-selector'
-import { useDebounceFn, useMount } from 'ahooks'
+import { useDebounceFn } from 'ahooks'
import { RiArrowDownSLine } from '@remixicon/react'
-import { useStore as useLabelStore } from './store'
import cn from '@/utils/classnames'
import {
PortalToFollowElem,
@@ -16,11 +14,9 @@ import { Tag01, Tag03 } from '@/app/components/base/icons/src/vender/line/financ
import { Check } from '@/app/components/base/icons/src/vender/line/general'
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
import type { Label } from '@/app/components/tools/labels/constant'
-import { fetchLabelList } from '@/service/tools'
-import I18n from '@/context/i18n'
-import { getLanguage } from '@/i18n/language'
+import { useTags } from '@/app/components/plugins/hooks'
-interface LabelFilterProps {
+type LabelFilterProps = {
value: string[]
onChange: (v: string[]) => void
}
@@ -29,12 +25,9 @@ const LabelFilter: FC = ({
onChange,
}) => {
const { t } = useTranslation()
- const { locale } = useContext(I18n)
- const language = getLanguage(locale)
const [open, setOpen] = useState(false)
- const labelList = useLabelStore(s => s.labelList)
- const setLabelList = useLabelStore(s => s.setLabelList)
+ const labelList = useTags()
const [keywords, setKeywords] = useState('')
const [searchKeywords, setSearchKeywords] = useState('')
@@ -61,12 +54,6 @@ const LabelFilter: FC = ({
onChange([...value, label.name])
}
- useMount(() => {
- fetchLabelList().then((res) => {
- setLabelList(res)
- })
- })
-
return (
= ({
{!value.length && t('common.tag.placeholder')}
- {!!value.length && currentLabel?.label[language]}
+ {!!value.length && currentLabel?.label}
{value.length > 1 && (
{`+${value.length - 1}`}
@@ -128,7 +115,7 @@ const LabelFilter: FC
= ({
className='flex items-center gap-2 pl-3 py-[6px] pr-2 rounded-lg cursor-pointer hover:bg-gray-100'
onClick={() => selectLabel(label)}
>
- {label.label[language]}
+ {label.label}
{value.includes(label.name) && }
))}
diff --git a/web/app/components/tools/labels/selector.tsx b/web/app/components/tools/labels/selector.tsx
index baa043010e..0c64ebb142 100644
--- a/web/app/components/tools/labels/selector.tsx
+++ b/web/app/components/tools/labels/selector.tsx
@@ -1,10 +1,8 @@
import type { FC } from 'react'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { useContext } from 'use-context-selector'
-import { useDebounceFn, useMount } from 'ahooks'
+import { useDebounceFn } from 'ahooks'
import { RiArrowDownSLine } from '@remixicon/react'
-import { useStore as useLabelStore } from './store'
import cn from '@/utils/classnames'
import {
PortalToFollowElem,
@@ -15,11 +13,9 @@ import Input from '@/app/components/base/input'
import { Tag03 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
import Checkbox from '@/app/components/base/checkbox'
import type { Label } from '@/app/components/tools/labels/constant'
-import { fetchLabelList } from '@/service/tools'
-import I18n from '@/context/i18n'
-import { getLanguage } from '@/i18n/language'
+import { useTags } from '@/app/components/plugins/hooks'
-interface LabelSelectorProps {
+type LabelSelectorProps = {
value: string[]
onChange: (v: string[]) => void
}
@@ -28,12 +24,9 @@ const LabelSelector: FC = ({
onChange,
}) => {
const { t } = useTranslation()
- const { locale } = useContext(I18n)
- const language = getLanguage(locale)
const [open, setOpen] = useState(false)
- const labelList = useLabelStore(s => s.labelList)
- const setLabelList = useLabelStore(s => s.setLabelList)
+ const labelList = useTags()
const [keywords, setKeywords] = useState('')
const [searchKeywords, setSearchKeywords] = useState('')
@@ -50,8 +43,8 @@ const LabelSelector: FC = ({
}, [labelList, searchKeywords])
const selectedLabels = useMemo(() => {
- return value.map(v => labelList.find(l => l.name === v)?.label[language]).join(', ')
- }, [value, labelList, language])
+ return value.map(v => labelList.find(l => l.name === v)?.label).join(', ')
+ }, [value, labelList])
const selectLabel = (label: Label) => {
if (value.includes(label.name))
@@ -60,12 +53,6 @@ const LabelSelector: FC = ({
onChange([...value, label.name])
}
- useMount(() => {
- fetchLabelList().then((res) => {
- setLabelList(res)
- })
- })
-
return (
= ({
checked={value.includes(label.name)}
onCheck={() => { }}
/>
- {label.label[language]}
+ {label.label}
))}
{!filteredLabelList.length && (
diff --git a/web/app/components/tools/labels/store.ts b/web/app/components/tools/labels/store.ts
deleted file mode 100644
index c19991dfd4..0000000000
--- a/web/app/components/tools/labels/store.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { create } from 'zustand'
-import type { Label } from './constant'
-
-type State = {
- labelList: Label[]
-}
-
-type Action = {
- setLabelList: (labelList?: Label[]) => void
-}
-
-export const useStore = create(set => ({
- labelList: [],
- setLabelList: labelList => set(() => ({ labelList })),
-}))
diff --git a/web/app/components/tools/marketplace/hooks.ts b/web/app/components/tools/marketplace/hooks.ts
index d1558e7aaf..82f019ef14 100644
--- a/web/app/components/tools/marketplace/hooks.ts
+++ b/web/app/components/tools/marketplace/hooks.ts
@@ -37,7 +37,7 @@ export const useMarketplace = (searchPluginText: string, filterPluginTags: strin
}
else {
queryMarketplaceCollectionsAndPlugins()
- setPlugins([])
+ setPlugins(undefined)
}
}, [searchPluginText, filterPluginTags, queryPlugins, queryMarketplaceCollectionsAndPlugins, queryPluginsWithDebounced, setPlugins])
diff --git a/web/context/modal-context.tsx b/web/context/modal-context.tsx
index 60d53f1e98..622077ee91 100644
--- a/web/context/modal-context.tsx
+++ b/web/context/modal-context.tsx
@@ -31,8 +31,10 @@ import ModelLoadBalancingModal from '@/app/components/header/account-setting/mod
import OpeningSettingModal from '@/app/components/base/features/new-feature-panel/conversation-opener/modal'
import type { OpeningStatement } from '@/app/components/base/features/types'
import type { InputVar } from '@/app/components/workflow/types'
+import type { UpdatePluginPayload } from '@/app/components/plugins/types'
+import UpdatePlugin from '@/app/components/plugins/update-plugin'
-export interface ModalState {
+export type ModalState = {
payload: T
onCancelCallback?: () => void
onSaveCallback?: (newPayload: T) => void
@@ -43,7 +45,7 @@ export interface ModalState {
datasetBindings?: { id: string; name: string }[]
}
-export interface ModelModalType {
+export type ModelModalType = {
currentProvider: ModelProvider
currentConfigurationMethod: ConfigurationMethodEnum
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
@@ -52,7 +54,8 @@ export type LoadBalancingEntryModalType = ModelModalType & {
entry?: ModelLoadBalancingConfigEntry
index?: number
}
-export interface ModalContextState {
+
+export type ModalContextState = {
setShowAccountSettingModal: Dispatch | null>>
setShowApiBasedExtensionModal: Dispatch | null>>
setShowModerationSettingModal: Dispatch | null>>
@@ -68,6 +71,7 @@ export interface ModalContextState {
workflowVariables?: InputVar[]
onAutoAddPromptVariable?: (variable: PromptVariable[]) => void
}> | null>>
+ setShowUpdatePluginModal: Dispatch | null>>
}
const ModalContext = createContext({
setShowAccountSettingModal: () => { },
@@ -81,6 +85,7 @@ const ModalContext = createContext({
setShowModelLoadBalancingModal: () => { },
setShowModelLoadBalancingEntryModal: () => { },
setShowOpeningModal: () => { },
+ setShowUpdatePluginModal: () => { },
})
export const useModalContext = () => useContext(ModalContext)
@@ -90,7 +95,7 @@ export const useModalContext = () => useContext(ModalContext)
export const useModalContextSelector = (selector: (state: ModalContextState) => T): T =>
useContextSelector(ModalContext, selector)
-interface ModalContextProviderProps {
+type ModalContextProviderProps = {
children: React.ReactNode
}
export const ModalContextProvider = ({
@@ -109,6 +114,8 @@ export const ModalContextProvider = ({
workflowVariables?: InputVar[]
onAutoAddPromptVariable?: (variable: PromptVariable[]) => void
}> | null>(null)
+ const [showUpdatePluginModal, setShowUpdatePluginModal] = useState | null>(null)
+
const searchParams = useSearchParams()
const router = useRouter()
const [showPricingModal, setShowPricingModal] = useState(searchParams.get('show-pricing') === '1')
@@ -228,6 +235,7 @@ export const ModalContextProvider = ({
setShowModelLoadBalancingModal,
setShowModelLoadBalancingEntryModal,
setShowOpeningModal,
+ setShowUpdatePluginModal,
}}>
<>
{children}
@@ -338,6 +346,22 @@ export const ModalContextProvider = ({
onAutoAddPromptVariable={showOpeningModal.payload.onAutoAddPromptVariable}
/>
)}
+
+ {
+ !!showUpdatePluginModal && (
+ {
+ setShowUpdatePluginModal(null)
+ showUpdatePluginModal.onCancelCallback?.()
+ }}
+ onSave={() => {
+ setShowUpdatePluginModal(null)
+ showUpdatePluginModal.onSaveCallback?.({} as any)
+ }}
+ />
+ )
+ }
>
)
diff --git a/web/i18n/de-DE/plugin-tags.ts b/web/i18n/de-DE/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/de-DE/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/en-US/plugin-tags.ts b/web/i18n/en-US/plugin-tags.ts
new file mode 100644
index 0000000000..6eca3ac8a2
--- /dev/null
+++ b/web/i18n/en-US/plugin-tags.ts
@@ -0,0 +1,20 @@
+const translation = {
+ search: 'Search',
+ image: 'Image',
+ videos: 'Videos',
+ weather: 'Weather',
+ finance: 'Finance',
+ design: 'Design',
+ travel: 'Travel',
+ social: 'Social',
+ news: 'News',
+ medical: 'Medical',
+ productivity: 'Productivity',
+ education: 'Education',
+ business: 'Business',
+ entertainment: 'Entertainment',
+ utilities: 'Utilities',
+ other: 'Other',
+}
+
+export default translation
diff --git a/web/i18n/es-ES/plugin-tags.ts b/web/i18n/es-ES/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/es-ES/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/fa-IR/plugin-tags.ts b/web/i18n/fa-IR/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/fa-IR/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/fr-FR/plugin-tags.ts b/web/i18n/fr-FR/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/fr-FR/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/hi-IN/plugin-tags.ts b/web/i18n/hi-IN/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/hi-IN/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/i18next-config.ts b/web/i18n/i18next-config.ts
index be8b4c46e1..bbba4c7c35 100644
--- a/web/i18n/i18next-config.ts
+++ b/web/i18n/i18next-config.ts
@@ -29,6 +29,7 @@ const loadLangResources = (lang: string) => ({
workflow: require(`./${lang}/workflow`).default,
runLog: require(`./${lang}/run-log`).default,
plugin: require(`./${lang}/plugin`).default,
+ pluginTags: require(`./${lang}/plugin-tags`).default,
},
})
diff --git a/web/i18n/it-IT/plugin-tags.ts b/web/i18n/it-IT/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/it-IT/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/ja-JP/plugin-tags.ts b/web/i18n/ja-JP/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/ja-JP/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/ko-KR/plugin-tags.ts b/web/i18n/ko-KR/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/ko-KR/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/pl-PL/plugin-tags.ts b/web/i18n/pl-PL/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/pl-PL/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/pt-BR/plugin-tags.ts b/web/i18n/pt-BR/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/pt-BR/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/ro-RO/plugin-tags.ts b/web/i18n/ro-RO/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/ro-RO/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/ru-RU/plugin-tags.ts b/web/i18n/ru-RU/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/ru-RU/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/tr-TR/plugin-tags.ts b/web/i18n/tr-TR/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/tr-TR/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/uk-UA/plugin-tags.ts b/web/i18n/uk-UA/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/uk-UA/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/vi-VN/plugin-tags.ts b/web/i18n/vi-VN/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/vi-VN/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation
diff --git a/web/i18n/zh-Hans/plugin-tags.ts b/web/i18n/zh-Hans/plugin-tags.ts
new file mode 100644
index 0000000000..f8251d339d
--- /dev/null
+++ b/web/i18n/zh-Hans/plugin-tags.ts
@@ -0,0 +1,20 @@
+const translation = {
+ search: '搜索',
+ image: '图片',
+ videos: '视频',
+ weather: '天气',
+ finance: '金融',
+ design: '设计',
+ travel: '旅行',
+ social: '社交',
+ news: '新闻',
+ medical: '医疗',
+ productivity: '生产力',
+ education: '教育',
+ business: '商业',
+ entertainment: '娱乐',
+ utilities: '工具',
+ other: '其他',
+}
+
+export default translation
diff --git a/web/i18n/zh-Hant/plugin-tags.ts b/web/i18n/zh-Hant/plugin-tags.ts
new file mode 100644
index 0000000000..928649474b
--- /dev/null
+++ b/web/i18n/zh-Hant/plugin-tags.ts
@@ -0,0 +1,4 @@
+const translation = {
+}
+
+export default translation