diff --git a/web/app/components/workflow/hooks/use-edges-interactions.ts b/web/app/components/workflow/hooks/use-edges-interactions.ts index 20d5019707..5dedadd2aa 100644 --- a/web/app/components/workflow/hooks/use-edges-interactions.ts +++ b/web/app/components/workflow/hooks/use-edges-interactions.ts @@ -62,6 +62,10 @@ export const useEdgesInteractions = () => { setEdges, } = store.getState() const currentEdgeIndex = edges.findIndex(edge => edge.source === nodeId && edge.sourceHandle === branchId) + + if (currentEdgeIndex < 0) + return + const currentEdge = edges[currentEdgeIndex] const newNodes = produce(getNodes(), (draft: Node[]) => { const sourceNode = draft.find(node => node.id === currentEdge.source) @@ -94,10 +98,13 @@ export const useEdgesInteractions = () => { setEdges, } = store.getState() const currentEdgeIndex = edges.findIndex(edge => edge.selected) + + if (currentEdgeIndex < 0) + return const currentEdge = edges[currentEdgeIndex] const newNodes = produce(getNodes(), (draft: Node[]) => { - const sourceNode = draft.find(node => node.id === currentEdge.source) - const targetNode = draft.find(node => node.id === currentEdge.target) + const sourceNode = draft.find(node => node.id === currentEdge?.source) + const targetNode = draft.find(node => node.id === currentEdge?.target) if (sourceNode) sourceNode.data._connectedSourceHandleIds = sourceNode.data._connectedSourceHandleIds?.filter(handleId => handleId !== currentEdge.sourceHandle) diff --git a/web/app/components/workflow/hooks/use-nodes-data.ts b/web/app/components/workflow/hooks/use-nodes-data.ts index 7a904ffc08..8a1ab815cb 100644 --- a/web/app/components/workflow/hooks/use-nodes-data.ts +++ b/web/app/components/workflow/hooks/use-nodes-data.ts @@ -1,33 +1,21 @@ import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import produce from 'immer' -import { BlockEnum } from '../types' +import type { BlockEnum } from '../types' import { NODES_EXTRA_DATA, NODES_INITIAL_DATA, } from '../constants' import { useStore } from '../store' -import type { LLMNodeType } from '../nodes/llm/types' -import type { QuestionClassifierNodeType } from '../nodes/question-classifier/types' -import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' export const useNodesInitialData = () => { const { t } = useTranslation() const nodesDefaultConfigs = useStore(s => s.nodesDefaultConfigs) - const { - currentProvider, - currentModel, - } = useModelListAndDefaultModelAndCurrentProviderAndModel(1) return useMemo(() => produce(NODES_INITIAL_DATA, (draft) => { Object.keys(draft).forEach((key) => { draft[key as BlockEnum].title = t(`workflow.blocks.${key}`) - if (currentProvider && currentModel && (key === BlockEnum.LLM || key === BlockEnum.QuestionClassifier)) { - (draft[key as BlockEnum] as LLMNodeType | QuestionClassifierNodeType).model.provider = currentProvider.provider; - (draft[key as BlockEnum] as LLMNodeType | QuestionClassifierNodeType).model.name = currentModel.model - } - if (nodesDefaultConfigs[key as BlockEnum]) { draft[key as BlockEnum] = { ...draft[key as BlockEnum], @@ -35,15 +23,15 @@ export const useNodesInitialData = () => { } } }) - }), [t, nodesDefaultConfigs, currentProvider, currentModel]) + }), [t, nodesDefaultConfigs]) } export const useNodesExtraData = () => { const { t } = useTranslation() - return produce(NODES_EXTRA_DATA, (draft) => { + return useMemo(() => produce(NODES_EXTRA_DATA, (draft) => { Object.keys(draft).forEach((key) => { draft[key as BlockEnum].about = t(`workflow.blocksAbout.${key}`) }) - }) + }), [t]) } diff --git a/web/app/components/workflow/nodes/_base/components/title-description-input.tsx b/web/app/components/workflow/nodes/_base/components/title-description-input.tsx index 86354ef0bf..17b70f6b58 100644 --- a/web/app/components/workflow/nodes/_base/components/title-description-input.tsx +++ b/web/app/components/workflow/nodes/_base/components/title-description-input.tsx @@ -6,36 +6,52 @@ import { import Textarea from 'rc-textarea' import { useTranslation } from 'react-i18next' -type InputProps = { +type TitleInputProps = { value: string - onChange: (value: string) => void + onBlur: (value: string) => void } export const TitleInput = memo(({ value, - onChange, -}: InputProps) => { + onBlur, +}: TitleInputProps) => { const { t } = useTranslation() + const [localValue, setLocalValue] = useState(value) + + const handleBlur = () => { + if (!localValue) { + setLocalValue(value) + onBlur(value) + return + } + + onBlur(localValue) + } return ( onChange(e.target.value)} + value={localValue} + onChange={e => setLocalValue(e.target.value)} className={` grow mr-2 px-1 h-6 text-base text-gray-900 font-semibold rounded-lg border border-transparent appearance-none outline-none hover:bg-gray-50 focus:border-gray-300 focus:shadow-xs focus:bg-white caret-[#295EFF] `} placeholder={t('workflow.common.addTitle') || ''} + onBlur={handleBlur} /> ) }) TitleInput.displayName = 'TitleInput' +type DescriptionInputProps = { + value: string + onChange: (value: string) => void +} export const DescriptionInput = memo(({ value, onChange, -}: InputProps) => { +}: DescriptionInputProps) => { const { t } = useTranslation() const [focus, setFocus] = useState(false) const handleFocus = useCallback(() => { diff --git a/web/app/components/workflow/nodes/_base/panel.tsx b/web/app/components/workflow/nodes/_base/panel.tsx index 8f538d2b3e..059b8bfee8 100644 --- a/web/app/components/workflow/nodes/_base/panel.tsx +++ b/web/app/components/workflow/nodes/_base/panel.tsx @@ -45,14 +45,12 @@ const BasePanel: FC = ({ handleNodeDataUpdateWithSyncDraft, } = useNodeDataUpdate() - const handleTitleChange = useCallback((title: string) => { - if (!title) - return - handleNodeDataUpdateWithSyncDraft({ id, data: { ...data, title } }) - }, [handleNodeDataUpdateWithSyncDraft, id, data]) + const handleTitleBlur = useCallback((title: string) => { + handleNodeDataUpdateWithSyncDraft({ id, data: { title } }) + }, [handleNodeDataUpdateWithSyncDraft, id]) const handleDescriptionChange = useCallback((desc: string) => { - handleNodeDataUpdateWithSyncDraft({ id, data: { ...data, desc } }) - }, [handleNodeDataUpdateWithSyncDraft, id, data]) + handleNodeDataUpdateWithSyncDraft({ id, data: { desc } }) + }, [handleNodeDataUpdateWithSyncDraft, id]) return (
@@ -66,7 +64,7 @@ const BasePanel: FC = ({ />
{ diff --git a/web/app/components/workflow/nodes/llm/panel.tsx b/web/app/components/workflow/nodes/llm/panel.tsx index 2f67a29590..a30c1fb840 100644 --- a/web/app/components/workflow/nodes/llm/panel.tsx +++ b/web/app/components/workflow/nodes/llm/panel.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import React, { useEffect } from 'react' import { useTranslation } from 'react-i18next' import MemoryConfig from '../_base/components/memory-config' import VarReferencePicker from '../_base/components/variable/var-reference-picker' @@ -18,6 +18,7 @@ import { InputVarType, type NodePanelProps } from '@/app/components/workflow/typ import BeforeRunForm from '@/app/components/workflow/nodes/_base/components/before-run-form' import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' import ResultPanel from '@/app/components/workflow/run/result-panel' +import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' const i18nPrefix = 'workflow.nodes.llm' @@ -27,6 +28,10 @@ const Panel: FC> = ({ }) => { const { t } = useTranslation() const readOnly = false + const { + currentProvider, + currentModel, + } = useModelListAndDefaultModelAndCurrentProviderAndModel(1) const { inputs, @@ -108,6 +113,15 @@ const Panel: FC> = ({ return forms })() + useEffect(() => { + if (currentProvider?.provider && currentModel?.model && !model.provider) { + handleModelChanged({ + provider: currentProvider?.provider, + modelId: currentModel?.model, + }) + } + }, [model.provider, currentProvider, currentModel, handleModelChanged]) + return (
diff --git a/web/app/components/workflow/nodes/question-classifier/panel.tsx b/web/app/components/workflow/nodes/question-classifier/panel.tsx index 4ded95a61c..8b643207ac 100644 --- a/web/app/components/workflow/nodes/question-classifier/panel.tsx +++ b/web/app/components/workflow/nodes/question-classifier/panel.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import React, { useEffect } from 'react' import { useTranslation } from 'react-i18next' import VarReferencePicker from '../_base/components/variable/var-reference-picker' import useConfig from './use-config' @@ -11,6 +11,7 @@ import ModelParameterModal from '@/app/components/header/account-setting/model-p import { InputVarType, type NodePanelProps } from '@/app/components/workflow/types' import BeforeRunForm from '@/app/components/workflow/nodes/_base/components/before-run-form' import ResultPanel from '@/app/components/workflow/run/result-panel' +import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' const i18nPrefix = 'workflow.nodes.questionClassifiers' @@ -20,6 +21,10 @@ const Panel: FC> = ({ }) => { const { t } = useTranslation() const readOnly = false + const { + currentProvider, + currentModel, + } = useModelListAndDefaultModelAndCurrentProviderAndModel(1) const { inputs, @@ -41,6 +46,15 @@ const Panel: FC> = ({ const model = inputs.model + useEffect(() => { + if (currentProvider?.provider && currentModel?.model && !model.provider) { + handleModelChanged({ + provider: currentProvider?.provider, + modelId: currentModel?.model, + }) + } + }, [model.provider, currentProvider, currentModel, handleModelChanged]) + return (