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