Merge branch 'feat/plugins' of github.com:langgenius/dify into feat/plugins
This commit is contained in:
commit
912f84777b
@ -10,6 +10,7 @@ import Description from './base/description'
|
||||
import Placeholder from './base/placeholder'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
import { getLanguage } from '@/i18n/language'
|
||||
|
||||
export type Props = {
|
||||
className?: string
|
||||
@ -22,6 +23,7 @@ export type Props = {
|
||||
footer?: React.ReactNode
|
||||
isLoading?: boolean
|
||||
loadingFileName?: string
|
||||
locale?: string
|
||||
}
|
||||
|
||||
const Card = ({
|
||||
@ -35,8 +37,10 @@ const Card = ({
|
||||
footer,
|
||||
isLoading = false,
|
||||
loadingFileName,
|
||||
locale: localeFromProps,
|
||||
}: Props) => {
|
||||
const locale = useGetLanguage()
|
||||
const defaultLocale = useGetLanguage()
|
||||
const locale = localeFromProps ? getLanguage(localeFromProps) : defaultLocale
|
||||
|
||||
const { type, name, org, label, brief, icon, verified } = payload
|
||||
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { TFunction } from 'i18next'
|
||||
|
||||
type Tag = {
|
||||
name: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export const useTags = () => {
|
||||
const { t } = useTranslation()
|
||||
export const useTags = (translateFromOut?: TFunction) => {
|
||||
const { t: translation } = useTranslation()
|
||||
const t = translateFromOut || translation
|
||||
|
||||
const tags = [
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import type { PluginDeclaration, PluginManifestInMarket } from '../../types'
|
||||
import type { Plugin, PluginDeclaration, PluginManifestInMarket } from '../../types'
|
||||
import Card from '../../card'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { pluginManifestInMarketToPluginProps, pluginManifestToCardPluginProps } from '../utils'
|
||||
@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import Badge, { BadgeState } from '@/app/components/base/badge/index'
|
||||
|
||||
type Props = {
|
||||
payload?: PluginDeclaration | PluginManifestInMarket | null
|
||||
payload?: Plugin | PluginDeclaration | PluginManifestInMarket | null
|
||||
isMarketPayload?: boolean
|
||||
isFailed: boolean
|
||||
errMsg?: string | null
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import type { PluginManifestInMarket } from '../../types'
|
||||
import type { Plugin, PluginManifestInMarket } from '../../types'
|
||||
import { InstallStep } from '../../types'
|
||||
import Install from './steps/install'
|
||||
import Installed from '../base/installed'
|
||||
@ -12,7 +12,7 @@ const i18nPrefix = 'plugin.installModal'
|
||||
|
||||
type InstallFromMarketplaceProps = {
|
||||
uniqueIdentifier: string
|
||||
manifest: PluginManifestInMarket
|
||||
manifest: PluginManifestInMarket | Plugin
|
||||
onSuccess: () => void
|
||||
onClose: () => void
|
||||
}
|
||||
@ -36,7 +36,7 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
|
||||
if (step === InstallStep.installFailed)
|
||||
return t(`${i18nPrefix}.installFailed`)
|
||||
return t(`${i18nPrefix}.installPlugin`)
|
||||
}, [step])
|
||||
}, [step, t])
|
||||
|
||||
const handleInstalled = useCallback(() => {
|
||||
setStep(InstallStep.installed)
|
||||
|
@ -2,7 +2,7 @@
|
||||
import type { FC } from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import { RiInformation2Line } from '@remixicon/react'
|
||||
import type { PluginManifestInMarket } from '../../../types'
|
||||
import type { Plugin, PluginManifestInMarket } from '../../../types'
|
||||
import Card from '../../../card'
|
||||
import { pluginManifestInMarketToPluginProps } from '../../utils'
|
||||
import Button from '@/app/components/base/button'
|
||||
@ -16,7 +16,7 @@ const i18nPrefix = 'plugin.installModal'
|
||||
|
||||
type Props = {
|
||||
uniqueIdentifier: string
|
||||
payload: PluginManifestInMarket
|
||||
payload: PluginManifestInMarket | Plugin
|
||||
onCancel: () => void
|
||||
onStartToInstall?: () => void
|
||||
onInstalled: () => void
|
||||
@ -104,7 +104,7 @@ const Installed: FC<Props> = ({
|
||||
<div className='flex p-2 items-start content-start gap-1 self-stretch flex-wrap rounded-2xl bg-background-section-burn'>
|
||||
<Card
|
||||
className='w-full'
|
||||
payload={pluginManifestInMarketToPluginProps(payload)}
|
||||
payload={pluginManifestInMarketToPluginProps(payload as PluginManifestInMarket)}
|
||||
titleLeft={versionInfo}
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,4 +1,17 @@
|
||||
const Description = () => {
|
||||
import {
|
||||
getLocaleOnServer,
|
||||
useTranslation as translate,
|
||||
} from '@/i18n/server'
|
||||
|
||||
type DescriptionProps = {
|
||||
locale?: string
|
||||
}
|
||||
const Description = async ({
|
||||
locale: localeFromProps,
|
||||
}: DescriptionProps) => {
|
||||
const localeDefault = getLocaleOnServer()
|
||||
const { t } = await translate(localeFromProps || localeDefault, 'plugin')
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className='mb-2 text-center title-4xl-semi-bold text-text-primary'>
|
||||
@ -7,19 +20,19 @@ const Description = () => {
|
||||
<h2 className='flex justify-center items-center text-center body-md-regular text-text-tertiary'>
|
||||
Discover
|
||||
<span className="relative ml-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
|
||||
models
|
||||
{t('category.models')}
|
||||
</span>
|
||||
,
|
||||
<span className="relative ml-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
|
||||
tools
|
||||
{t('category.tools')}
|
||||
</span>
|
||||
,
|
||||
<span className="relative ml-1 mr-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
|
||||
extensions
|
||||
{t('category.extensions')}
|
||||
</span>
|
||||
and
|
||||
<span className="relative ml-1 mr-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
|
||||
bundles
|
||||
{t('category.bundles')}
|
||||
</span>
|
||||
in Dify Marketplace
|
||||
</h2>
|
||||
|
@ -2,6 +2,7 @@ import {
|
||||
useCallback,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDebounceFn } from 'ahooks'
|
||||
import type { Plugin } from '../types'
|
||||
import type {
|
||||
@ -13,6 +14,7 @@ import {
|
||||
getMarketplaceCollectionsAndPlugins,
|
||||
getMarketplacePlugins,
|
||||
} from './utils'
|
||||
import i18n from '@/i18n/i18next-config'
|
||||
|
||||
export const useMarketplaceCollectionsAndPlugins = () => {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
@ -63,3 +65,14 @@ export const useMarketplacePlugins = () => {
|
||||
setIsLoading,
|
||||
}
|
||||
}
|
||||
|
||||
export const useMixedTranslation = (localeFromOuter?: string) => {
|
||||
let t = useTranslation().t
|
||||
|
||||
if (localeFromOuter)
|
||||
t = i18n.getFixedT(localeFromOuter)
|
||||
|
||||
return {
|
||||
t,
|
||||
}
|
||||
}
|
||||
|
@ -7,20 +7,23 @@ import ListWrapper from './list/list-wrapper'
|
||||
import { getMarketplaceCollectionsAndPlugins } from './utils'
|
||||
|
||||
type MarketplaceProps = {
|
||||
locale?: string
|
||||
showInstallButton?: boolean
|
||||
}
|
||||
const Marketplace = async ({
|
||||
locale,
|
||||
showInstallButton = true,
|
||||
}: MarketplaceProps) => {
|
||||
const { marketplaceCollections, marketplaceCollectionPluginsMap } = await getMarketplaceCollectionsAndPlugins()
|
||||
|
||||
return (
|
||||
<MarketplaceContextProvider>
|
||||
<Description />
|
||||
<Description locale={locale} />
|
||||
<IntersectionLine />
|
||||
<SearchBoxWrapper />
|
||||
<PluginTypeSwitch />
|
||||
<SearchBoxWrapper locale={locale} />
|
||||
<PluginTypeSwitch locale={locale} />
|
||||
<ListWrapper
|
||||
locale={locale}
|
||||
marketplaceCollections={marketplaceCollections}
|
||||
marketplaceCollectionPluginsMap={marketplaceCollectionPluginsMap}
|
||||
showInstallButton={showInstallButton}
|
||||
|
@ -1,26 +1,42 @@
|
||||
'use client'
|
||||
import { RiArrowRightUpLine } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Card from '@/app/components/plugins/card'
|
||||
import CardMoreInfo from '@/app/components/plugins/card/card-more-info'
|
||||
import type { Plugin } from '@/app/components/plugins/types'
|
||||
import { MARKETPLACE_URL_PREFIX } from '@/config'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useMixedTranslation } from '@/app/components/plugins/marketplace/hooks'
|
||||
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
||||
import { useBoolean } from 'ahooks'
|
||||
|
||||
type CardWrapperProps = {
|
||||
plugin: Plugin
|
||||
showInstallButton?: boolean
|
||||
locale?: string
|
||||
}
|
||||
const CardWrapper = ({
|
||||
plugin,
|
||||
showInstallButton,
|
||||
locale,
|
||||
}: CardWrapperProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useMixedTranslation(locale)
|
||||
const [isShowInstallFromMarketplace, {
|
||||
setTrue: showInstallFromMarketplace,
|
||||
setFalse: hideInstallFromMarketplace,
|
||||
}] = useBoolean(false)
|
||||
|
||||
return (
|
||||
<div className='group relative rounded-xl cursor-pointer'>
|
||||
<div
|
||||
className='group relative rounded-xl cursor-pointer'
|
||||
onClick={() => {
|
||||
if (!showInstallButton)
|
||||
window.open(`${MARKETPLACE_URL_PREFIX}/plugin/${plugin.org}/${plugin.name}`)
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
key={plugin.name}
|
||||
payload={plugin}
|
||||
locale={locale}
|
||||
footer={
|
||||
<CardMoreInfo
|
||||
downloadCount={plugin.install_count}
|
||||
@ -34,6 +50,7 @@ const CardWrapper = ({
|
||||
<Button
|
||||
variant='primary'
|
||||
className='flex-1'
|
||||
onClick={showInstallFromMarketplace}
|
||||
>
|
||||
{t('plugin.detailPanel.operation.install')}
|
||||
</Button>
|
||||
@ -48,6 +65,16 @@ const CardWrapper = ({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
isShowInstallFromMarketplace && (
|
||||
<InstallFromMarketplace
|
||||
manifest={plugin as any}
|
||||
uniqueIdentifier={plugin.latest_package_identifier}
|
||||
onClose={hideInstallFromMarketplace}
|
||||
onSuccess={hideInstallFromMarketplace}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -10,12 +10,14 @@ type ListProps = {
|
||||
marketplaceCollectionPluginsMap: Record<string, Plugin[]>
|
||||
plugins?: Plugin[]
|
||||
showInstallButton?: boolean
|
||||
locale?: string
|
||||
}
|
||||
const List = ({
|
||||
marketplaceCollections,
|
||||
marketplaceCollectionPluginsMap,
|
||||
plugins,
|
||||
showInstallButton,
|
||||
locale,
|
||||
}: ListProps) => {
|
||||
return (
|
||||
<>
|
||||
@ -25,6 +27,7 @@ const List = ({
|
||||
marketplaceCollections={marketplaceCollections}
|
||||
marketplaceCollectionPluginsMap={marketplaceCollectionPluginsMap}
|
||||
showInstallButton={showInstallButton}
|
||||
locale={locale}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -37,6 +40,7 @@ const List = ({
|
||||
key={plugin.name}
|
||||
plugin={plugin}
|
||||
showInstallButton={showInstallButton}
|
||||
locale={locale}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
@ -7,11 +7,13 @@ type ListWithCollectionProps = {
|
||||
marketplaceCollections: MarketplaceCollection[]
|
||||
marketplaceCollectionPluginsMap: Record<string, Plugin[]>
|
||||
showInstallButton?: boolean
|
||||
locale?: string
|
||||
}
|
||||
const ListWithCollection = ({
|
||||
marketplaceCollections,
|
||||
marketplaceCollectionPluginsMap,
|
||||
showInstallButton,
|
||||
locale,
|
||||
}: ListWithCollectionProps) => {
|
||||
return (
|
||||
<>
|
||||
@ -30,6 +32,7 @@ const ListWithCollection = ({
|
||||
key={plugin.name}
|
||||
plugin={plugin}
|
||||
showInstallButton={showInstallButton}
|
||||
locale={locale}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
@ -9,11 +9,13 @@ type ListWrapperProps = {
|
||||
marketplaceCollections: MarketplaceCollection[]
|
||||
marketplaceCollectionPluginsMap: Record<string, Plugin[]>
|
||||
showInstallButton?: boolean
|
||||
locale?: string
|
||||
}
|
||||
const ListWrapper = ({
|
||||
marketplaceCollections,
|
||||
marketplaceCollectionPluginsMap,
|
||||
showInstallButton,
|
||||
locale,
|
||||
}: ListWrapperProps) => {
|
||||
const plugins = useMarketplaceContext(v => v.plugins)
|
||||
const marketplaceCollectionsFromClient = useMarketplaceContext(v => v.marketplaceCollectionsFromClient)
|
||||
@ -35,6 +37,7 @@ const ListWrapper = ({
|
||||
marketplaceCollectionPluginsMap={marketplaceCollectionPluginsMapFromClient || marketplaceCollectionPluginsMap}
|
||||
plugins={plugins}
|
||||
showInstallButton={showInstallButton}
|
||||
locale={locale}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
@ -1,5 +1,4 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
RiArchive2Line,
|
||||
RiBrain2Line,
|
||||
@ -8,6 +7,7 @@ import {
|
||||
} from '@remixicon/react'
|
||||
import { PluginType } from '../types'
|
||||
import { useMarketplaceContext } from './context'
|
||||
import { useMixedTranslation } from './hooks'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
export const PLUGIN_TYPE_SEARCH_MAP = {
|
||||
@ -17,37 +17,44 @@ export const PLUGIN_TYPE_SEARCH_MAP = {
|
||||
extension: PluginType.extension,
|
||||
bundle: 'bundle',
|
||||
}
|
||||
const options = [
|
||||
{
|
||||
value: PLUGIN_TYPE_SEARCH_MAP.all,
|
||||
text: 'All',
|
||||
icon: null,
|
||||
},
|
||||
{
|
||||
value: PLUGIN_TYPE_SEARCH_MAP.model,
|
||||
text: 'Models',
|
||||
icon: <RiBrain2Line className='mr-1.5 w-4 h-4' />,
|
||||
},
|
||||
{
|
||||
value: PLUGIN_TYPE_SEARCH_MAP.tool,
|
||||
text: 'Tools',
|
||||
icon: <RiHammerLine className='mr-1.5 w-4 h-4' />,
|
||||
},
|
||||
{
|
||||
value: PLUGIN_TYPE_SEARCH_MAP.extension,
|
||||
text: 'Extensions',
|
||||
icon: <RiPuzzle2Line className='mr-1.5 w-4 h-4' />,
|
||||
},
|
||||
{
|
||||
value: PLUGIN_TYPE_SEARCH_MAP.bundle,
|
||||
text: 'Bundles',
|
||||
icon: <RiArchive2Line className='mr-1.5 w-4 h-4' />,
|
||||
},
|
||||
]
|
||||
const PluginTypeSwitch = () => {
|
||||
type PluginTypeSwitchProps = {
|
||||
locale?: string
|
||||
}
|
||||
const PluginTypeSwitch = ({
|
||||
locale,
|
||||
}: PluginTypeSwitchProps) => {
|
||||
const { t } = useMixedTranslation(locale)
|
||||
const activePluginType = useMarketplaceContext(s => s.activePluginType)
|
||||
const handleActivePluginTypeChange = useMarketplaceContext(s => s.handleActivePluginTypeChange)
|
||||
|
||||
const options = [
|
||||
{
|
||||
value: PLUGIN_TYPE_SEARCH_MAP.all,
|
||||
text: 'All',
|
||||
icon: null,
|
||||
},
|
||||
{
|
||||
value: PLUGIN_TYPE_SEARCH_MAP.model,
|
||||
text: t('plugin.category.models'),
|
||||
icon: <RiBrain2Line className='mr-1.5 w-4 h-4' />,
|
||||
},
|
||||
{
|
||||
value: PLUGIN_TYPE_SEARCH_MAP.tool,
|
||||
text: t('plugin.category.tools'),
|
||||
icon: <RiHammerLine className='mr-1.5 w-4 h-4' />,
|
||||
},
|
||||
{
|
||||
value: PLUGIN_TYPE_SEARCH_MAP.extension,
|
||||
text: t('plugin.category.extensions'),
|
||||
icon: <RiPuzzle2Line className='mr-1.5 w-4 h-4' />,
|
||||
},
|
||||
{
|
||||
value: PLUGIN_TYPE_SEARCH_MAP.bundle,
|
||||
text: t('plugin.category.bundles'),
|
||||
icon: <RiArchive2Line className='mr-1.5 w-4 h-4' />,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
'sticky top-[60px] flex items-center justify-center py-3 bg-background-body space-x-2 z-10',
|
||||
|
@ -12,6 +12,7 @@ type SearchBoxProps = {
|
||||
onTagsChange: (tags: string[]) => void
|
||||
size?: 'small' | 'large'
|
||||
placeholder?: string
|
||||
locale?: string
|
||||
}
|
||||
const SearchBox = ({
|
||||
search,
|
||||
@ -20,7 +21,8 @@ const SearchBox = ({
|
||||
tags,
|
||||
onTagsChange,
|
||||
size = 'small',
|
||||
placeholder = 'Search tools...',
|
||||
placeholder = '',
|
||||
locale,
|
||||
}: SearchBoxProps) => {
|
||||
return (
|
||||
<div
|
||||
@ -35,6 +37,7 @@ const SearchBox = ({
|
||||
tags={tags}
|
||||
onTagsChange={onTagsChange}
|
||||
size={size}
|
||||
locale={locale}
|
||||
/>
|
||||
<div className='mx-1 w-[1px] h-3.5 bg-divider-regular'></div>
|
||||
<div className='grow flex items-center p-1 pl-2'>
|
||||
|
@ -1,9 +1,16 @@
|
||||
'use client'
|
||||
import { useMarketplaceContext } from '../context'
|
||||
import { useMixedTranslation } from '../hooks'
|
||||
import SearchBox from './index'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
const SearchBoxWrapper = () => {
|
||||
type SearchBoxWrapperProps = {
|
||||
locale?: string
|
||||
}
|
||||
const SearchBoxWrapper = ({
|
||||
locale,
|
||||
}: SearchBoxWrapperProps) => {
|
||||
const { t } = useMixedTranslation(locale)
|
||||
const intersected = useMarketplaceContext(v => v.intersected)
|
||||
const searchPluginText = useMarketplaceContext(v => v.searchPluginText)
|
||||
const handleSearchPluginTextChange = useMarketplaceContext(v => v.handleSearchPluginTextChange)
|
||||
@ -21,6 +28,8 @@ const SearchBoxWrapper = () => {
|
||||
tags={filterPluginTags}
|
||||
onTagsChange={handleFilterPluginTagsChange}
|
||||
size='large'
|
||||
locale={locale}
|
||||
placeholder={t('plugin.searchPlugins')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiArrowDownSLine,
|
||||
RiCloseCircleFill,
|
||||
@ -16,21 +15,24 @@ 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'
|
||||
import { useMixedTranslation } from '@/app/components/plugins/marketplace/hooks'
|
||||
|
||||
type TagsFilterProps = {
|
||||
tags: string[]
|
||||
onTagsChange: (tags: string[]) => void
|
||||
size: 'small' | 'large'
|
||||
locale?: string
|
||||
}
|
||||
const TagsFilter = ({
|
||||
tags,
|
||||
onTagsChange,
|
||||
size,
|
||||
locale,
|
||||
}: TagsFilterProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useMixedTranslation(locale)
|
||||
const [open, setOpen] = useState(false)
|
||||
const [searchText, setSearchText] = useState('')
|
||||
const { tags: options, tagsMap } = useTags()
|
||||
const { tags: options, tagsMap } = useTags(t)
|
||||
const filteredOptions = options.filter(option => option.label.toLowerCase().includes(searchText.toLowerCase()))
|
||||
const handleCheck = (id: string) => {
|
||||
if (tags.includes(id))
|
||||
|
@ -52,13 +52,14 @@ const DetailHeader = ({
|
||||
meta,
|
||||
} = detail
|
||||
const { author, name, label, description, icon, verified } = detail.declaration
|
||||
const isFromGitHub = source === PluginSource.github
|
||||
// Only plugin installed from GitHub need to check if it's the new version
|
||||
const hasNewVersion = useMemo(() => {
|
||||
return source === PluginSource.github && latest_version !== version
|
||||
}, [source, latest_version, version])
|
||||
|
||||
// #plugin TODO# update plugin
|
||||
const handleUpdate = () => {}
|
||||
const handleUpdate = () => { }
|
||||
|
||||
const [isShowPluginInfo, {
|
||||
setTrue: showPluginInfo,
|
||||
@ -151,7 +152,7 @@ const DetailHeader = ({
|
||||
<Description className='mt-3' text={description[locale]} descriptionLineRows={2}></Description>
|
||||
{isShowPluginInfo && (
|
||||
<PluginInfo
|
||||
repository={meta?.repo}
|
||||
repository={isFromGitHub ? meta?.repo : ''}
|
||||
release={version}
|
||||
packageName={meta?.package}
|
||||
onHide={hidePluginInfo}
|
||||
|
@ -21,7 +21,6 @@ import Title from '../card/base/title'
|
||||
import Action from './action'
|
||||
import cn from '@/utils/classnames'
|
||||
import I18n from '@/context/i18n'
|
||||
|
||||
import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config'
|
||||
|
||||
type Props = {
|
||||
|
@ -7,9 +7,9 @@ import Modal from '../../base/modal'
|
||||
|
||||
const i18nPrefix = 'plugin.pluginInfoModal'
|
||||
type Props = {
|
||||
repository: string
|
||||
release: string
|
||||
packageName: string
|
||||
repository?: string
|
||||
release?: string
|
||||
packageName?: string
|
||||
onHide: () => void
|
||||
}
|
||||
|
||||
@ -30,9 +30,9 @@ const PlugInfo: FC<Props> = ({
|
||||
closable
|
||||
>
|
||||
<div className='mt-5 space-y-3'>
|
||||
<KeyValueItem label={t(`${i18nPrefix}.repository`)} labelWidthClassName={labelWidthClassName} value={repository} />
|
||||
<KeyValueItem label={t(`${i18nPrefix}.release`)} labelWidthClassName={labelWidthClassName} value={release} />
|
||||
<KeyValueItem label={t(`${i18nPrefix}.packageName`)} labelWidthClassName={labelWidthClassName} value={packageName} />
|
||||
{repository && <KeyValueItem label={t(`${i18nPrefix}.repository`)} labelWidthClassName={labelWidthClassName} value={repository} />}
|
||||
{release && <KeyValueItem label={t(`${i18nPrefix}.release`)} labelWidthClassName={labelWidthClassName} value={release} />}
|
||||
{packageName && <KeyValueItem label={t(`${i18nPrefix}.packageName`)} labelWidthClassName={labelWidthClassName} value={packageName} />}
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ import { useDebounceFn } from 'ahooks'
|
||||
import Empty from './empty'
|
||||
|
||||
const PluginsPanel = () => {
|
||||
const [filters, setFilters] = usePluginPageContext(v => [v.filters, v.setFilters])
|
||||
const [filters, setFilters] = usePluginPageContext(v => [v.filters, v.setFilters]) as [FilterState, (filter: FilterState) => void]
|
||||
const pluginList = usePluginPageContext(v => v.installedPluginList) as PluginDetail[]
|
||||
const mutateInstalledPluginList = usePluginPageContext(v => v.mutateInstalledPluginList)
|
||||
|
||||
@ -19,11 +19,11 @@ const PluginsPanel = () => {
|
||||
}, { wait: 500 })
|
||||
|
||||
const filteredList = useMemo(() => {
|
||||
// todo: filter by tags
|
||||
const { categories, searchQuery } = filters
|
||||
const { categories, searchQuery, tags } = filters
|
||||
const filteredList = pluginList.filter((plugin) => {
|
||||
return (
|
||||
(categories.length === 0 || categories.includes(plugin.declaration.category))
|
||||
&& (tags.length === 0 || tags.some(tag => plugin.declaration.tags.includes(tag)))
|
||||
&& (searchQuery === '' || plugin.plugin_id.toLowerCase().includes(searchQuery.toLowerCase()))
|
||||
)
|
||||
})
|
||||
|
@ -12,7 +12,9 @@ import DownloadCount from './card/base/download-count'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
import { MARKETPLACE_URL_PREFIX } from '@/config'
|
||||
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useBoolean } from 'ahooks'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
@ -24,6 +26,10 @@ const ProviderCard: FC<Props> = ({
|
||||
payload,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [isShowInstallFromMarketplace, {
|
||||
setTrue: showInstallFromMarketplace,
|
||||
setFalse: hideInstallFromMarketplace,
|
||||
}] = useBoolean(false)
|
||||
const language = useGetLanguage()
|
||||
const { org, label } = payload
|
||||
|
||||
@ -58,6 +64,7 @@ const ProviderCard: FC<Props> = ({
|
||||
<Button
|
||||
className='flex-grow'
|
||||
variant='primary'
|
||||
onClick={showInstallFromMarketplace}
|
||||
>
|
||||
{t('plugin.detailPanel.operation.install')}
|
||||
</Button>
|
||||
@ -71,6 +78,16 @@ const ProviderCard: FC<Props> = ({
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
{
|
||||
isShowInstallFromMarketplace && (
|
||||
<InstallFromMarketplace
|
||||
manifest={payload as any}
|
||||
uniqueIdentifier={payload.latest_package_identifier}
|
||||
onClose={hideInstallFromMarketplace}
|
||||
onSuccess={hideInstallFromMarketplace}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ export type PluginDeclaration = {
|
||||
endpoint: PluginEndpointDeclaration
|
||||
tool: PluginToolDeclaration
|
||||
model: any // TODO
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
export type PluginManifestInMarket = {
|
||||
@ -110,6 +111,7 @@ export type Plugin = {
|
||||
plugin_id: string
|
||||
version: string
|
||||
latest_version: string
|
||||
latest_package_identifier: string
|
||||
icon: string
|
||||
verified: boolean
|
||||
label: Record<Locale, string>
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { RiArrowUpDoubleLine } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useMarketplace } from './hooks'
|
||||
import List from '@/app/components/plugins/marketplace/list'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { getLocaleOnClient } from '@/i18n'
|
||||
|
||||
type MarketplaceProps = {
|
||||
searchPluginText: string
|
||||
@ -13,6 +15,8 @@ const Marketplace = ({
|
||||
filterPluginTags,
|
||||
onMarketplaceScroll,
|
||||
}: MarketplaceProps) => {
|
||||
const locale = getLocaleOnClient()
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
isLoading,
|
||||
marketplaceCollections,
|
||||
@ -31,19 +35,19 @@ const Marketplace = ({
|
||||
<div className='flex items-center text-center body-md-regular text-text-tertiary'>
|
||||
Discover
|
||||
<span className="relative ml-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
|
||||
models
|
||||
{t('plugin.category.models')}
|
||||
</span>
|
||||
,
|
||||
<span className="relative ml-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
|
||||
tools
|
||||
{t('plugin.category.tools')}
|
||||
</span>
|
||||
,
|
||||
<span className="relative ml-1 mr-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
|
||||
extensions
|
||||
{t('plugin.category.extensions')}
|
||||
</span>
|
||||
and
|
||||
<span className="relative ml-1 mr-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
|
||||
bundles
|
||||
{t('plugin.category.bundles')}
|
||||
</span>
|
||||
in Dify Marketplace
|
||||
</div>
|
||||
@ -62,6 +66,7 @@ const Marketplace = ({
|
||||
marketplaceCollectionPluginsMap={marketplaceCollectionPluginsMap || {}}
|
||||
plugins={plugins}
|
||||
showInstallButton
|
||||
locale={locale}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -1,4 +1,11 @@
|
||||
const translation = {
|
||||
category: {
|
||||
models: 'models',
|
||||
tools: 'tools',
|
||||
extensions: 'extensions',
|
||||
bundles: 'bundles',
|
||||
},
|
||||
searchPlugins: 'Search plugins',
|
||||
from: 'From',
|
||||
findMoreInMarketplace: 'Find more in Marketplace',
|
||||
searchInMarketplace: 'Search in Marketplace',
|
||||
|
@ -1,4 +1,11 @@
|
||||
const translation = {
|
||||
category: {
|
||||
models: '模型',
|
||||
tools: '工具',
|
||||
extensions: '扩展',
|
||||
bundles: '捆绑包',
|
||||
},
|
||||
searchPlugins: '搜索插件',
|
||||
from: '来自',
|
||||
findMoreInMarketplace: '在 Marketplace 中查找更多',
|
||||
searchInMarketplace: '在 Marketplace 中搜索',
|
||||
|
Loading…
Reference in New Issue
Block a user