Feat: Add note node #3221 (#8728)

### What problem does this PR solve?

Feat: Add note node #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-07-08 19:18:55 +08:00
committed by GitHub
parent 2a03d49a84
commit 3fe143d84a
17 changed files with 414 additions and 407 deletions

View File

@ -269,9 +269,13 @@ function useResizeIterationNode() {
return { resizeIterationNode };
}
type CanvasMouseEvent = Pick<
React.MouseEvent<HTMLElement>,
'clientX' | 'clientY'
>;
export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
const { edges, nodes, addEdge, addNode, getNode, updateNode } = useGraphStore(
const { edges, nodes, addEdge, addNode, getNode } = useGraphStore(
(state) => state,
);
const getNodeName = useGetNodeName();
@ -290,7 +294,7 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
position: Position.Right,
},
) =>
(event?: React.MouseEvent<HTMLElement>) => {
(event?: CanvasMouseEvent) => {
const nodeId = params.nodeId;
const node = getNode(nodeId);
@ -303,7 +307,7 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
y: event?.clientY || 0,
});
if (params.position === Position.Right) {
if (params.position === Position.Right && type !== Operator.Note) {
position = calculateNewlyBackChildPosition(nodeId, params.id);
}
@ -420,9 +424,16 @@ export function useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>) {
initializeOperatorParams,
nodes,
reactFlowInstance,
updateNode,
resizeIterationNode,
],
);
return { addCanvasNode };
const addNoteNode = useCallback(
(e: CanvasMouseEvent) => {
addCanvasNode(Operator.Note)(e);
},
[addCanvasNode],
);
return { addCanvasNode, addNoteNode };
}

View File

@ -1,4 +1,8 @@
import { IEventList, MessageEventType } from '@/hooks/use-send-message';
import {
IEventList,
INodeEvent,
MessageEventType,
} from '@/hooks/use-send-message';
import { useCallback, useMemo, useState } from 'react';
export const ExcludeTypes = [
@ -41,11 +45,13 @@ export function useCacheChatLog() {
}, []);
const currentEventListWithoutMessage = useMemo(() => {
return eventList.filter(
const list = eventList.filter(
(x) =>
x.message_id === currentMessageId &&
ExcludeTypes.every((y) => y !== x.event),
);
return list as INodeEvent[];
}, [currentMessageId, eventList]);
return {

View File

@ -0,0 +1,35 @@
import { useMouse } from 'ahooks';
import { useCallback, useEffect, useRef, useState } from 'react';
export function useMoveNote() {
const ref = useRef<SVGSVGElement>(null);
const mouse = useMouse();
const [imgVisible, setImgVisible] = useState(false);
const toggleVisible = useCallback((visible: boolean) => {
setImgVisible(visible);
}, []);
const showImage = useCallback(() => {
toggleVisible(true);
}, [toggleVisible]);
const hideImage = useCallback(() => {
toggleVisible(false);
}, [toggleVisible]);
useEffect(() => {
if (ref.current) {
ref.current.style.top = `${mouse.clientY - 70}px`;
ref.current.style.left = `${mouse.clientX + 10}px`;
}
}, [mouse.clientX, mouse.clientY]);
return {
ref,
showImage,
hideImage,
mouse,
imgVisible,
};
}

View File

@ -1,154 +1,6 @@
import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent';
import { settledModelVariableMap } from '@/constants/knowledge';
import { omit } from 'lodash';
import { useCallback, useEffect } from 'react';
import { useEffect } from 'react';
import { UseFormReturn, useWatch } from 'react-hook-form';
import { Operator } from '../constant';
import useGraphStore from '../store';
import { buildCategorizeObjectFromList, convertToStringArray } from '../utils';
export const useHandleFormValuesChange = (
operatorName: Operator,
id?: string,
form?: UseFormReturn,
) => {
const updateNodeForm = useGraphStore((state) => state.updateNodeForm);
const handleValuesChange = useCallback(
(changedValues: any, values: any) => {
let nextValues: any = values;
// Fixed the issue that the related form value does not change after selecting the freedom field of the model
if (
Object.keys(changedValues).length === 1 &&
'parameter' in changedValues &&
changedValues['parameter'] in settledModelVariableMap
) {
nextValues = {
...values,
...settledModelVariableMap[
changedValues['parameter'] as keyof typeof settledModelVariableMap
],
};
}
if (id) {
updateNodeForm(id, nextValues);
}
},
[updateNodeForm, id],
);
let values = useWatch({ control: form?.control });
// console.log('🚀 ~ x:', values);
useEffect(() => {
// Manually triggered form updates are synchronized to the canvas
if (id && form?.formState.isDirty) {
values = form?.getValues();
let nextValues: any = values;
// run(id, nextValues);
const categoryDescriptionRegex = /items\.\d+\.name/g;
if (operatorName === Operator.Categorize) {
console.log('🚀 ~ useEffect ~ values:', values);
const categoryDescription = Array.isArray(values.items)
? buildCategorizeObjectFromList(values.items)
: {};
if (categoryDescription) {
nextValues = {
...omit(values, 'items'),
category_description: categoryDescription,
};
}
} else if (operatorName === Operator.Message) {
nextValues = {
...values,
content: convertToStringArray(values.content),
};
}
updateNodeForm(id, nextValues);
}
}, [form?.formState.isDirty, id, operatorName, updateNodeForm, values]);
// useEffect(() => {
// form?.subscribe({
// formState: { values: true },
// callback: ({ values }) => {
// // console.info('subscribe', values);
// },
// });
// }, [form]);
return { handleValuesChange };
useEffect(() => {
const subscription = form?.watch((value, { name, type, values }) => {
if (id && name) {
let nextValues: any = value;
// Fixed the issue that the related form value does not change after selecting the freedom field of the model
if (
name === 'parameter' &&
value['parameter'] in settledModelVariableMap
) {
nextValues = {
...value,
...settledModelVariableMap[
value['parameter'] as keyof typeof settledModelVariableMap
],
};
}
const categoryDescriptionRegex = /items\.\d+\.name/g;
if (
operatorName === Operator.Categorize &&
categoryDescriptionRegex.test(name)
) {
nextValues = {
...omit(value, 'items'),
category_description: buildCategorizeObjectFromList(value.items),
};
}
if (
operatorName === Operator.Code &&
type === 'change' &&
name === 'lang'
) {
nextValues = {
...value,
script: CodeTemplateStrMap[value.lang as ProgrammingLanguage],
};
}
if (operatorName === Operator.Message) {
nextValues = {
...value,
content: convertToStringArray(value.content),
};
}
// Manually triggered form updates are synchronized to the canvas
if (form.formState.isDirty) {
console.log(
'🚀 ~ useEffect ~ value:',
name,
type,
values,
operatorName,
);
// run(id, nextValues);
updateNodeForm(id, nextValues);
}
}
});
return () => subscription?.unsubscribe();
}, [form, form?.watch, id, operatorName, updateNodeForm]);
return { handleValuesChange };
};
export function useWatchFormChange(id?: string, form?: UseFormReturn) {
let values = useWatch({ control: form?.control });