Merge branch 'feat/plugins' into dev/plugin-deploy
commit
a071d2cd1b
@ -1,14 +0,0 @@
|
|||||||
'use server'
|
|
||||||
|
|
||||||
import { revalidatePath } from 'next/cache'
|
|
||||||
|
|
||||||
// Server Actions
|
|
||||||
export async function handleDelete() {
|
|
||||||
// revalidatePath only invalidates the cache when the included path is next visited.
|
|
||||||
revalidatePath('/')
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fetchPluginDetail(org: string, name: string) {
|
|
||||||
// Fetch plugin detail TODO
|
|
||||||
return { org, name }
|
|
||||||
}
|
|
||||||
@ -1,118 +0,0 @@
|
|||||||
'use client'
|
|
||||||
import Card from '@/app/components/plugins/card'
|
|
||||||
import { customTool, extensionDallE, modelGPT4, toolNotion } from '@/app/components/plugins/card/card-mock'
|
|
||||||
// import PluginItem from '@/app/components/plugins/plugin-item'
|
|
||||||
import CardMoreInfo from '@/app/components/plugins/card/card-more-info'
|
|
||||||
// import ProviderCard from '@/app/components/plugins/provider-card'
|
|
||||||
import Badge from '@/app/components/base/badge'
|
|
||||||
import InstallBundle from '@/app/components/plugins/install-plugin/install-bundle'
|
|
||||||
import { useBoolean } from 'ahooks'
|
|
||||||
import LoadingError from '@/app/components/plugins/install-plugin/base/loading-error'
|
|
||||||
|
|
||||||
const PluginList = () => {
|
|
||||||
const pluginList = [toolNotion, extensionDallE, modelGPT4, customTool]
|
|
||||||
const [isShow, {
|
|
||||||
setFalse: hide,
|
|
||||||
}] = useBoolean(true)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='pb-3 bg-white'>
|
|
||||||
<LoadingError />
|
|
||||||
{isShow && (
|
|
||||||
<InstallBundle
|
|
||||||
onClose={hide}
|
|
||||||
fromDSLPayload={[
|
|
||||||
// {
|
|
||||||
// type: 'marketplace',
|
|
||||||
// value: {
|
|
||||||
// plugin_unique_identifier: 'langgenius/google:0.0.2@dcb354c9d0fee60e6e9c9eb996e1e485bbef343ba8cd545c0cfb3ec80970f6f1',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
type: 'github',
|
|
||||||
value: {
|
|
||||||
repo: 'YIXIAO0/test',
|
|
||||||
version: '1.11.5',
|
|
||||||
package: 'test.difypkg',
|
|
||||||
github_plugin_unique_identifier: 'yixiao0/test:0.0.1@3592166c87afcf944b4f13f27467a5c8f9e00bd349cb42033a072734a37431b4',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// type: 'github',
|
|
||||||
// value: {
|
|
||||||
// package: 'dify-test.difypkg',
|
|
||||||
// repo: 'WTW0313/dify-test',
|
|
||||||
// release: '0.0.5-beta.2',
|
|
||||||
// github_plugin_unique_identifier: 'wtw0313/dify-test:0.0.1@1633daa043b47155d4228e2db7734245fd6d3e20ba812e5c02ce69fc1e3038f4',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// type: 'marketplace',
|
|
||||||
// value: {
|
|
||||||
// plugin_unique_identifier: 'langgenius/openai:0.0.2@7baee9635a07573ea192621ebfdacb39db466fa691e75255beaf48bf41d44375',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
]} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<div className='mx-3 '>
|
|
||||||
{/* <h2 className='my-3'>Dify Plugin list</h2> */}
|
|
||||||
{/* <div className='grid grid-cols-2 gap-3'>
|
|
||||||
{pluginList.map((plugin, index) => (
|
|
||||||
<PluginItem
|
|
||||||
key={index}
|
|
||||||
payload={plugin as any}
|
|
||||||
onDelete={handleDelete}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div> */}
|
|
||||||
|
|
||||||
<h2 className='my-3'>Install Plugin / Package under bundle</h2>
|
|
||||||
<div className='w-[512px] rounded-2xl bg-background-section-burn p-2'>
|
|
||||||
<Card
|
|
||||||
payload={toolNotion as any}
|
|
||||||
descriptionLineRows={1}
|
|
||||||
titleLeft={
|
|
||||||
<Badge className='ml-1' text={toolNotion.version} />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/* <h3 className='my-1'>Installed</h3>
|
|
||||||
<div className='w-[512px] rounded-2xl bg-background-section-burn p-2'>
|
|
||||||
<Card
|
|
||||||
payload={toolNotion as any}
|
|
||||||
descriptionLineRows={1}
|
|
||||||
installed
|
|
||||||
/>
|
|
||||||
</div> */}
|
|
||||||
|
|
||||||
{/* <h3 className='my-1'>Install model provide</h3>
|
|
||||||
<div className='grid grid-cols-2 gap-3'>
|
|
||||||
{pluginList.map((plugin, index) => (
|
|
||||||
<ProviderCard key={index} payload={plugin as any} />
|
|
||||||
))}
|
|
||||||
</div> */}
|
|
||||||
|
|
||||||
<div className='my-3 h-[px] bg-gray-50'></div>
|
|
||||||
<h2 className='my-3'>Marketplace Plugin list</h2>
|
|
||||||
<div className='grid grid-cols-4 gap-3'>
|
|
||||||
{pluginList.map((plugin, index) => (
|
|
||||||
<Card
|
|
||||||
key={index}
|
|
||||||
payload={plugin as any}
|
|
||||||
footer={
|
|
||||||
<CardMoreInfo downloadCount={index % 2 === 0 ? 1234 : 6} tags={index % 2 === 0 ? ['Search', 'Tag that has very very long name', 'Productivity', 'Tag2'] : []} />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// export const metadata = {
|
|
||||||
// title: 'Plugins - Card',
|
|
||||||
// }
|
|
||||||
|
|
||||||
export default PluginList
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
'use client'
|
|
||||||
import React from 'react'
|
|
||||||
import ToolPicker from '@/app/components/workflow/block-selector/tool-picker'
|
|
||||||
|
|
||||||
const ToolsPicker = () => {
|
|
||||||
const [show, setShow] = React.useState(true)
|
|
||||||
return (
|
|
||||||
<div className=' mt-10 ml-10'>
|
|
||||||
<ToolPicker
|
|
||||||
trigger={<div className='inline-block w-[70px]'>Click me</div>}
|
|
||||||
isShow={show}
|
|
||||||
onShowChange={setShow}
|
|
||||||
disabled={false}
|
|
||||||
supportAddCustomTool={true}
|
|
||||||
onSelect={() => { }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ToolsPicker
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
'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 (
|
|
||||||
<div>
|
|
||||||
<div>更新组件</div>
|
|
||||||
<div className='flex space-x-1'>
|
|
||||||
<div className='underline cursor-pointer' onClick={handleUpdateFromMarketPlace}>从 Marketplace</div>
|
|
||||||
<div className='underline cursor-pointer' onClick={handleUpdateFromGithub}>从 GitHub</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default React.memo(UpdatePlugin)
|
|
||||||
@ -0,0 +1,180 @@
|
|||||||
|
import type { FC } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import type {
|
||||||
|
CustomConfigurationModelFixedFields,
|
||||||
|
ModelItem,
|
||||||
|
ModelProvider,
|
||||||
|
} from '../declarations'
|
||||||
|
import {
|
||||||
|
ConfigurationMethodEnum,
|
||||||
|
CustomConfigurationStatusEnum,
|
||||||
|
} from '../declarations'
|
||||||
|
import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from '../provider-added-card'
|
||||||
|
import { ModelStatusEnum } from '../declarations'
|
||||||
|
import {
|
||||||
|
useUpdateModelList,
|
||||||
|
useUpdateModelProviders,
|
||||||
|
} from '../hooks'
|
||||||
|
import ModelIcon from '../model-icon'
|
||||||
|
import ModelName from '../model-name'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
|
import { useModalContextSelector } from '@/context/modal-context'
|
||||||
|
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||||
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
import { RiEqualizer2Line, RiErrorWarningFill } from '@remixicon/react'
|
||||||
|
|
||||||
|
export type AgentModelTriggerProps = {
|
||||||
|
open?: boolean
|
||||||
|
disabled?: boolean
|
||||||
|
currentProvider?: ModelProvider
|
||||||
|
currentModel?: ModelItem
|
||||||
|
providerName?: string
|
||||||
|
modelId?: string
|
||||||
|
hasDeprecated?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
||||||
|
disabled,
|
||||||
|
currentProvider,
|
||||||
|
currentModel,
|
||||||
|
providerName,
|
||||||
|
modelId,
|
||||||
|
hasDeprecated,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { modelProviders } = useProviderContext()
|
||||||
|
const setShowModelModal = useModalContextSelector(state => state.setShowModelModal)
|
||||||
|
const updateModelProviders = useUpdateModelProviders()
|
||||||
|
const updateModelList = useUpdateModelList()
|
||||||
|
const { eventEmitter } = useEventEmitterContextContext()
|
||||||
|
const modelProvider = modelProviders.find(item => item.provider === providerName)
|
||||||
|
const needsConfiguration = modelProvider?.custom_configuration.status === CustomConfigurationStatusEnum.noConfigure && !(
|
||||||
|
modelProvider.system_configuration.enabled === true
|
||||||
|
&& modelProvider.system_configuration.quota_configurations.find(
|
||||||
|
item => item.quota_type === modelProvider.system_configuration.current_quota_type,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'relative group flex items-center p-1 gap-[2px] flex-grow rounded-lg bg-components-input-bg-normal cursor-pointer hover:bg-state-base-hover-alt',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{modelId ? (
|
||||||
|
<>
|
||||||
|
{currentProvider && (
|
||||||
|
<ModelIcon
|
||||||
|
className="m-0.5"
|
||||||
|
provider={currentProvider}
|
||||||
|
modelName={currentModel?.model}
|
||||||
|
isDeprecated={hasDeprecated}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!currentProvider && (
|
||||||
|
<ModelIcon
|
||||||
|
className="m-0.5"
|
||||||
|
provider={modelProvider}
|
||||||
|
modelName={modelId}
|
||||||
|
isDeprecated={hasDeprecated}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{currentModel && (
|
||||||
|
<ModelName
|
||||||
|
className="flex px-1 py-[3px] items-center gap-1 grow"
|
||||||
|
modelItem={currentModel}
|
||||||
|
showMode
|
||||||
|
showFeatures
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!currentModel && (
|
||||||
|
<div className="flex py-[3px] px-1 items-center gap-1 grow opacity-50 truncate">
|
||||||
|
<div className="text-components-input-text-filled text-ellipsis overflow-hidden system-sm-regular">
|
||||||
|
{modelId}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{needsConfiguration && (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
className="z-[100]"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
handleOpenModal(modelProvider, ConfigurationMethodEnum.predefinedModel, undefined)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex px-[3px] justify-center items-center gap-1">
|
||||||
|
{t('workflow.nodes.agent.notAuthorized')}
|
||||||
|
</div>
|
||||||
|
<div className="flex w-[14px] h-[14px] justify-center items-center">
|
||||||
|
<div className="w-2 h-2 shrink-0 rounded-[3px] border border-components-badge-status-light-warning-border-inner
|
||||||
|
bg-components-badge-status-light-warning-bg shadow-components-badge-status-light-warning-halo" />
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{!needsConfiguration && disabled && (
|
||||||
|
<Tooltip
|
||||||
|
popupContent={t('workflow.nodes.agent.modelSelectorTooltips.deprecated')}
|
||||||
|
asChild={false}
|
||||||
|
>
|
||||||
|
<RiErrorWarningFill className='w-4 h-4 text-text-destructive' />
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="flex p-1 pl-2 items-center gap-1 grow">
|
||||||
|
<span className="overflow-hidden text-ellipsis whitespace-nowrap system-sm-regular text-components-input-text-placeholder">
|
||||||
|
{t('workflow.nodes.agent.configureModel')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex pr-1 items-center">
|
||||||
|
<RiEqualizer2Line className="w-4 h-4 text-text-tertiary group-hover:text-text-secondary" />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{currentProvider && currentModel && currentModel.status === ModelStatusEnum.active && (
|
||||||
|
<div className="flex pr-1 items-center">
|
||||||
|
<RiEqualizer2Line className="w-4 h-4 text-text-tertiary group-hover:text-text-secondary" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AgentModelTrigger
|
||||||
@ -0,0 +1,120 @@
|
|||||||
|
'use client'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import {
|
||||||
|
RiDeleteBinLine,
|
||||||
|
RiEqualizer2Line,
|
||||||
|
RiErrorWarningFill,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
|
import Switch from '@/app/components/base/switch'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import Indicator from '@/app/components/header/indicator'
|
||||||
|
import ActionButton from '@/app/components/base/action-button'
|
||||||
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
icon?: any
|
||||||
|
providerName?: string
|
||||||
|
toolName?: string
|
||||||
|
showSwitch?: boolean
|
||||||
|
switchValue?: boolean
|
||||||
|
onSwitchChange?: (value: boolean) => void
|
||||||
|
onDelete?: () => void
|
||||||
|
noAuth?: boolean
|
||||||
|
onAuth?: () => void
|
||||||
|
isError?: boolean
|
||||||
|
errorTip?: any
|
||||||
|
uninstalled?: boolean
|
||||||
|
isInstalling?: boolean
|
||||||
|
onInstall?: () => void
|
||||||
|
open: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToolItem = ({
|
||||||
|
open,
|
||||||
|
icon,
|
||||||
|
providerName,
|
||||||
|
toolName,
|
||||||
|
showSwitch,
|
||||||
|
switchValue,
|
||||||
|
onSwitchChange,
|
||||||
|
onDelete,
|
||||||
|
noAuth,
|
||||||
|
onAuth,
|
||||||
|
uninstalled,
|
||||||
|
isInstalling,
|
||||||
|
onInstall,
|
||||||
|
isError,
|
||||||
|
errorTip,
|
||||||
|
}: Props) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const providerNameText = providerName?.split('/').pop()
|
||||||
|
const isTransparent = uninstalled || isError
|
||||||
|
const [isDeleting, setIsDeleting] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn(
|
||||||
|
'group p-1.5 pr-2 flex items-center gap-1 bg-components-panel-on-panel-item-bg border-[0.5px] border-components-panel-border-subtle rounded-lg shadow-xs cursor-default hover:bg-components-panel-on-panel-item-bg-hover hover:shadow-sm',
|
||||||
|
open && 'bg-components-panel-on-panel-item-bg-hover shadow-sm',
|
||||||
|
isDeleting && 'hover:bg-state-destructive-hover border-state-destructive-border shadow-xs',
|
||||||
|
)}>
|
||||||
|
<div className={cn('shrink-0', isTransparent && 'opacity-50')}>
|
||||||
|
{typeof icon === 'string' && <div className='w-7 h-7 bg-cover bg-center border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge rounded-lg' style={{ backgroundImage: `url(${icon})` }} />}
|
||||||
|
{typeof icon !== 'string' && <AppIcon className='w-7 h-7 border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge rounded-lg' size='xs' icon={icon?.content} background={icon?.background} />}
|
||||||
|
</div>
|
||||||
|
<div className={cn('pl-0.5 grow truncate', isTransparent && 'opacity-50')}>
|
||||||
|
<div className='text-text-tertiary system-2xs-medium-uppercase'>{providerNameText}</div>
|
||||||
|
<div className='text-text-secondary system-xs-medium'>{toolName}</div>
|
||||||
|
</div>
|
||||||
|
<div className='hidden group-hover:flex items-center gap-1'>
|
||||||
|
{!noAuth && !isError && !uninstalled && (
|
||||||
|
<ActionButton>
|
||||||
|
<RiEqualizer2Line className='w-4 h-4' />
|
||||||
|
</ActionButton>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className='p-1 rounded-md text-text-tertiary cursor-pointer hover:text-text-destructive'
|
||||||
|
onClick={onDelete}
|
||||||
|
onMouseOver={() => setIsDeleting(true)}
|
||||||
|
onMouseLeave={() => setIsDeleting(false)}
|
||||||
|
>
|
||||||
|
<RiDeleteBinLine className='w-4 h-4' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{!isError && !uninstalled && !noAuth && showSwitch && (
|
||||||
|
<Switch
|
||||||
|
className='mr-1'
|
||||||
|
size='md'
|
||||||
|
defaultValue={switchValue}
|
||||||
|
onChange={onSwitchChange} />
|
||||||
|
)}
|
||||||
|
{!isError && !uninstalled && noAuth && (
|
||||||
|
<Button variant='secondary' size='small' onClick={onAuth}>
|
||||||
|
{t('tools.notAuthorized')}
|
||||||
|
<Indicator className='ml-2' color='orange' />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{!isError && uninstalled && (
|
||||||
|
<InstallPluginButton size={'small'} loading={isInstalling} onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
onInstall?.()
|
||||||
|
}} />
|
||||||
|
)}
|
||||||
|
{isError && (
|
||||||
|
<Tooltip
|
||||||
|
popupContent={errorTip}
|
||||||
|
needsDelay
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<RiErrorWarningFill className='w-4 h-4 text-text-destructive' />
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ToolItem
|
||||||
Loading…
Reference in New Issue