mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### 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:
@ -12,41 +12,9 @@ import { RightHandleStyle } from './handle-icon';
|
||||
import styles from './index.less';
|
||||
import NodeHeader from './node-header';
|
||||
import { NodeWrapper } from './node-wrapper';
|
||||
import { ResizeIcon, controlStyle } from './resize-icon';
|
||||
import { ToolBar } from './toolbar';
|
||||
|
||||
function ResizeIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="2"
|
||||
stroke="#5025f9"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
right: 5,
|
||||
bottom: 5,
|
||||
}}
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<polyline points="16 20 20 20 20 16" />
|
||||
<line x1="14" y1="14" x2="20" y2="20" />
|
||||
<polyline points="8 4 4 4 4 8" />
|
||||
<line x1="4" y1="4" x2="10" y2="10" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
const controlStyle = {
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
cursor: 'nwse-resize',
|
||||
};
|
||||
|
||||
export function InnerIterationNode({
|
||||
id,
|
||||
data,
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
import { NodeProps, NodeResizeControl } from '@xyflow/react';
|
||||
import { Flex, Form, Input } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import NodeDropdown from './dropdown';
|
||||
|
||||
import SvgIcon from '@/components/svg-icon';
|
||||
import { useTheme } from '@/components/theme-provider';
|
||||
import { INoteNode } from '@/interfaces/database/flow';
|
||||
import { memo, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHandleNodeNameChange } from '../../hooks';
|
||||
import { useHandleFormValuesChange } from '../../hooks/use-watch-form-change';
|
||||
import styles from './index.less';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
const controlStyle = {
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
};
|
||||
|
||||
function NoteNode({ data, id }: NodeProps<INoteNode>) {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const { theme } = useTheme();
|
||||
|
||||
const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({
|
||||
id,
|
||||
data,
|
||||
});
|
||||
const { handleValuesChange } = useHandleFormValuesChange(id);
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue(data?.form);
|
||||
}, [form, data?.form]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeResizeControl style={controlStyle} minWidth={190} minHeight={128}>
|
||||
<SvgIcon
|
||||
name="resize"
|
||||
width={12}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
right: 5,
|
||||
bottom: 5,
|
||||
cursor: 'nwse-resize',
|
||||
}}
|
||||
></SvgIcon>
|
||||
</NodeResizeControl>
|
||||
<section
|
||||
className={classNames(
|
||||
styles.noteNode,
|
||||
theme === 'dark' ? styles.dark : '',
|
||||
)}
|
||||
>
|
||||
<Flex
|
||||
justify={'space-between'}
|
||||
className={classNames('note-drag-handle')}
|
||||
align="center"
|
||||
gap={6}
|
||||
>
|
||||
<SvgIcon name="note" width={14}></SvgIcon>
|
||||
<Input
|
||||
value={name ?? t('flow.note')}
|
||||
onBlur={handleNameBlur}
|
||||
onChange={handleNameChange}
|
||||
className={styles.noteName}
|
||||
></Input>
|
||||
<NodeDropdown id={id} label={data.label}></NodeDropdown>
|
||||
</Flex>
|
||||
<Form
|
||||
onValuesChange={handleValuesChange}
|
||||
form={form}
|
||||
className={styles.noteForm}
|
||||
>
|
||||
<Form.Item name="text" noStyle>
|
||||
<TextArea
|
||||
rows={3}
|
||||
placeholder={t('flow.notePlaceholder')}
|
||||
className={styles.noteTextarea}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(NoteNode);
|
||||
76
web/src/pages/agent/canvas/node/note-node/index.tsx
Normal file
76
web/src/pages/agent/canvas/node/note-node/index.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import { NodeProps, NodeResizeControl } from '@xyflow/react';
|
||||
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { INoteNode } from '@/interfaces/database/flow';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { NotebookPen } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
import { NodeWrapper } from '../node-wrapper';
|
||||
import { ResizeIcon, controlStyle } from '../resize-icon';
|
||||
import { useChangeName, useWatchFormChange } from './use-watch-change';
|
||||
|
||||
const FormSchema = z.object({
|
||||
text: z.string(),
|
||||
});
|
||||
|
||||
function NoteNode({ data, id }: NodeProps<INoteNode>) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: data.form,
|
||||
});
|
||||
|
||||
const { handleChangeName } = useChangeName(id);
|
||||
|
||||
useWatchFormChange(id, form);
|
||||
|
||||
return (
|
||||
<NodeWrapper className="p-0 w-full h-full flex flex-col rounded-md ">
|
||||
<NodeResizeControl minWidth={190} minHeight={128} style={controlStyle}>
|
||||
<ResizeIcon />
|
||||
</NodeResizeControl>
|
||||
<section className="px-1 py-2 flex gap-2 bg-background-highlight items-center note-drag-handle rounded-s-md">
|
||||
<NotebookPen className="size-4" />
|
||||
<Input
|
||||
type="text"
|
||||
defaultValue={data.name}
|
||||
onChange={handleChangeName}
|
||||
></Input>
|
||||
</section>
|
||||
<Form {...form}>
|
||||
<form className="flex-1">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="text"
|
||||
render={({ field }) => (
|
||||
<FormItem className="h-full">
|
||||
<FormControl>
|
||||
<Textarea
|
||||
placeholder={t('flow.notePlaceholder')}
|
||||
className="resize-none rounded-none p-1 h-full overflow-auto bg-background-header-bar"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
</NodeWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(NoteNode);
|
||||
@ -0,0 +1,31 @@
|
||||
import useGraphStore from '@/pages/agent/store';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { UseFormReturn, useWatch } from 'react-hook-form';
|
||||
|
||||
export function useWatchFormChange(id?: string, form?: UseFormReturn<any>) {
|
||||
let values = useWatch({ control: form?.control });
|
||||
const updateNodeForm = useGraphStore((state) => state.updateNodeForm);
|
||||
|
||||
useEffect(() => {
|
||||
// Manually triggered form updates are synchronized to the canvas
|
||||
if (id) {
|
||||
values = form?.getValues() || {};
|
||||
let nextValues: any = values;
|
||||
|
||||
updateNodeForm(id, nextValues);
|
||||
}
|
||||
}, [id, updateNodeForm, values]);
|
||||
}
|
||||
|
||||
export function useChangeName(id: string) {
|
||||
const updateNodeName = useGraphStore((state) => state.updateNodeName);
|
||||
|
||||
const handleChangeName = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateNodeName(id, e.target.value.trim());
|
||||
},
|
||||
[id, updateNodeName],
|
||||
);
|
||||
|
||||
return { handleChangeName };
|
||||
}
|
||||
32
web/src/pages/agent/canvas/node/resize-icon.tsx
Normal file
32
web/src/pages/agent/canvas/node/resize-icon.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
export function ResizeIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="2"
|
||||
stroke="#5025f9"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
right: 5,
|
||||
bottom: 5,
|
||||
}}
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<polyline points="16 20 20 20 20 16" />
|
||||
<line x1="14" y1="14" x2="20" y2="20" />
|
||||
<polyline points="8 4 4 4 4 8" />
|
||||
<line x1="4" y1="4" x2="10" y2="10" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export const controlStyle = {
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
cursor: 'nwse-resize',
|
||||
};
|
||||
Reference in New Issue
Block a user