diff --git a/web/app/components/plugins/plugin-detail-panel/index.tsx b/web/app/components/plugins/plugin-detail-panel/index.tsx index b349e9dc5c..ae82b1f55a 100644 --- a/web/app/components/plugins/plugin-detail-panel/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/index.tsx @@ -12,7 +12,7 @@ import EndpointList from './endpoint-list' import ModelList from './model-list' import { SubscriptionList } from './subscription-list' import { usePluginStore } from './subscription-list/store' -import { TriggerEventsList } from './trigger-events-list' +import { TriggerEventsList } from './trigger/event-list' type Props = { detail?: PluginDetail diff --git a/web/app/components/plugins/plugin-detail-panel/trigger-events-list.tsx b/web/app/components/plugins/plugin-detail-panel/trigger-events-list.tsx deleted file mode 100644 index 75de19d968..0000000000 --- a/web/app/components/plugins/plugin-detail-panel/trigger-events-list.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import React, { useMemo } from 'react' -import { useTranslation } from 'react-i18next' -import { useContextSelector } from 'use-context-selector' -import I18n from '@/context/i18n' -import { getLanguage } from '@/i18n-config/language' -import ToolItem from '@/app/components/tools/provider/tool-item' -import { useTriggerProviderInfo } from '@/service/use-triggers' -import type { Tool, ToolParameter } from '@/app/components/tools/types' -import { CollectionType } from '@/app/components/tools/types' -import type { ToolWithProvider } from '@/app/components/workflow/types' -import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations' -import type { Trigger } from '@/app/components/plugins/types' -import { usePluginStore } from './subscription-list/store' - -type TriggerOption = { - value: string - label: TypeWithI18N - icon?: string | null -} - -const pickLocaleText = (value?: TypeWithI18N | string, fallback = '', language = 'en_US'): string => { - if (!value) - return fallback - if (typeof value === 'string') - return value - const typedValue = value as Record - return typedValue[language] ?? typedValue.en_US ?? Object.values(typedValue)[0] ?? fallback -} - -const getTriggerDescription = (description: TypeWithI18N | { human?: TypeWithI18N | string } | string | undefined, language = 'en_US'): string => { - if (!description) - return '' - - if (typeof description === 'string') - return description - - if (typeof description === 'object' && 'human' in description) { - const human = (description as { human?: TypeWithI18N | string }).human - return pickLocaleText(human, '', language) - } - - return pickLocaleText(description as TypeWithI18N, '', language) -} - -const toToolParameter = (parameter: any): ToolParameter => { - const paramLabel = parameter.label as TypeWithI18N - const paramDescription = parameter.description as TypeWithI18N | undefined - - return { - name: parameter.name, - label: paramLabel, - human_description: paramDescription || paramLabel, - type: parameter.type, - form: 'setting', - llm_description: typeof paramDescription === 'object' ? (paramDescription?.en_US || '') : (paramDescription || ''), - required: parameter.required ?? false, - multiple: parameter.multiple ?? false, - default: parameter.default ?? '', - options: parameter.options?.map((option: TriggerOption) => ({ - label: option.label, - value: option.value, - })) || [], - } -} - -const toTool = (trigger: Trigger, fallbackAuthor: string): Tool => { - const name = trigger.identity?.name || '' - const label = trigger.identity?.label || { en_US: name } - const description = trigger.description?.human || trigger.description || { en_US: '' } - return { - name, - author: trigger.identity?.author || fallbackAuthor, - label: label as TypeWithI18N, - description: description as TypeWithI18N, - parameters: (trigger.parameters || []).map((param: any) => toToolParameter(param)), - labels: [], - output_schema: trigger.output_schema || {}, - } -} - -export const TriggerEventsList = () => { - const { t } = useTranslation() - const locale = useContextSelector(I18n, state => state.locale) - const language = getLanguage(locale) - const detail = usePluginStore(state => state.detail) - const events = detail?.declaration.trigger?.events || [] - - const { data: providerInfo } = useTriggerProviderInfo(detail?.provider || '') - - const collection = useMemo(() => { - if (!detail || !providerInfo) - return undefined - - const tools = (providerInfo.events || []).map((trigger: any) => toTool(trigger, providerInfo.author)) - - const metaVersion = detail.declaration.meta?.version || detail.declaration.version || '1.0' - - return { - id: providerInfo.plugin_id || providerInfo.name, - name: providerInfo.name, - author: providerInfo.author, - description: providerInfo.description, - icon: providerInfo.icon || providerInfo.icon_dark || '', - label: providerInfo.label, - type: CollectionType.builtIn, - team_credentials: {}, - is_team_authorization: false, - allow_delete: false, - labels: providerInfo.tags || [], - plugin_id: providerInfo.plugin_id || detail.plugin_id, - tools, - meta: { version: metaVersion }, - } - }, [detail, providerInfo]) - - if (!events.length) - return null - - return ( -
-
-
- {t('pluginTrigger.events.actionNum', { num: events.length, event: t(`pluginTrigger.events.${events.length > 1 ? 'events' : 'event'}`) })} -
-
-
- {collection - ? events.map((triggerEvent: Trigger) => { - const triggerName = triggerEvent.identity?.name || '' - const tool = collection.tools.find(item => item.name === triggerName) - || toTool(triggerEvent, collection.author) - - return ( - - ) - }) - : events.map((triggerEvent: Trigger) => ( -
-
- {pickLocaleText(triggerEvent.identity?.label as TypeWithI18N, triggerEvent.identity?.name || '', language)} -
-
- {getTriggerDescription(triggerEvent.description, language)} -
-
- ))} -
-
- ) -} diff --git a/web/app/components/plugins/plugin-detail-panel/trigger/event-detail-drawer.tsx b/web/app/components/plugins/plugin-detail-panel/trigger/event-detail-drawer.tsx new file mode 100644 index 0000000000..c5abd616e0 --- /dev/null +++ b/web/app/components/plugins/plugin-detail-panel/trigger/event-detail-drawer.tsx @@ -0,0 +1,127 @@ +'use client' +import ActionButton from '@/app/components/base/action-button' +import Drawer from '@/app/components/base/drawer' +import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' +import Icon from '@/app/components/plugins/card/base/card-icon' +import Description from '@/app/components/plugins/card/base/description' +import OrgInfo from '@/app/components/plugins/card/base/org-info' +import { triggerEventParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema' +import type { TriggerProviderApiEntity } from '@/app/components/workflow/block-selector/types' +import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' +// import Split from '@/app/components/workflow/nodes/_base/components/split' +import cn from '@/utils/classnames' +import { + RiArrowLeftLine, + RiCloseLine, +} from '@remixicon/react' +import type { TFunction } from 'i18next' +import type { FC } from 'react' +import { useTranslation } from 'react-i18next' +import type { TriggerEvent } from '../../types' + +type EventDetailDrawerProps = { + eventInfo: TriggerEvent + providerInfo: TriggerProviderApiEntity + onClose: () => void +} + +const getType = (type: string, t: TFunction) => { + if (type === 'number-input') + return t('tools.setBuiltInTools.number') + if (type === 'text-input') + return t('tools.setBuiltInTools.string') + if (type === 'checkbox') + return 'boolean' + if (type === 'file') + return t('tools.setBuiltInTools.file') + return type +} + +export const EventDetailDrawer: FC = (props) => { + const { eventInfo, providerInfo, onClose } = props + const language = useLanguage() + const { t } = useTranslation() + const parametersSchemas = triggerEventParametersToFormSchemas(eventInfo.parameters) + const outputVars = Object.entries(eventInfo.output_schema?.properties || {}).map(([name, schema]: [string, any]) => ({ + name, + type: schema.type || 'string', + description: schema.description || '', + })) + + return ( + +
+
+ + + +
+
+ + BACK +
+
+ + +
+
{eventInfo?.identity?.label[language]}
+ +
+
+
{t('tools.setBuiltInTools.parameters')}
+
+ {parametersSchemas.length > 0 && ( +
+ {parametersSchemas.map((item, index) => ( +
+
+
{item.label[language]}
+
+ {getType(item.type, t)} +
+ {item.required && ( +
{t('tools.setBuiltInTools.required')}
+ )} +
+ {item.description && ( +
+ {item.description?.[language]} +
+ )} +
+ ))} +
+ )} +
+ {/* */} +
{t('pluginTrigger.events.output')}
+ + {outputVars.map(varItem => ( + + ))} + +
+
+ ) +} diff --git a/web/app/components/plugins/plugin-detail-panel/trigger/event-list.tsx b/web/app/components/plugins/plugin-detail-panel/trigger/event-list.tsx new file mode 100644 index 0000000000..0756035a49 --- /dev/null +++ b/web/app/components/plugins/plugin-detail-panel/trigger/event-list.tsx @@ -0,0 +1,69 @@ +import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' +import type { TriggerEvent } from '@/app/components/plugins/types' +import type { TriggerProviderApiEntity } from '@/app/components/workflow/block-selector/types' +import { useTriggerProviderInfo } from '@/service/use-triggers' +import cn from '@/utils/classnames' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import { usePluginStore } from '../subscription-list/store' +import { EventDetailDrawer } from './event-detail-drawer' + +type TriggerEventCardProps = { + eventInfo: TriggerEvent + providerInfo: TriggerProviderApiEntity +} + +const TriggerEventCard = ({ eventInfo, providerInfo }: TriggerEventCardProps) => { + const { identity, description = {} } = eventInfo + const language = useLanguage() + const [showDetail, setShowDetail] = useState(false) + return ( + <> +
setShowDetail(true)} + > +
{identity.label[language]}
+
{description[language]}
+
+ {showDetail && ( + setShowDetail(false)} + /> + )} + + ) +} + +export const TriggerEventsList = () => { + const { t } = useTranslation() + const detail = usePluginStore(state => state.detail) + + const { data: providerInfo } = useTriggerProviderInfo(detail?.provider || '') + const triggerEvents = providerInfo?.events || [] + + if (!providerInfo || !triggerEvents.length) + return null + + return ( +
+
+
+ {t('pluginTrigger.events.actionNum', { num: triggerEvents.length, event: t(`pluginTrigger.events.${triggerEvents.length > 1 ? 'events' : 'event'}`) })} +
+
+
+ { + triggerEvents.map((triggerEvent: TriggerEvent) => ( + )) + } +
+
+ ) +} diff --git a/web/app/components/plugins/types.ts b/web/app/components/plugins/types.ts index 3639157c91..06189af139 100644 --- a/web/app/components/plugins/types.ts +++ b/web/app/components/plugins/types.ts @@ -95,7 +95,7 @@ export type PluginTriggerSubscriptionConstructor = { } export type PluginTriggerDefinition = { - events: Trigger[] + events: TriggerEvent[] identity: Identity subscription_constructor: PluginTriggerSubscriptionConstructor subscription_schema: ParametersSchema[] @@ -158,29 +158,31 @@ export type PropertiesSchema = { placeholder: any } -export type Trigger = { +export type TriggerEventParameter = { + name: string + label: Record + type: string + auto_generate: any + template: any + scope: any + required: boolean + multiple: boolean + default: any + min: any + max: any + precision: any + options?: Array<{ + value: string + label: Record + icon?: string + }> + description?: Record +} + +export type TriggerEvent = { identity: Identity description: Record - parameters: { - name: string - label: Record - type: string - auto_generate: any - template: any - scope: any - required: boolean - multiple: boolean - default: any - min: any - max: any - precision: any - options?: Array<{ - value: string - label: Record - icon?: string - }> - description?: Record - }[] + parameters: TriggerEventParameter[] output_schema: Record } diff --git a/web/app/components/tools/utils/to-form-schema.ts b/web/app/components/tools/utils/to-form-schema.ts index 8e85a5f9b0..32a2151f0f 100644 --- a/web/app/components/tools/utils/to-form-schema.ts +++ b/web/app/components/tools/utils/to-form-schema.ts @@ -1,6 +1,7 @@ -import type { ToolCredential, ToolParameter } from '../types' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' +import type { TriggerEventParameter } from '../../plugins/types' +import type { ToolCredential, ToolParameter } from '../types' export const toType = (type: string) => { switch (type) { @@ -14,6 +15,21 @@ export const toType = (type: string) => { return type } } + +export const triggerEventParametersToFormSchemas = (parameters: TriggerEventParameter[]) => { + if (!parameters?.length) + return [] + + return parameters.map((parameter) => { + return { + ...parameter, + type: toType(parameter.type), + _type: parameter.type, + tooltip: parameter.description, + } + }) +} + export const toolParametersToFormSchemas = (parameters: ToolParameter[]) => { if (!parameters) return [] diff --git a/web/app/components/workflow/block-selector/types.ts b/web/app/components/workflow/block-selector/types.ts index c02d12913e..dcc40308a5 100644 --- a/web/app/components/workflow/block-selector/types.ts +++ b/web/app/components/workflow/block-selector/types.ts @@ -1,5 +1,5 @@ import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations' -import type { ParametersSchema, PluginMeta, PluginTriggerSubscriptionConstructor, SupportedCreationMethods } from '../../plugins/types' +import type { ParametersSchema, PluginMeta, PluginTriggerSubscriptionConstructor, SupportedCreationMethods, TriggerEvent } from '../../plugins/types' import type { Collection, Event } from '../../tools/types' export enum TabsEnum { @@ -200,10 +200,10 @@ export type TriggerProviderApiEntity = { plugin_id?: string plugin_unique_identifier: string supported_creation_methods: SupportedCreationMethods[] - credentials_schema: TriggerCredentialField[] - subscription_constructor: PluginTriggerSubscriptionConstructor + credentials_schema?: TriggerCredentialField[] + subscription_constructor?: PluginTriggerSubscriptionConstructor | null subscription_schema: ParametersSchema[] - events: TriggerApiEntity[] + events: TriggerEvent[] } // Frontend types - compatible with ToolWithProvider diff --git a/web/i18n/en-US/plugin-trigger.ts b/web/i18n/en-US/plugin-trigger.ts index 63f34af945..0a19844125 100644 --- a/web/i18n/en-US/plugin-trigger.ts +++ b/web/i18n/en-US/plugin-trigger.ts @@ -165,6 +165,7 @@ const translation = { item: { parameters: '{{count}} parameters', }, + output: 'Output', }, } diff --git a/web/i18n/zh-Hans/plugin-trigger.ts b/web/i18n/zh-Hans/plugin-trigger.ts index bef0377457..c793f0572d 100644 --- a/web/i18n/zh-Hans/plugin-trigger.ts +++ b/web/i18n/zh-Hans/plugin-trigger.ts @@ -165,6 +165,7 @@ const translation = { item: { parameters: '{{count}}个参数', }, + output: '输出', }, }