mirror of
https://github.com/langgenius/dify.git
synced 2026-04-25 01:26:57 +08:00
Merge branch 'feat/plugins' into dev/plugin-deploy
This commit is contained in:
commit
d7657f4874
@ -20,7 +20,7 @@ const Badge = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'inline-flex items-center px-[5px] h-5 rounded-[5px] border border-divider-deep leading-3 text-text-tertiary',
|
'relative inline-flex items-center px-[5px] h-5 rounded-[5px] border border-divider-deep leading-3 text-text-tertiary',
|
||||||
uppercase ? 'system-2xs-medium-uppercase' : 'system-xs-medium',
|
uppercase ? 'system-2xs-medium-uppercase' : 'system-xs-medium',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const InstallButton = ({ loading, onInstall, t }: InstallButtonProps) => {
|
|||||||
{loading ? t('workflow.nodes.agent.pluginInstaller.installing') : t('workflow.nodes.agent.pluginInstaller.install')}
|
{loading ? t('workflow.nodes.agent.pluginInstaller.installing') : t('workflow.nodes.agent.pluginInstaller.install')}
|
||||||
</div>
|
</div>
|
||||||
{loading
|
{loading
|
||||||
? <RiLoader2Line className='w-3.5 h-3.5 text-text-quaternary' />
|
? <RiLoader2Line className='w-3.5 h-3.5 text-text-quaternary animate-spin' />
|
||||||
: <RiInstallLine className='w-3.5 h-3.5 text-text-secondary' />
|
: <RiInstallLine className='w-3.5 h-3.5 text-text-secondary' />
|
||||||
}
|
}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -41,6 +41,7 @@
|
|||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
.qrcodeform {
|
.qrcodeform {
|
||||||
|
z-index: 50;
|
||||||
border: 0.5px solid #eaecf0;
|
border: 0.5px solid #eaecf0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import type {
|
|||||||
} from './declarations'
|
} from './declarations'
|
||||||
import {
|
import {
|
||||||
ConfigurationMethodEnum,
|
ConfigurationMethodEnum,
|
||||||
|
CustomConfigurationStatusEnum,
|
||||||
ModelStatusEnum,
|
ModelStatusEnum,
|
||||||
} from './declarations'
|
} from './declarations'
|
||||||
import I18n from '@/context/i18n'
|
import I18n from '@/context/i18n'
|
||||||
@ -33,6 +34,9 @@ import {
|
|||||||
import type { Plugin } from '@/app/components/plugins/types'
|
import type { Plugin } from '@/app/components/plugins/types'
|
||||||
import { PluginType } from '@/app/components/plugins/types'
|
import { PluginType } from '@/app/components/plugins/types'
|
||||||
import { getMarketplacePluginsByCollectionId } from '@/app/components/plugins/marketplace/utils'
|
import { getMarketplacePluginsByCollectionId } from '@/app/components/plugins/marketplace/utils'
|
||||||
|
import { useModalContextSelector } from '@/context/modal-context'
|
||||||
|
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||||
|
import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './provider-added-card'
|
||||||
|
|
||||||
type UseDefaultModelAndModelList = (
|
type UseDefaultModelAndModelList = (
|
||||||
defaultModel: DefaultModelResponse | undefined,
|
defaultModel: DefaultModelResponse | undefined,
|
||||||
@ -304,3 +308,42 @@ export const useMarketplaceAllPlugins = (providers: ModelProvider[], searchText:
|
|||||||
isLoading,
|
isLoading,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useModelModalHandler = () => {
|
||||||
|
const setShowModelModal = useModalContextSelector(state => state.setShowModelModal)
|
||||||
|
const updateModelProviders = useUpdateModelProviders()
|
||||||
|
const updateModelList = useUpdateModelList()
|
||||||
|
const { eventEmitter } = useEventEmitterContextContext()
|
||||||
|
|
||||||
|
return (
|
||||||
|
provider: ModelProvider,
|
||||||
|
configurationMethod: ConfigurationMethodEnum,
|
||||||
|
CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
|
) => {
|
||||||
|
setShowModelModal({
|
||||||
|
payload: {
|
||||||
|
currentProvider: provider,
|
||||||
|
currentConfigurationMethod: configurationMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
||||||
|
},
|
||||||
|
onSaveCallback: () => {
|
||||||
|
updateModelProviders()
|
||||||
|
|
||||||
|
provider.supported_model_types.forEach((type) => {
|
||||||
|
updateModelList(type)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (configurationMethod === ConfigurationMethodEnum.customizableModel
|
||||||
|
&& provider.custom_configuration.status === CustomConfigurationStatusEnum.active) {
|
||||||
|
eventEmitter?.emit({
|
||||||
|
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
|
||||||
|
payload: provider.provider,
|
||||||
|
} as any)
|
||||||
|
|
||||||
|
if (CustomConfigurationModelFixedFields?.__model_type)
|
||||||
|
updateModelList(CustomConfigurationModelFixedFields.__model_type)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -9,19 +9,21 @@ import {
|
|||||||
RiBrainLine,
|
RiBrainLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import SystemModelSelector from './system-model-selector'
|
import SystemModelSelector from './system-model-selector'
|
||||||
import ProviderAddedCard, { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './provider-added-card'
|
import ProviderAddedCard from './provider-added-card'
|
||||||
import type {
|
import type {
|
||||||
|
ConfigurationMethodEnum,
|
||||||
CustomConfigurationModelFixedFields,
|
CustomConfigurationModelFixedFields,
|
||||||
|
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from './declarations'
|
} from './declarations'
|
||||||
import {
|
import {
|
||||||
ConfigurationMethodEnum,
|
|
||||||
CustomConfigurationStatusEnum,
|
CustomConfigurationStatusEnum,
|
||||||
ModelTypeEnum,
|
ModelTypeEnum,
|
||||||
} from './declarations'
|
} from './declarations'
|
||||||
import {
|
import {
|
||||||
useDefaultModel,
|
useDefaultModel,
|
||||||
useMarketplaceAllPlugins,
|
useMarketplaceAllPlugins,
|
||||||
|
useModelModalHandler,
|
||||||
useUpdateModelList,
|
useUpdateModelList,
|
||||||
useUpdateModelProviders,
|
useUpdateModelProviders,
|
||||||
} from './hooks'
|
} from './hooks'
|
||||||
@ -87,37 +89,7 @@ const ModelProviderPage = ({ searchText }: Props) => {
|
|||||||
return [filteredConfiguredProviders, filteredNotConfiguredProviders]
|
return [filteredConfiguredProviders, filteredNotConfiguredProviders]
|
||||||
}, [configuredProviders, debouncedSearchText, notConfiguredProviders])
|
}, [configuredProviders, debouncedSearchText, notConfiguredProviders])
|
||||||
|
|
||||||
const handleOpenModal = (
|
const handleOpenModal = useModelModalHandler()
|
||||||
provider: ModelProvider,
|
|
||||||
configurationMethod: ConfigurationMethodEnum,
|
|
||||||
CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
|
||||||
) => {
|
|
||||||
setShowModelModal({
|
|
||||||
payload: {
|
|
||||||
currentProvider: provider,
|
|
||||||
currentConfigurationMethod: configurationMethod,
|
|
||||||
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
|
||||||
},
|
|
||||||
onSaveCallback: () => {
|
|
||||||
updateModelProviders()
|
|
||||||
|
|
||||||
provider.supported_model_types.forEach((type) => {
|
|
||||||
updateModelList(type)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (configurationMethod === ConfigurationMethodEnum.customizableModel && provider.custom_configuration.status === CustomConfigurationStatusEnum.active) {
|
|
||||||
eventEmitter?.emit({
|
|
||||||
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
|
|
||||||
payload: provider.provider,
|
|
||||||
} as any)
|
|
||||||
|
|
||||||
if (CustomConfigurationModelFixedFields?.__model_type)
|
|
||||||
updateModelList(CustomConfigurationModelFixedFields?.__model_type)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const [collapse, setCollapse] = useState(false)
|
const [collapse, setCollapse] = useState(false)
|
||||||
const locale = getLocaleOnClient()
|
const locale = getLocaleOnClient()
|
||||||
const {
|
const {
|
||||||
|
|||||||
@ -2,27 +2,25 @@ import type { FC } from 'react'
|
|||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import type {
|
import type {
|
||||||
CustomConfigurationModelFixedFields,
|
|
||||||
ModelItem,
|
ModelItem,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '../declarations'
|
} from '../declarations'
|
||||||
import {
|
import {
|
||||||
ConfigurationMethodEnum,
|
|
||||||
CustomConfigurationStatusEnum,
|
CustomConfigurationStatusEnum,
|
||||||
ModelTypeEnum,
|
ModelTypeEnum,
|
||||||
} from '../declarations'
|
} from '../declarations'
|
||||||
import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from '../provider-added-card'
|
|
||||||
import type { PluginInfoFromMarketPlace } from '@/app/components/plugins/types'
|
import type { PluginInfoFromMarketPlace } from '@/app/components/plugins/types'
|
||||||
import { useInstallPackageFromMarketPlace } from '@/service/use-plugins'
|
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
||||||
import ConfigurationButton from './configuration-button'
|
import ConfigurationButton from './configuration-button'
|
||||||
import { PluginType } from '@/app/components/plugins/types'
|
import { PluginType } from '@/app/components/plugins/types'
|
||||||
import {
|
import {
|
||||||
|
useModelModalHandler,
|
||||||
useUpdateModelList,
|
useUpdateModelList,
|
||||||
useUpdateModelProviders,
|
useUpdateModelProviders,
|
||||||
} from '../hooks'
|
} from '../hooks'
|
||||||
import ModelIcon from '../model-icon'
|
import ModelIcon from '../model-icon'
|
||||||
import ModelDisplay from './model-display'
|
import ModelDisplay from './model-display'
|
||||||
import InstallButton from '@/app/components/base/install-button'
|
import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button'
|
||||||
import StatusIndicators from './status-indicators'
|
import StatusIndicators from './status-indicators'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
@ -72,10 +70,9 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
|||||||
}, [modelProviders, providerName])
|
}, [modelProviders, providerName])
|
||||||
const [pluginInfo, setPluginInfo] = useState<PluginInfoFromMarketPlace | null>(null)
|
const [pluginInfo, setPluginInfo] = useState<PluginInfoFromMarketPlace | null>(null)
|
||||||
const [isPluginChecked, setIsPluginChecked] = useState(false)
|
const [isPluginChecked, setIsPluginChecked] = useState(false)
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
const [installed, setInstalled] = useState(false)
|
const [installed, setInstalled] = useState(false)
|
||||||
const { mutateAsync: installPackageFromMarketPlace } = useInstallPackageFromMarketPlace()
|
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||||
|
const handleOpenModal = useModelModalHandler()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if (providerName && !modelProvider) {
|
if (providerName && !modelProvider) {
|
||||||
@ -101,66 +98,6 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
|||||||
if (modelId && !isPluginChecked)
|
if (modelId && !isPluginChecked)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
const handleOpenModal = (
|
|
||||||
provider: ModelProvider,
|
|
||||||
configurationMethod: ConfigurationMethodEnum,
|
|
||||||
CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
|
||||||
) => {
|
|
||||||
setShowModelModal({
|
|
||||||
payload: {
|
|
||||||
currentProvider: provider,
|
|
||||||
currentConfigurationMethod: configurationMethod,
|
|
||||||
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
|
||||||
},
|
|
||||||
onSaveCallback: () => {
|
|
||||||
updateModelProviders()
|
|
||||||
|
|
||||||
provider.supported_model_types.forEach((type) => {
|
|
||||||
updateModelList(type)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (configurationMethod === ConfigurationMethodEnum.customizableModel
|
|
||||||
&& provider.custom_configuration.status === CustomConfigurationStatusEnum.active) {
|
|
||||||
eventEmitter?.emit({
|
|
||||||
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
|
|
||||||
payload: provider.provider,
|
|
||||||
} as any)
|
|
||||||
|
|
||||||
if (CustomConfigurationModelFixedFields?.__model_type)
|
|
||||||
updateModelList(CustomConfigurationModelFixedFields.__model_type)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleInstall = async (pluginInfo: PluginInfoFromMarketPlace) => {
|
|
||||||
setLoading(true)
|
|
||||||
try {
|
|
||||||
const { all_installed } = await installPackageFromMarketPlace(pluginInfo.latest_package_identifier)
|
|
||||||
if (all_installed) {
|
|
||||||
[
|
|
||||||
ModelTypeEnum.textGeneration,
|
|
||||||
ModelTypeEnum.textEmbedding,
|
|
||||||
ModelTypeEnum.rerank,
|
|
||||||
ModelTypeEnum.moderation,
|
|
||||||
ModelTypeEnum.speech2text,
|
|
||||||
ModelTypeEnum.tts,
|
|
||||||
].forEach((type: ModelTypeEnum) => {
|
|
||||||
if (scope?.includes(type))
|
|
||||||
updateModelList(type)
|
|
||||||
})
|
|
||||||
updateModelProviders()
|
|
||||||
setInstalled(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.error('Installation failed:', error)
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -193,13 +130,27 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
|||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
{!installed && !modelProvider && pluginInfo && (
|
{!installed && !modelProvider && pluginInfo && (
|
||||||
<InstallButton
|
<InstallPluginButton
|
||||||
loading={loading}
|
onClick={e => e.stopPropagation()}
|
||||||
onInstall={(e) => {
|
size={'small'}
|
||||||
e.stopPropagation()
|
uniqueIdentifier={pluginInfo.latest_package_identifier}
|
||||||
handleInstall(pluginInfo)
|
onSuccess={() => {
|
||||||
|
[
|
||||||
|
ModelTypeEnum.textGeneration,
|
||||||
|
ModelTypeEnum.textEmbedding,
|
||||||
|
ModelTypeEnum.rerank,
|
||||||
|
ModelTypeEnum.moderation,
|
||||||
|
ModelTypeEnum.speech2text,
|
||||||
|
ModelTypeEnum.tts,
|
||||||
|
].forEach((type: ModelTypeEnum) => {
|
||||||
|
if (scope?.includes(type))
|
||||||
|
updateModelList(type)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
updateModelProviders()
|
||||||
|
invalidateInstalledPluginList()
|
||||||
|
setInstalled(true)
|
||||||
}}
|
}}
|
||||||
t={t}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{modelProvider && !disabled && !needsConfiguration && (
|
{modelProvider && !disabled && !needsConfiguration && (
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
@keyframes realistic-blink {
|
||||||
|
0% { fill: #37ff37; opacity: 0.4; }
|
||||||
|
15% { fill: #37ff37; opacity: 0.9; }
|
||||||
|
25% { fill: #37ff37; opacity: 0.3; }
|
||||||
|
38% { fill: #ff4444; opacity: 0.8; }
|
||||||
|
42% { fill: #ff4444; opacity: 0.3; }
|
||||||
|
58% { fill: #37ff37; opacity: 0.9; }
|
||||||
|
65% { fill: #37ff37; opacity: 0.4; }
|
||||||
|
79% { fill: #ff4444; opacity: 0.8; }
|
||||||
|
84% { fill: #ff4444; opacity: 0.3; }
|
||||||
|
92% { fill: #37ff37; opacity: 0.8; }
|
||||||
|
100% { fill: #37ff37; opacity: 0.4; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes drop {
|
||||||
|
0% {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
5% {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
65% {
|
||||||
|
transform: translateY(2px);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: translateY(2px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(2px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#downloadingIconLight {
|
||||||
|
animation: realistic-blink 3s infinite ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#downloadingIconArrow {
|
||||||
|
animation: drop 1.2s cubic-bezier(0.4, 0, 1, 1) infinite;
|
||||||
|
}
|
||||||
17
web/app/components/header/plugins-nav/downloading-icon.tsx
Normal file
17
web/app/components/header/plugins-nav/downloading-icon.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import s from './downloading-icon.module.css'
|
||||||
|
|
||||||
|
const DownloadingIcon = () => {
|
||||||
|
return (
|
||||||
|
<div className="inline-flex text-components-button-secondary-text">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="install-icon">
|
||||||
|
<g id="install-line">
|
||||||
|
<path d="M8 2V4H5L4.999 14H18.999L19 4H16V2H20C20.5523 2 21 2.44772 21 3V21C21 21.5523 20.5523 22 20 22H4C3.44772 22 3 21.5523 3 21V3C3 2.44772 3.44772 2 4 2H8ZM18.999 16H4.999L5 20H19L18.999 16Z" fill="currentColor"/>
|
||||||
|
<path id={s.downloadingIconLight} d="M17 19V17H15V19H17Z"/>
|
||||||
|
<path id={s.downloadingIconArrow} d="M13 2V7H16L12 11L8 7H11V2H13Z" fill="currentColor"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DownloadingIcon
|
||||||
@ -5,6 +5,9 @@ import Link from 'next/link'
|
|||||||
import classNames from '@/utils/classnames'
|
import classNames from '@/utils/classnames'
|
||||||
import { Group } from '@/app/components/base/icons/src/vender/other'
|
import { Group } from '@/app/components/base/icons/src/vender/other'
|
||||||
import { useSelectedLayoutSegment } from 'next/navigation'
|
import { useSelectedLayoutSegment } from 'next/navigation'
|
||||||
|
import DownloadingIcon from './downloading-icon'
|
||||||
|
import { usePluginTaskStatus } from '@/app/components/plugins/plugin-page/plugin-tasks/hooks'
|
||||||
|
import Indicator from '@/app/components/header/indicator'
|
||||||
|
|
||||||
type PluginsNavProps = {
|
type PluginsNavProps = {
|
||||||
className?: string
|
className?: string
|
||||||
@ -16,17 +19,43 @@ const PluginsNav = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const selectedSegment = useSelectedLayoutSegment()
|
const selectedSegment = useSelectedLayoutSegment()
|
||||||
const activated = selectedSegment === 'plugins'
|
const activated = selectedSegment === 'plugins'
|
||||||
|
const {
|
||||||
|
isInstalling,
|
||||||
|
isInstallingWithError,
|
||||||
|
isFailed,
|
||||||
|
} = usePluginTaskStatus()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href="/plugins" className={classNames(
|
<Link href="/plugins" className={classNames(
|
||||||
className, 'group',
|
className, 'group',
|
||||||
)}>
|
)}>
|
||||||
<div className={`flex flex-row h-8 p-1.5 gap-0.5 items-center justify-center
|
<div
|
||||||
rounded-xl system-sm-medium-uppercase ${activated
|
className={classNames(
|
||||||
? 'border border-components-main-nav-nav-button-border bg-components-main-nav-nav-button-bg-active shadow-md text-components-main-nav-nav-button-text'
|
'relative flex flex-row h-8 p-1.5 gap-0.5 border border-transparent items-center justify-center rounded-xl system-sm-medium-uppercase',
|
||||||
: 'text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary'}`}>
|
activated && 'border-components-main-nav-nav-button-border bg-components-main-nav-nav-button-bg-active shadow-md text-components-main-nav-nav-button-text',
|
||||||
|
!activated && 'text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||||
|
(isInstallingWithError || isFailed) && !activated && 'border-components-panel-border-subtle',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
(isFailed || isInstallingWithError) && !activated && (
|
||||||
|
<Indicator
|
||||||
|
color='red'
|
||||||
|
className='absolute top-[-1px] left-[-1px]'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
<div className='flex mr-0.5 w-5 h-5 justify-center items-center'>
|
<div className='flex mr-0.5 w-5 h-5 justify-center items-center'>
|
||||||
<Group className='w-4 h-4' />
|
{
|
||||||
|
(!(isInstalling || isInstallingWithError) || activated) && (
|
||||||
|
<Group className='w-4 h-4' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
(isInstalling || isInstallingWithError) && !activated && (
|
||||||
|
<DownloadingIcon />
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<span className='px-0.5'>{t('common.menus.plugins')}</span>
|
<span className='px-0.5'>{t('common.menus.plugins')}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import AgentStrategyList from './agent-strategy-list'
|
|||||||
import Drawer from '@/app/components/base/drawer'
|
import Drawer from '@/app/components/base/drawer'
|
||||||
import type { PluginDetail } from '@/app/components/plugins/types'
|
import type { PluginDetail } from '@/app/components/plugins/types'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
detail?: PluginDetail
|
detail?: PluginDetail
|
||||||
@ -27,6 +28,16 @@ const PluginDetailPanel: FC<Props> = ({
|
|||||||
onUpdate()
|
onUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [value, setValue] = React.useState({
|
||||||
|
provider_name: 'langgenius/google/google',
|
||||||
|
tool_name: 'google_search',
|
||||||
|
})
|
||||||
|
|
||||||
|
const testHandle = (item: any) => {
|
||||||
|
console.log(item)
|
||||||
|
setValue(item)
|
||||||
|
}
|
||||||
|
|
||||||
if (!detail)
|
if (!detail)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
@ -52,6 +63,17 @@ const PluginDetailPanel: FC<Props> = ({
|
|||||||
{!!detail.declaration.agent_strategy && <AgentStrategyList detail={detail} />}
|
{!!detail.declaration.agent_strategy && <AgentStrategyList detail={detail} />}
|
||||||
{!!detail.declaration.endpoint && <EndpointList detail={detail} />}
|
{!!detail.declaration.endpoint && <EndpointList detail={detail} />}
|
||||||
{!!detail.declaration.model && <ModelList detail={detail} />}
|
{!!detail.declaration.model && <ModelList detail={detail} />}
|
||||||
|
{false && (
|
||||||
|
<div className='px-4'>
|
||||||
|
<ToolSelector
|
||||||
|
scope={'all'}
|
||||||
|
value={value}
|
||||||
|
onSelect={item => testHandle(item)}
|
||||||
|
onDelete={() => testHandle(null)}
|
||||||
|
supportEnableSwitch
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -32,7 +32,7 @@ import {
|
|||||||
useInvalidateAllBuiltInTools,
|
useInvalidateAllBuiltInTools,
|
||||||
useUpdateProviderCredentials,
|
useUpdateProviderCredentials,
|
||||||
} from '@/service/use-tools'
|
} from '@/service/use-tools'
|
||||||
import { useInstallPackageFromMarketPlace } from '@/service/use-plugins'
|
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
||||||
import { usePluginInstalledCheck } from '@/app/components/plugins/plugin-detail-panel/tool-selector/hooks'
|
import { usePluginInstalledCheck } from '@/app/components/plugins/plugin-detail-panel/tool-selector/hooks'
|
||||||
import { CollectionType } from '@/app/components/tools/types'
|
import { CollectionType } from '@/app/components/tools/types'
|
||||||
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
|
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
|
||||||
@ -94,6 +94,7 @@ const ToolSelector: FC<Props> = ({
|
|||||||
const { data: customTools } = useAllCustomTools()
|
const { data: customTools } = useAllCustomTools()
|
||||||
const { data: workflowTools } = useAllWorkflowTools()
|
const { data: workflowTools } = useAllWorkflowTools()
|
||||||
const invalidateAllBuiltinTools = useInvalidateAllBuiltInTools()
|
const invalidateAllBuiltinTools = useInvalidateAllBuiltInTools()
|
||||||
|
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||||
|
|
||||||
// plugin info check
|
// plugin info check
|
||||||
const { inMarketPlace, manifest } = usePluginInstalledCheck(value?.provider_name)
|
const { inMarketPlace, manifest } = usePluginInstalledCheck(value?.provider_name)
|
||||||
@ -171,22 +172,15 @@ const ToolSelector: FC<Props> = ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// install from marketplace
|
// install from marketplace
|
||||||
const { mutateAsync: installPackageFromMarketPlace, isPending } = useInstallPackageFromMarketPlace()
|
|
||||||
const manifestIcon = useMemo(() => {
|
const manifestIcon = useMemo(() => {
|
||||||
if (!manifest)
|
if (!manifest)
|
||||||
return ''
|
return ''
|
||||||
return `${MARKETPLACE_API_PREFIX}/plugins/${(manifest as any).plugin_id}/icon`
|
return `${MARKETPLACE_API_PREFIX}/plugins/${(manifest as any).plugin_id}/icon`
|
||||||
}, [manifest])
|
}, [manifest])
|
||||||
const handleInstall = async () => {
|
const handleInstall = async () => {
|
||||||
if (!manifest)
|
invalidateAllBuiltinTools()
|
||||||
return
|
invalidateInstalledPluginList()
|
||||||
try {
|
|
||||||
await installPackageFromMarketPlace(manifest.latest_package_identifier)
|
|
||||||
invalidateAllBuiltinTools()
|
|
||||||
}
|
|
||||||
catch (e: any) {
|
|
||||||
Toast.notify({ type: 'error', message: `${e.message || e}` })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -223,7 +217,7 @@ const ToolSelector: FC<Props> = ({
|
|||||||
noAuth={currentProvider && !currentProvider.is_team_authorization}
|
noAuth={currentProvider && !currentProvider.is_team_authorization}
|
||||||
onAuth={() => setShowSettingAuth(true)}
|
onAuth={() => setShowSettingAuth(true)}
|
||||||
uninstalled={!currentProvider && inMarketPlace}
|
uninstalled={!currentProvider && inMarketPlace}
|
||||||
isInstalling={isPending}
|
installInfo={manifest?.latest_package_identifier}
|
||||||
onInstall={() => handleInstall()}
|
onInstall={() => handleInstall()}
|
||||||
isError={!currentProvider && !inMarketPlace}
|
isError={!currentProvider && !inMarketPlace}
|
||||||
errorTip={<div className='space-y-1 max-w-[240px] text-xs'>
|
errorTip={<div className='space-y-1 max-w-[240px] text-xs'>
|
||||||
|
|||||||
@ -5,8 +5,6 @@ import {
|
|||||||
RiDeleteBinLine,
|
RiDeleteBinLine,
|
||||||
RiEqualizer2Line,
|
RiEqualizer2Line,
|
||||||
RiErrorWarningFill,
|
RiErrorWarningFill,
|
||||||
RiInstallLine,
|
|
||||||
RiLoader2Line,
|
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import { Group } from '@/app/components/base/icons/src/vender/other'
|
import { Group } from '@/app/components/base/icons/src/vender/other'
|
||||||
import AppIcon from '@/app/components/base/app-icon'
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
@ -15,6 +13,7 @@ import Button from '@/app/components/base/button'
|
|||||||
import Indicator from '@/app/components/header/indicator'
|
import Indicator from '@/app/components/header/indicator'
|
||||||
import ActionButton from '@/app/components/base/action-button'
|
import ActionButton from '@/app/components/base/action-button'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -30,7 +29,7 @@ type Props = {
|
|||||||
isError?: boolean
|
isError?: boolean
|
||||||
errorTip?: any
|
errorTip?: any
|
||||||
uninstalled?: boolean
|
uninstalled?: boolean
|
||||||
isInstalling?: boolean
|
installInfo?: string
|
||||||
onInstall?: () => void
|
onInstall?: () => void
|
||||||
open: boolean
|
open: boolean
|
||||||
}
|
}
|
||||||
@ -47,7 +46,7 @@ const ToolItem = ({
|
|||||||
noAuth,
|
noAuth,
|
||||||
onAuth,
|
onAuth,
|
||||||
uninstalled,
|
uninstalled,
|
||||||
isInstalling,
|
installInfo,
|
||||||
onInstall,
|
onInstall,
|
||||||
isError,
|
isError,
|
||||||
errorTip,
|
errorTip,
|
||||||
@ -115,20 +114,15 @@ const ToolItem = ({
|
|||||||
<Indicator className='ml-2' color='orange' />
|
<Indicator className='ml-2' color='orange' />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{!isError && uninstalled && (
|
{!isError && uninstalled && installInfo && (
|
||||||
<Button
|
<InstallPluginButton
|
||||||
className={cn('flex items-center')}
|
onClick={e => e.stopPropagation()}
|
||||||
size='small'
|
size={'small'}
|
||||||
variant='secondary'
|
uniqueIdentifier={installInfo}
|
||||||
disabled={isInstalling}
|
onSuccess={() => {
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
onInstall?.()
|
onInstall?.()
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
{!isInstalling ? t('workflow.nodes.agent.pluginInstaller.install') : t('workflow.nodes.agent.pluginInstaller.installing')}
|
|
||||||
{!isInstalling ? <RiInstallLine className='size-4 ml-1' /> : <RiLoader2Line className='size-4 ml-1 animate-spin' />}
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
{isError && (
|
{isError && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import Action from './action'
|
|||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config'
|
import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config'
|
||||||
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
||||||
import { useInvalidateAllToolProviders } from '@/service/use-tools'
|
import { useInvalidateAllBuiltInTools, useInvalidateAllToolProviders } from '@/service/use-tools'
|
||||||
import { useCategories } from '../hooks'
|
import { useCategories } from '../hooks'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||||
@ -41,6 +41,7 @@ const PluginItem: FC<Props> = ({
|
|||||||
const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID)
|
const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID)
|
||||||
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||||
const invalidateAllToolProviders = useInvalidateAllToolProviders()
|
const invalidateAllToolProviders = useInvalidateAllToolProviders()
|
||||||
|
const invalidateAllBuiltinTools = useInvalidateAllBuiltInTools()
|
||||||
const { refreshModelProviders } = useProviderContext()
|
const { refreshModelProviders } = useProviderContext()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -62,8 +63,10 @@ const PluginItem: FC<Props> = ({
|
|||||||
invalidateInstalledPluginList()
|
invalidateInstalledPluginList()
|
||||||
if (PluginType.model.includes(category))
|
if (PluginType.model.includes(category))
|
||||||
refreshModelProviders()
|
refreshModelProviders()
|
||||||
if (PluginType.tool.includes(category))
|
if (PluginType.tool.includes(category)) {
|
||||||
invalidateAllToolProviders()
|
invalidateAllToolProviders()
|
||||||
|
invalidateAllBuiltinTools()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const getValueFromI18nObject = useRenderI18nObject()
|
const getValueFromI18nObject = useRenderI18nObject()
|
||||||
const title = getValueFromI18nObject(label)
|
const title = getValueFromI18nObject(label)
|
||||||
|
|||||||
@ -64,14 +64,16 @@ export const usePluginTaskStatus = () => {
|
|||||||
const timerRef = useRef<NodeJS.Timeout | null>(null)
|
const timerRef = useRef<NodeJS.Timeout | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isSuccess && opacity > 0) {
|
if (isSuccess) {
|
||||||
if (timerRef.current) {
|
if (timerRef.current) {
|
||||||
clearTimeout(timerRef.current)
|
clearTimeout(timerRef.current)
|
||||||
timerRef.current = null
|
timerRef.current = null
|
||||||
}
|
}
|
||||||
timerRef.current = setTimeout(() => {
|
if (opacity > 0) {
|
||||||
setOpacity(v => v - 0.1)
|
timerRef.current = setTimeout(() => {
|
||||||
}, 200)
|
setOpacity(v => v - 0.1)
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSuccess)
|
if (!isSuccess)
|
||||||
|
|||||||
@ -15,14 +15,16 @@ type Props = {
|
|||||||
list: Plugin[]
|
list: Plugin[]
|
||||||
searchText: string
|
searchText: string
|
||||||
tags: string[]
|
tags: string[]
|
||||||
|
disableMaxWidth?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const List = ({
|
const List = forwardRef<{ handleScroll: () => void }, Props>(({
|
||||||
wrapElemRef,
|
wrapElemRef,
|
||||||
searchText,
|
searchText,
|
||||||
tags,
|
tags,
|
||||||
list,
|
list,
|
||||||
}: Props, ref: any) => {
|
disableMaxWidth = false,
|
||||||
|
}, ref) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const hasFilter = !searchText
|
const hasFilter = !searchText
|
||||||
const hasRes = list.length > 0
|
const hasRes = list.length > 0
|
||||||
@ -95,7 +97,7 @@ const List = ({
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={cn('p-1', maxWidthClassName)} ref={nextToStickyELemRef}>
|
<div className={cn('p-1', !disableMaxWidth && maxWidthClassName)} ref={nextToStickyELemRef}>
|
||||||
{list.map((item, index) => (
|
{list.map((item, index) => (
|
||||||
<Item
|
<Item
|
||||||
key={index}
|
key={index}
|
||||||
@ -103,7 +105,7 @@ const List = ({
|
|||||||
onAction={() => { }}
|
onAction={() => { }}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<div className='mt-2 mb-3 flex items-center space-x-2'>
|
<div className='mt-2 mb-3 flex items-center justify-center space-x-2'>
|
||||||
<div className="w-[90px] h-[2px] bg-gradient-to-l from-[rgba(16,24,40,0.08)] to-[rgba(255,255,255,0.01)]"></div>
|
<div className="w-[90px] h-[2px] bg-gradient-to-l from-[rgba(16,24,40,0.08)] to-[rgba(255,255,255,0.01)]"></div>
|
||||||
<Link
|
<Link
|
||||||
href={urlWithSearchText}
|
href={urlWithSearchText}
|
||||||
@ -118,5 +120,8 @@ const List = ({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
export default forwardRef(List)
|
|
||||||
|
List.displayName = 'List'
|
||||||
|
|
||||||
|
export default List
|
||||||
|
|||||||
@ -1,24 +1,25 @@
|
|||||||
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
|
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
|
||||||
import type { ReactNode } from 'react'
|
import type { ReactNode } from 'react'
|
||||||
import { memo, useMemo, useState } from 'react'
|
import { memo, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import type { Strategy } from './agent-strategy'
|
import type { Strategy } from './agent-strategy'
|
||||||
import classNames from '@/utils/classnames'
|
import classNames from '@/utils/classnames'
|
||||||
import { RiArrowDownSLine, RiArrowRightUpLine, RiErrorWarningFill } from '@remixicon/react'
|
import { RiArrowDownSLine, RiErrorWarningFill } from '@remixicon/react'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { InstallPluginButton } from './install-plugin-button'
|
import { InstallPluginButton } from './install-plugin-button'
|
||||||
import ViewTypeSelect, { ViewType } from '../../../block-selector/view-type-select'
|
import ViewTypeSelect, { ViewType } from '../../../block-selector/view-type-select'
|
||||||
import SearchInput from '@/app/components/base/search-input'
|
import SearchInput from '@/app/components/base/search-input'
|
||||||
import { MARKETPLACE_URL_PREFIX } from '@/config'
|
|
||||||
import Tools from '../../../block-selector/tools'
|
import Tools from '../../../block-selector/tools'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useStrategyProviders } from '@/service/use-strategy'
|
import { useStrategyProviders } from '@/service/use-strategy'
|
||||||
import type { StrategyPluginDetail } from '@/app/components/plugins/types'
|
import { PluginType, type StrategyPluginDetail } from '@/app/components/plugins/types'
|
||||||
import type { ToolWithProvider } from '../../../types'
|
import type { ToolWithProvider } from '../../../types'
|
||||||
import { CollectionType } from '@/app/components/tools/types'
|
import { CollectionType } from '@/app/components/tools/types'
|
||||||
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
|
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
|
||||||
import { useStrategyInfo } from '../../agent/use-config'
|
import { useStrategyInfo } from '../../agent/use-config'
|
||||||
import { SwitchPluginVersion } from './switch-plugin-version'
|
import { SwitchPluginVersion } from './switch-plugin-version'
|
||||||
|
import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list'
|
||||||
|
import { useMarketplacePlugins } from '@/app/components/plugins/marketplace/hooks'
|
||||||
|
|
||||||
const NotFoundWarn = (props: {
|
const NotFoundWarn = (props: {
|
||||||
title: ReactNode,
|
title: ReactNode,
|
||||||
@ -119,6 +120,25 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) =>
|
|||||||
)?.icon as string | undefined
|
)?.icon as string | undefined
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const wrapElemRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
const {
|
||||||
|
queryPluginsWithDebounced: fetchPlugins,
|
||||||
|
plugins: notInstalledPlugins = [],
|
||||||
|
} = useMarketplacePlugins()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (query) {
|
||||||
|
fetchPlugins({
|
||||||
|
query,
|
||||||
|
category: PluginType.agent,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [query])
|
||||||
|
|
||||||
|
const pluginRef = useRef(null)
|
||||||
|
|
||||||
return <PortalToFollowElem open={open} onOpenChange={setOpen} placement='bottom'>
|
return <PortalToFollowElem open={open} onOpenChange={setOpen} placement='bottom'>
|
||||||
<PortalToFollowElemTrigger className='w-full'>
|
<PortalToFollowElemTrigger className='w-full'>
|
||||||
<div
|
<div
|
||||||
@ -172,8 +192,7 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) =>
|
|||||||
<SearchInput placeholder={t('workflow.nodes.agent.strategy.searchPlaceholder')} value={query} onChange={setQuery} className={'w-full'} />
|
<SearchInput placeholder={t('workflow.nodes.agent.strategy.searchPlaceholder')} value={query} onChange={setQuery} className={'w-full'} />
|
||||||
<ViewTypeSelect viewType={viewType} onChange={setViewType} />
|
<ViewTypeSelect viewType={viewType} onChange={setViewType} />
|
||||||
</header>
|
</header>
|
||||||
<main className="md:h-[300px] xl:h-[400px] 2xl:h-[564px] relative overflow-hidden">
|
<main className="md:max-h-[300px] xl:max-h-[400px] 2xl:max-h-[564px] relative overflow-hidden flex flex-col w-full" ref={wrapElemRef}>
|
||||||
{/* TODO: fix when in list view show workflow as group label */}
|
|
||||||
<Tools
|
<Tools
|
||||||
tools={filteredTools}
|
tools={filteredTools}
|
||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
@ -186,18 +205,16 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) =>
|
|||||||
plugin_unique_identifier: tool!.provider_id,
|
plugin_unique_identifier: tool!.provider_id,
|
||||||
})
|
})
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
}}
|
} }
|
||||||
hasSearchText={false}
|
className='max-w-none max-h-full h-full overflow-y-auto'
|
||||||
showWorkflowEmpty={false}
|
indexBarClassName='top-0 xl:top-36' showWorkflowEmpty={false} hasSearchText={false} />
|
||||||
className='max-w-none'
|
<PluginList
|
||||||
indexBarClassName='top-0 xl:top-36'
|
wrapElemRef={wrapElemRef}
|
||||||
|
list={notInstalledPlugins as any} ref={pluginRef}
|
||||||
|
searchText={query}
|
||||||
|
tags={[]}
|
||||||
|
disableMaxWidth
|
||||||
/>
|
/>
|
||||||
<div className='absolute bottom-0 px-4 py-2 flex items-center justify-center border-t border-divider-subtle text-text-accent-light-mode-only bg-components-panel-bg text-xs'>
|
|
||||||
Find more in
|
|
||||||
<Link href={MARKETPLACE_URL_PREFIX} className='flex ml-1'>
|
|
||||||
Marketplace <RiArrowRightUpLine className='size-3' />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</PortalToFollowElemContent>
|
</PortalToFollowElemContent>
|
||||||
|
|||||||
@ -38,6 +38,6 @@ export const InstallPluginButton = (props: InstallPluginButtonProps) => {
|
|||||||
className={classNames('flex items-center', className)}
|
className={classNames('flex items-center', className)}
|
||||||
>
|
>
|
||||||
{!isLoading ? t('workflow.nodes.agent.pluginInstaller.install') : t('workflow.nodes.agent.pluginInstaller.installing')}
|
{!isLoading ? t('workflow.nodes.agent.pluginInstaller.install') : t('workflow.nodes.agent.pluginInstaller.installing')}
|
||||||
{!isLoading ? <RiInstallLine className='size-4 ml-1' /> : <RiLoader2Line className='size-4 ml-1 animate-spin' />}
|
{!isLoading ? <RiInstallLine className='size-3.5 ml-1' /> : <RiLoader2Line className='size-3.5 ml-1 animate-spin' />}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user