mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
Feat: Fixed the issue where the cursor would go to the end when changing its own data #9869 (#10316)
### What problem does this PR solve? Feat: Fixed the issue where the cursor would go to the end when changing its own data #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -8,7 +8,7 @@ import {
|
|||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
AlertDialogTrigger,
|
AlertDialogTrigger,
|
||||||
} from '@/components/ui/alert-dialog';
|
} from '@/components/ui/alert-dialog';
|
||||||
import { PropsWithChildren } from 'react';
|
import { DialogProps } from '@radix-ui/react-dialog';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@ -24,7 +24,10 @@ export function ConfirmDeleteDialog({
|
|||||||
onOk,
|
onOk,
|
||||||
onCancel,
|
onCancel,
|
||||||
hidden = false,
|
hidden = false,
|
||||||
}: IProps & PropsWithChildren) {
|
onOpenChange,
|
||||||
|
open,
|
||||||
|
defaultOpen,
|
||||||
|
}: IProps & DialogProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
@ -32,7 +35,11 @@ export function ConfirmDeleteDialog({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AlertDialog>
|
<AlertDialog
|
||||||
|
onOpenChange={onOpenChange}
|
||||||
|
open={open}
|
||||||
|
defaultOpen={defaultOpen}
|
||||||
|
>
|
||||||
<AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
|
<AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
|
||||||
<AlertDialogContent
|
<AlertDialogContent
|
||||||
onSelect={(e) => e.preventDefault()}
|
onSelect={(e) => e.preventDefault()}
|
||||||
|
|||||||
@ -1765,6 +1765,9 @@ Important structured information may include: names, dates, locations, events, k
|
|||||||
metadata: `Content: [INSERT CONTENT HERE]`,
|
metadata: `Content: [INSERT CONTENT HERE]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
cancel: 'Cancel',
|
||||||
|
swicthPromptMessage:
|
||||||
|
'The prompt word will change. Please confirm whether to abandon the existing prompt word?',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1683,6 +1683,8 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
|||||||
metadata: `内容:[在此处插入内容]`,
|
metadata: `内容:[在此处插入内容]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
cancel: '取消',
|
||||||
|
switchPromptMessage: '提示词将发生变化,请确认是否放弃已有提示词?',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
|
|||||||
import {
|
import {
|
||||||
LexicalTypeaheadMenuPlugin,
|
LexicalTypeaheadMenuPlugin,
|
||||||
MenuOption,
|
MenuOption,
|
||||||
useBasicTypeaheadTriggerMatch,
|
|
||||||
} from '@lexical/react/LexicalTypeaheadMenuPlugin';
|
} from '@lexical/react/LexicalTypeaheadMenuPlugin';
|
||||||
import {
|
import {
|
||||||
$createParagraphNode,
|
$createParagraphNode,
|
||||||
@ -131,9 +130,23 @@ export default function VariablePickerMenuPlugin({
|
|||||||
baseOptions,
|
baseOptions,
|
||||||
}: VariablePickerMenuPluginProps): JSX.Element {
|
}: VariablePickerMenuPluginProps): JSX.Element {
|
||||||
const [editor] = useLexicalComposerContext();
|
const [editor] = useLexicalComposerContext();
|
||||||
const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('/', {
|
|
||||||
minLength: 0,
|
// const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('/', {
|
||||||
});
|
// minLength: 0,
|
||||||
|
// });
|
||||||
|
|
||||||
|
const testTriggerFn = React.useCallback((text: string) => {
|
||||||
|
const lastChar = text.slice(-1);
|
||||||
|
if (lastChar === '/') {
|
||||||
|
console.log('Found trigger character "/"');
|
||||||
|
return {
|
||||||
|
leadOffset: text.length - 1,
|
||||||
|
matchingString: '',
|
||||||
|
replaceableString: '/',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, []);
|
||||||
|
|
||||||
const previousValue = useRef<string | undefined>();
|
const previousValue = useRef<string | undefined>();
|
||||||
|
|
||||||
@ -291,6 +304,21 @@ export default function VariablePickerMenuPlugin({
|
|||||||
}
|
}
|
||||||
}, [parseTextToVariableNodes, editor, value]);
|
}, [parseTextToVariableNodes, editor, value]);
|
||||||
|
|
||||||
|
// Fixed the issue where the cursor would go to the end when changing its own data
|
||||||
|
useEffect(() => {
|
||||||
|
return editor.registerUpdateListener(({ editorState, tags }) => {
|
||||||
|
// If we trigger the programmatic update ourselves, we should not write back to avoid an infinite loop.
|
||||||
|
if (tags.has(ProgrammaticTag)) return;
|
||||||
|
|
||||||
|
editorState.read(() => {
|
||||||
|
const text = $getRoot().getTextContent();
|
||||||
|
if (text !== previousValue.current) {
|
||||||
|
previousValue.current = text;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [editor]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LexicalTypeaheadMenuPlugin<VariableOption | VariableInnerOption>
|
<LexicalTypeaheadMenuPlugin<VariableOption | VariableInnerOption>
|
||||||
onQueryChange={setQueryString}
|
onQueryChange={setQueryString}
|
||||||
@ -301,7 +329,7 @@ export default function VariablePickerMenuPlugin({
|
|||||||
closeMenu,
|
closeMenu,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
triggerFn={checkForTriggerMatch}
|
triggerFn={testTriggerFn}
|
||||||
options={buildNextOptions()}
|
options={buildNextOptions()}
|
||||||
menuRenderFn={(anchorElementRef, { selectOptionAndCleanUp }) => {
|
menuRenderFn={(anchorElementRef, { selectOptionAndCleanUp }) => {
|
||||||
const nextOptions = buildNextOptions();
|
const nextOptions = buildNextOptions();
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
|
||||||
import { LargeModelFormField } from '@/components/large-model-form-field';
|
import { LargeModelFormField } from '@/components/large-model-form-field';
|
||||||
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
|
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
|
||||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||||
@ -6,7 +7,7 @@ import { Form } from '@/components/ui/form';
|
|||||||
import { PromptEditor } from '@/pages/agent/form/components/prompt-editor';
|
import { PromptEditor } from '@/pages/agent/form/components/prompt-editor';
|
||||||
import { buildOptions } from '@/utils/form';
|
import { buildOptions } from '@/utils/form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo } from 'react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@ -19,6 +20,7 @@ import { useFormValues } from '../../hooks/use-form-values';
|
|||||||
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
||||||
import { INextOperatorForm } from '../../interface';
|
import { INextOperatorForm } from '../../interface';
|
||||||
import { FormWrapper } from '../components/form-wrapper';
|
import { FormWrapper } from '../components/form-wrapper';
|
||||||
|
import { useSwitchPrompt } from './use-switch-prompt';
|
||||||
|
|
||||||
export const FormSchema = z.object({
|
export const FormSchema = z.object({
|
||||||
field_name: z.string(),
|
field_name: z.string(),
|
||||||
@ -43,25 +45,13 @@ const ExtractorForm = ({ node }: INextOperatorForm) => {
|
|||||||
|
|
||||||
const options = buildOptions(ContextGeneratorFieldName, t, 'dataflow');
|
const options = buildOptions(ContextGeneratorFieldName, t, 'dataflow');
|
||||||
|
|
||||||
const setPromptValue = useCallback(
|
const {
|
||||||
(field: keyof ExtractorFormSchemaType, key: string, value: string) => {
|
handleFieldNameChange,
|
||||||
form.setValue(field, t(`dataflow.prompts.${key}.${value}`), {
|
confirmSwitch,
|
||||||
shouldDirty: true,
|
hideModal,
|
||||||
shouldValidate: true,
|
visible,
|
||||||
});
|
cancelSwitch,
|
||||||
},
|
} = useSwitchPrompt(form);
|
||||||
[form, t],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleFieldNameChange = useCallback(
|
|
||||||
(value: string) => {
|
|
||||||
if (value) {
|
|
||||||
setPromptValue('sys_prompt', 'system', value);
|
|
||||||
setPromptValue('prompts', 'user', value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setPromptValue],
|
|
||||||
);
|
|
||||||
|
|
||||||
useWatchFormChange(node?.id, form);
|
useWatchFormChange(node?.id, form);
|
||||||
|
|
||||||
@ -96,6 +86,15 @@ const ExtractorForm = ({ node }: INextOperatorForm) => {
|
|||||||
></PromptEditor>
|
></PromptEditor>
|
||||||
</RAGFlowFormItem>
|
</RAGFlowFormItem>
|
||||||
</FormWrapper>
|
</FormWrapper>
|
||||||
|
{visible && (
|
||||||
|
<ConfirmDeleteDialog
|
||||||
|
title={t('dataflow.switchPromptMessage')}
|
||||||
|
open
|
||||||
|
onOpenChange={hideModal}
|
||||||
|
onOk={confirmSwitch}
|
||||||
|
onCancel={cancelSwitch}
|
||||||
|
></ConfirmDeleteDialog>
|
||||||
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
|
||||||
|
import { useSetModalState } from '@/hooks/common-hooks';
|
||||||
|
import { useCallback, useRef } from 'react';
|
||||||
|
import { UseFormReturn } from 'react-hook-form';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const FormSchema = z.object({
|
||||||
|
field_name: z.string(),
|
||||||
|
sys_prompt: z.string(),
|
||||||
|
prompts: z.string().optional(),
|
||||||
|
...LlmSettingSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type ExtractorFormSchemaType = z.infer<typeof FormSchema>;
|
||||||
|
|
||||||
|
export function useSwitchPrompt(form: UseFormReturn<ExtractorFormSchemaType>) {
|
||||||
|
const { visible, showModal, hideModal } = useSetModalState();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const previousFieldNames = useRef<string[]>([form.getValues('field_name')]);
|
||||||
|
|
||||||
|
const setPromptValue = useCallback(
|
||||||
|
(field: keyof ExtractorFormSchemaType, key: string, value: string) => {
|
||||||
|
form.setValue(field, t(`dataflow.prompts.${key}.${value}`), {
|
||||||
|
shouldDirty: true,
|
||||||
|
shouldValidate: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[form, t],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFieldNameChange = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
if (value) {
|
||||||
|
const names = previousFieldNames.current;
|
||||||
|
if (names.length > 1) {
|
||||||
|
names.shift();
|
||||||
|
}
|
||||||
|
names.push(value);
|
||||||
|
showModal();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[showModal],
|
||||||
|
);
|
||||||
|
|
||||||
|
const confirmSwitch = useCallback(() => {
|
||||||
|
const value = form.getValues('field_name');
|
||||||
|
setPromptValue('sys_prompt', 'system', value);
|
||||||
|
setPromptValue('prompts', 'user', value);
|
||||||
|
}, [form, setPromptValue]);
|
||||||
|
|
||||||
|
const cancelSwitch = useCallback(() => {
|
||||||
|
const previousValue = previousFieldNames.current.at(-2);
|
||||||
|
if (previousValue) {
|
||||||
|
form.setValue('field_name', previousValue, {
|
||||||
|
shouldDirty: true,
|
||||||
|
shouldValidate: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [form]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleFieldNameChange,
|
||||||
|
confirmSwitch,
|
||||||
|
hideModal,
|
||||||
|
visible,
|
||||||
|
cancelSwitch,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -4,11 +4,9 @@ import { useCallback } from 'react';
|
|||||||
export function useCancelCurrentDataflow({
|
export function useCancelCurrentDataflow({
|
||||||
messageId,
|
messageId,
|
||||||
setMessageId,
|
setMessageId,
|
||||||
hideLogSheet,
|
|
||||||
}: {
|
}: {
|
||||||
messageId: string;
|
messageId: string;
|
||||||
setMessageId: (messageId: string) => void;
|
setMessageId: (messageId: string) => void;
|
||||||
hideLogSheet(): void;
|
|
||||||
}) {
|
}) {
|
||||||
const { cancelDataflow } = useCancelDataflow();
|
const { cancelDataflow } = useCancelDataflow();
|
||||||
|
|
||||||
@ -16,9 +14,8 @@ export function useCancelCurrentDataflow({
|
|||||||
const code = await cancelDataflow(messageId);
|
const code = await cancelDataflow(messageId);
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
setMessageId('');
|
setMessageId('');
|
||||||
hideLogSheet();
|
|
||||||
}
|
}
|
||||||
}, [cancelDataflow, hideLogSheet, messageId, setMessageId]);
|
}, [cancelDataflow, messageId, setMessageId]);
|
||||||
|
|
||||||
return { handleCancel };
|
return { handleCancel };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,10 +4,6 @@ import useGraphStore from '../store';
|
|||||||
|
|
||||||
export function useWatchFormChange(id?: string, form?: UseFormReturn<any>) {
|
export function useWatchFormChange(id?: string, form?: UseFormReturn<any>) {
|
||||||
let values = useWatch({ control: form?.control });
|
let values = useWatch({ control: form?.control });
|
||||||
console.log(
|
|
||||||
'🚀 ~ useWatchFormChange ~ values:',
|
|
||||||
JSON.stringify(values, null, 2),
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateNodeForm = useGraphStore((state) => state.updateNodeForm);
|
const updateNodeForm = useGraphStore((state) => state.updateNodeForm);
|
||||||
|
|
||||||
|
|||||||
@ -103,7 +103,6 @@ export default function DataFlow() {
|
|||||||
const { handleCancel } = useCancelCurrentDataflow({
|
const { handleCancel } = useCancelCurrentDataflow({
|
||||||
messageId,
|
messageId,
|
||||||
setMessageId,
|
setMessageId,
|
||||||
hideLogSheet,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const time = useWatchAgentChange(chatDrawerVisible);
|
const time = useWatchAgentChange(chatDrawerVisible);
|
||||||
|
|||||||
@ -57,16 +57,16 @@ export function LogSheet({
|
|||||||
</section>
|
</section>
|
||||||
{isParsing ? (
|
{isParsing ? (
|
||||||
<Button
|
<Button
|
||||||
className="w-full mt-8 bg-state-error/10 text-state-error"
|
className="w-full mt-8 bg-state-error/10 text-state-error hover:bg-state-error hover:text-bg-base"
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
>
|
>
|
||||||
<CirclePause /> Cancel
|
<CirclePause /> {t('dataflow.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
onClick={handleDownloadJson}
|
onClick={handleDownloadJson}
|
||||||
disabled={isEndOutputEmpty(logs)}
|
disabled={isEndOutputEmpty(logs)}
|
||||||
className="w-full mt-8"
|
className="w-full mt-8 bg-accent-primary-5 text-text-secondary hover:bg-accent-primary-5 hover:text-accent-primary hover:border-accent-primary hover:border"
|
||||||
>
|
>
|
||||||
<SquareArrowOutUpRight />
|
<SquareArrowOutUpRight />
|
||||||
{t('dataflow.exportJson')}
|
{t('dataflow.exportJson')}
|
||||||
|
|||||||
@ -76,7 +76,10 @@ module.exports = {
|
|||||||
'border-default': 'var(--border-default)',
|
'border-default': 'var(--border-default)',
|
||||||
'border-accent': 'var(--border-accent)',
|
'border-accent': 'var(--border-accent)',
|
||||||
'border-button': 'var(--border-button)',
|
'border-button': 'var(--border-button)',
|
||||||
'accent-primary': 'var(--accent-primary)',
|
'accent-primary': {
|
||||||
|
DEFAULT: 'rgb(var(--accent-primary) / <alpha-value>)',
|
||||||
|
5: 'rgba(var(--accent-primary) / 0.05)', // 5%
|
||||||
|
},
|
||||||
'bg-accent': 'var(--bg-accent)',
|
'bg-accent': 'var(--bg-accent)',
|
||||||
'state-success': 'var(--state-success)',
|
'state-success': 'var(--state-success)',
|
||||||
'state-warning': 'var(--state-warning)',
|
'state-warning': 'var(--state-warning)',
|
||||||
|
|||||||
@ -112,7 +112,7 @@
|
|||||||
--border-accent: #000000;
|
--border-accent: #000000;
|
||||||
--border-button: rgba(0, 0, 0, 0.1);
|
--border-button: rgba(0, 0, 0, 0.1);
|
||||||
/* Regulators, parsing, switches, variables */
|
/* Regulators, parsing, switches, variables */
|
||||||
--accent-primary: #00beb4;
|
--accent-primary: 0 190 180;
|
||||||
/* Output Variables Box */
|
/* Output Variables Box */
|
||||||
--bg-accent: rgba(76, 164, 231, 0.05);
|
--bg-accent: rgba(76, 164, 231, 0.05);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user