diff --git a/web/app/components/snippets/components/snippet-main.tsx b/web/app/components/snippets/components/snippet-main.tsx index 770404acca..795d644752 100644 --- a/web/app/components/snippets/components/snippet-main.tsx +++ b/web/app/components/snippets/components/snippet-main.tsx @@ -23,6 +23,8 @@ import { useStore as useAppStore } from '@/app/components/app/store' import { toast } from '@/app/components/base/ui/toast' import Evaluation from '@/app/components/evaluation' import { WorkflowWithInnerContext } from '@/app/components/workflow' +import { useAvailableNodesMetaData } from '@/app/components/workflow-app/hooks' +import { BlockEnum } from '@/app/components/workflow/types' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import { useConfigsMap } from '../hooks/use-configs-map' import { useNodesSyncDraft } from '../hooks/use-nodes-sync-draft' @@ -66,6 +68,25 @@ const SnippetMain = ({ } = useNodesSyncDraft(snippetId) const { handleRefreshWorkflowDraft } = useSnippetRefreshDraft(snippetId) const configsMap = useConfigsMap(snippetId) + const workflowAvailableNodesMetaData = useAvailableNodesMetaData() + const availableNodesMetaData = useMemo(() => { + const nodes = workflowAvailableNodesMetaData.nodes.filter(node => + node.metaData.type !== BlockEnum.HumanInput && node.metaData.type !== BlockEnum.End) + + if (!workflowAvailableNodesMetaData.nodesMap) + return { nodes } + + const { + [BlockEnum.HumanInput]: _humanInput, + [BlockEnum.End]: _end, + ...nodesMap + } = workflowAvailableNodesMetaData.nodesMap + + return { + nodes, + nodesMap, + } + }, [workflowAvailableNodesMetaData]) const setAppSidebarExpand = useAppStore(state => state.setAppSidebarExpand) const { editingField, @@ -150,9 +171,10 @@ const SnippetMain = ({ doSyncWorkflowDraft, syncWorkflowDraftWhenPageClose, handleRefreshWorkflowDraft, + availableNodesMetaData, configsMap, } - }, [configsMap, doSyncWorkflowDraft, handleRefreshWorkflowDraft, syncWorkflowDraftWhenPageClose]) + }, [availableNodesMetaData, configsMap, doSyncWorkflowDraft, handleRefreshWorkflowDraft, syncWorkflowDraftWhenPageClose]) return (
@@ -190,7 +212,7 @@ const SnippetMain = ({ nodes={nodes} edges={edges} viewport={viewport ?? graph.viewport} - hooksStore={hooksStore} + hooksStore={hooksStore as any} > { const { t } = useTranslation() const shouldShowStartTab = !noStart - const shouldDisableStartTab = !forceEnableStartTab && hasUserInputNode + const shouldDisableStartTab = disableStartTab || (!forceEnableStartTab && hasUserInputNode) + const startDisabledTip = disableStartTab + ? t('tabs.startNotSupportedTip', { ns: 'workflow' }) + : t('tabs.startDisabledTip', { ns: 'workflow' }) const tabs = useMemo(() => { const tabConfigs = [{ key: TabsEnum.Blocks, @@ -71,6 +76,7 @@ export const useTabs = ({ name: t('tabs.start', { ns: 'workflow' }), show: shouldShowStartTab, disabled: shouldDisableStartTab, + disabledTip: shouldDisableStartTab ? startDisabledTip : undefined, }, { key: TabsEnum.Snippets, name: t('tabs.snippets', { ns: 'workflow' }), @@ -78,7 +84,7 @@ export const useTabs = ({ }] return tabConfigs.filter(tab => tab.show) - }, [t, noBlocks, noSources, noTools, shouldShowStartTab, shouldDisableStartTab]) + }, [t, noBlocks, noSources, noTools, shouldShowStartTab, shouldDisableStartTab, startDisabledTip]) const getValidTabKey = useCallback((targetKey?: TabsEnum) => { if (!targetKey) diff --git a/web/app/components/workflow/block-selector/main.tsx b/web/app/components/workflow/block-selector/main.tsx index 9511d02e16..cf647d9d67 100644 --- a/web/app/components/workflow/block-selector/main.tsx +++ b/web/app/components/workflow/block-selector/main.tsx @@ -30,7 +30,9 @@ import { PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' import SearchBox from '@/app/components/plugins/marketplace/search-box' +import { useHooksStore } from '@/app/components/workflow/hooks-store' import useNodes from '@/app/components/workflow/store/workflow/use-nodes' +import { FlowType } from '@/types/common' import { BlockEnum, isTriggerNode } from '../types' import { useTabs } from './hooks' import Snippets from './snippets' @@ -89,6 +91,7 @@ const NodeSelector: FC = ({ }) => { const { t } = useTranslation() const nodes = useNodes() + const flowType = useHooksStore(s => s.configsMap?.flowType) const [searchText, setSearchText] = useState('') const [snippetsLoading, setSnippetsLoading] = useState(() => Boolean(openFromProps) && defaultActiveTab === TabsEnum.Snippets) const [tags, setTags] = useState([]) @@ -122,6 +125,7 @@ const NodeSelector: FC = ({ // Default rule: user input option is only available when no Start node nor Trigger node exists on canvas. const defaultAllowUserInputSelection = !hasUserInputNode && !hasTriggerNode const canSelectUserInput = allowUserInputSelection ?? defaultAllowUserInputSelection + const disableStartTab = flowType === FlowType.snippet const { activeTab, setActiveTab, @@ -133,6 +137,7 @@ const NodeSelector: FC = ({ noStart: !showStartTab, defaultActiveTab, hasUserInputNode, + disableStartTab, forceEnableStartTab, }) const open = openFromProps === undefined ? localOpen : openFromProps diff --git a/web/app/components/workflow/block-selector/tabs.tsx b/web/app/components/workflow/block-selector/tabs.tsx index b1ca318fd2..b0658d371c 100644 --- a/web/app/components/workflow/block-selector/tabs.tsx +++ b/web/app/components/workflow/block-selector/tabs.tsx @@ -34,6 +34,7 @@ export type TabsProps = { key: TabsEnum name: string disabled?: boolean + disabledTip?: string }> filterElem: React.ReactNode noBlocks?: boolean @@ -225,7 +226,7 @@ const Tabs: FC = ({ tab={tab} activeTab={activeTab} onActiveTabChange={onActiveTabChange} - disabledTip={disabledTip} + disabledTip={tab.disabledTip || disabledTip} /> )) } diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json index 98d2976f8e..d787164407 100644 --- a/web/eslint-suppressions.json +++ b/web/eslint-suppressions.json @@ -177,6 +177,11 @@ "count": 2 } }, + "app/(commonLayout)/snippets/[snippetId]/page.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, "app/(humanInputLayout)/form/[token]/form.tsx": { "react/set-state-in-effect": { "count": 1 @@ -4917,6 +4922,11 @@ "count": 2 } }, + "app/components/evaluation/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, "app/components/explore/banner/banner-item.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 @@ -6686,6 +6696,11 @@ "count": 2 } }, + "app/components/snippets/components/snippet-main.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, "app/components/tools/edit-custom-collection-modal/config-credentials.tsx": { "no-restricted-imports": { "count": 1 @@ -10487,6 +10502,11 @@ "count": 7 } }, + "service/use-evaluation.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, "service/use-flow.ts": { "react/no-unnecessary-use-prefix": { "count": 1 diff --git a/web/i18n/en-US/workflow.json b/web/i18n/en-US/workflow.json index a6803329ff..586db51ad5 100644 --- a/web/i18n/en-US/workflow.json +++ b/web/i18n/en-US/workflow.json @@ -1148,6 +1148,7 @@ "tabs.sources": "Sources", "tabs.start": "Start", "tabs.startDisabledTip": "Trigger node and user input node are mutually exclusive.", + "tabs.startNotSupportedTip": "The Start tab is not supported in snippets.", "tabs.tools": "Tools", "tabs.transform": "Transform", "tabs.usePlugin": "Select tool", diff --git a/web/i18n/zh-Hans/workflow.json b/web/i18n/zh-Hans/workflow.json index e98af59a4c..ae8fd5305d 100644 --- a/web/i18n/zh-Hans/workflow.json +++ b/web/i18n/zh-Hans/workflow.json @@ -1148,6 +1148,7 @@ "tabs.sources": "数据源", "tabs.start": "开始", "tabs.startDisabledTip": "触发节点与用户输入节点互斥。", + "tabs.startNotSupportedTip": "Snippet 暂不支持 Start 标签。", "tabs.tools": "工具", "tabs.transform": "转换", "tabs.usePlugin": "选择工具",