diff --git a/web/src/components/prompt-editor/constant.ts b/web/src/components/prompt-editor/constant.ts
new file mode 100644
index 000000000..b6cf30ed9
--- /dev/null
+++ b/web/src/components/prompt-editor/constant.ts
@@ -0,0 +1 @@
+export const ProgrammaticTag = 'programmatic';
diff --git a/web/src/components/prompt-editor/index.tsx b/web/src/components/prompt-editor/index.tsx
index 695d10047..ba20e62f2 100644
--- a/web/src/components/prompt-editor/index.tsx
+++ b/web/src/components/prompt-editor/index.tsx
@@ -1,13 +1,10 @@
import { CodeHighlightNode, CodeNode } from '@lexical/code';
-import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';
import {
InitialConfigType,
LexicalComposer,
} from '@lexical/react/LexicalComposer';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
-import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
-import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { HeadingNode, QuoteNode } from '@lexical/rich-text';
import {
@@ -18,9 +15,11 @@ import {
LexicalNode,
} from 'lexical';
+import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import theme from './theme';
import { VariableNode } from './variable-node';
+import { VariableOnChangePlugin } from './variable-on-change-plugin';
import VariablePickerMenuPlugin from './variable-picker-plugin';
// Catch any errors that occur during Lexical updates and log them
@@ -52,16 +51,20 @@ export function PromptEditor({ value, onChange }: IProps) {
nodes: Nodes,
};
- function onValueChange(editorState: EditorState) {
- editorState?.read(() => {
- const listNodes = $nodesOfType(VariableNode); // to be removed
- // const allNodes = $dfs();
- console.log('🚀 ~ onChange ~ allNodes:', listNodes);
+ const onValueChange = useCallback(
+ (editorState: EditorState) => {
+ editorState?.read(() => {
+ const listNodes = $nodesOfType(VariableNode); // to be removed
+ // const allNodes = $dfs();
+ console.log('🚀 ~ onChange ~ allNodes:', listNodes);
- const text = $getRoot().getTextContent();
- onChange?.(text);
- });
- }
+ const text = $getRoot().getTextContent();
+
+ onChange?.(text);
+ });
+ },
+ [onChange],
+ );
return (
@@ -74,10 +77,8 @@ export function PromptEditor({ value, onChange }: IProps) {
}
ErrorBoundary={LexicalErrorBoundary}
/>
-
-
-
+
);
}
diff --git a/web/src/components/prompt-editor/variable-on-change-plugin.tsx b/web/src/components/prompt-editor/variable-on-change-plugin.tsx
new file mode 100644
index 000000000..86fa66db4
--- /dev/null
+++ b/web/src/components/prompt-editor/variable-on-change-plugin.tsx
@@ -0,0 +1,35 @@
+import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
+import { EditorState, LexicalEditor } from 'lexical';
+import { useEffect } from 'react';
+import { ProgrammaticTag } from './constant';
+
+interface IProps {
+ onChange: (
+ editorState: EditorState,
+ editor?: LexicalEditor,
+ tags?: Set,
+ ) => void;
+}
+
+export function VariableOnChangePlugin({ onChange }: IProps) {
+ // Access the editor through the LexicalComposerContext
+ const [editor] = useLexicalComposerContext();
+ // Wrap our listener in useEffect to handle the teardown and avoid stale references.
+ useEffect(() => {
+ // most listeners return a teardown function that can be called to clean them up.
+ return editor.registerUpdateListener(
+ ({ editorState, tags, dirtyElements }) => {
+ // Check if there is a "programmatic" tag
+ const isProgrammaticUpdate = tags.has(ProgrammaticTag);
+
+ // The onchange event is only triggered when the data is manually updated
+ // Otherwise, the content will be displayed incorrectly.
+ if (dirtyElements.size > 0 && !isProgrammaticUpdate) {
+ onChange(editorState);
+ }
+ },
+ );
+ }, [editor, onChange]);
+
+ return null;
+}
diff --git a/web/src/components/prompt-editor/variable-picker-plugin.tsx b/web/src/components/prompt-editor/variable-picker-plugin.tsx
index 4a30ad3bc..b50e540ee 100644
--- a/web/src/components/prompt-editor/variable-picker-plugin.tsx
+++ b/web/src/components/prompt-editor/variable-picker-plugin.tsx
@@ -33,6 +33,7 @@ import { FlowFormContext } from '@/pages/flow/context';
import { useBuildComponentIdSelectOptions } from '@/pages/flow/hooks/use-get-begin-query';
import { $createVariableNode } from './variable-node';
+import { ProgrammaticTag } from './constant';
import './index.css';
class VariableInnerOption extends MenuOption {
label: string;
@@ -215,9 +216,12 @@ export default function VariablePickerMenuPlugin({
useEffect(() => {
if (editor && value && isFirstRender.current) {
isFirstRender.current = false;
- editor.update(() => {
- parseTextToVariableNodes(value);
- });
+ editor.update(
+ () => {
+ parseTextToVariableNodes(value);
+ },
+ { tag: ProgrammaticTag },
+ );
}
}, [parseTextToVariableNodes, editor, value]);
diff --git a/web/src/hooks/flow-hooks.ts b/web/src/hooks/flow-hooks.ts
index e733e9be1..5a0964dc0 100644
--- a/web/src/hooks/flow-hooks.ts
+++ b/web/src/hooks/flow-hooks.ts
@@ -3,6 +3,7 @@ import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow';
import { IDebugSingleRequestBody } from '@/interfaces/request/flow';
import i18n from '@/locales/config';
import { useGetSharedChatSearchParams } from '@/pages/chat/shared-hooks';
+import { BeginId } from '@/pages/flow/constant';
import flowService from '@/services/flow-service';
import { buildMessageListWithUuid } from '@/utils/chat';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
@@ -16,7 +17,7 @@ export const EmptyDsl = {
graph: {
nodes: [
{
- id: 'begin',
+ id: BeginId,
type: 'beginNode',
position: {
x: 50,
diff --git a/web/src/pages/flow/form/generate-form/index.tsx b/web/src/pages/flow/form/generate-form/index.tsx
index 076ed2518..f21ce1035 100644
--- a/web/src/pages/flow/form/generate-form/index.tsx
+++ b/web/src/pages/flow/form/generate-form/index.tsx
@@ -35,6 +35,7 @@ const GenerateForm = ({ onValuesChange, form }: IOperatorForm) => {
},
]}
>
+ {/* */}
{
useDebounceEffect(
() => {
- saveAgent();
+ // saveAgent();
},
[nodes, edges],
{