From b6465327c11242ab4f979d2a7192df46096913e7 Mon Sep 17 00:00:00 2001 From: zhsama Date: Sat, 31 Jan 2026 22:10:25 +0800 Subject: [PATCH] fix: Fix race condition in prompt editor reference sync --- .gitignore | 1 + .../plugins/component-picker-block/index.tsx | 25 ++++++++++++++----- .../nodes/_base/hooks/use-one-step-run.ts | 5 ++-- .../hooks/use-mixed-variable-extractor.ts | 7 +++++- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index e0db6b0a19..dce9f66d2e 100644 --- a/.gitignore +++ b/.gitignore @@ -222,6 +222,7 @@ mise.toml # AI Assistant +.sisyphus/ .roo/ api/.env.backup /clickzetta diff --git a/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx b/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx index 683481fd3a..028a49424a 100644 --- a/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx +++ b/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx @@ -34,6 +34,7 @@ import { Fragment, memo, useCallback, + useLayoutEffect, useMemo, useState, } from 'react' @@ -71,6 +72,19 @@ type ComponentPickerProps = { isSupportFileVar?: boolean isSupportSandbox?: boolean } + +type ReferenceSyncProps = { + anchor: HTMLElement | null + setReference: (node: HTMLElement | null) => void +} + +const ReferenceSync = ({ anchor, setReference }: ReferenceSyncProps) => { + useLayoutEffect(() => { + setReference(anchor) + }, [anchor, setReference]) + return null +} + const ComponentPicker = ({ triggerString, contextBlock, @@ -265,14 +279,12 @@ const ComponentPicker = ({ if (!(anchorElementRef.current && (isSandboxMenu || allFlattenOptions.length || workflowVariableBlock?.show))) return null - setTimeout(() => { - if (anchorElementRef.current) - refs.setReference(anchorElementRef.current) - }, 0) + const anchorElement = anchorElementRef.current if (isSandboxMenu) { return ( <> + {ReactDOM.createPortal(
, - anchorElementRef.current, + anchorElement, )} ) @@ -355,6 +367,7 @@ const ComponentPicker = ({ return ( <> + { ReactDOM.createPortal(
@@ -443,7 +456,7 @@ const ComponentPicker = ({ )}
, - anchorElementRef.current, + anchorElement, ) } diff --git a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts index e98017f794..4174d0eaa0 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts @@ -647,7 +647,7 @@ const useOneStepRun = ({ return null }, [flowId, id, data, handleNodeDataUpdate, cancelPluginSingleRun]) - const checkValidWrap = () => { + const checkValidWrap = useCallback(() => { if (!checkValid) return { isValid: true, errorMessage: '' } const res = checkValid(data, t, moreDataForCheckValid) @@ -665,7 +665,8 @@ const useOneStepRun = ({ }) } return res - } + }, [checkValid, data, handleNodeDataUpdate, id, t, moreDataForCheckValid]) + const [canShowSingleRun, setCanShowSingleRun] = useState(false) const isShowSingleRun = data._isSingleRun && canShowSingleRun const [iterationRunResult, setIterationRunResult] = useState([]) diff --git a/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/hooks/use-mixed-variable-extractor.ts b/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/hooks/use-mixed-variable-extractor.ts index fe3c030699..d0229e440f 100644 --- a/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/hooks/use-mixed-variable-extractor.ts +++ b/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/hooks/use-mixed-variable-extractor.ts @@ -10,6 +10,7 @@ import type { Node as WorkflowNode, } from '@/app/components/workflow/types' import { useCallback, useMemo } from 'react' +import Toast from '@/app/components/base/toast' import { NULL_STRATEGY } from '@/app/components/workflow/nodes/_base/constants' import { Type } from '@/app/components/workflow/nodes/llm/types' import { BlockEnum, EditionType, isPromptMessageContext, PromptRole, VarType } from '@/app/components/workflow/types' @@ -441,7 +442,11 @@ export function useMixedVariableExtractor({ detectAgentFromText: payload.detectAgentFromText, }) } - catch { + catch (error) { + Toast.notify({ + type: 'error', + message: error as string, + }) } }, [applyNestedNodeGraphData, configsMap?.flowId, configsMap?.flowType, paramKey, resolveNestedNodeParameterSchema, toolNodeId])