Feat: Refactor the MessageForm with shadcn #3221 (#7820)

### What problem does this PR solve?

Feat: Refactor the MessageForm with shadcn #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2025-05-23 18:45:13 +08:00 committed by GitHub
parent 590b9dabab
commit e604634d2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 94 additions and 90 deletions

View File

@ -19,7 +19,7 @@ import {
import { cn } from '@/lib/utils';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { Variable } from 'lucide-react';
import { useCallback, useState } from 'react';
import { ReactNode, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
import theme from './theme';
@ -45,6 +45,7 @@ const Nodes: Array<Klass<LexicalNode>> = [
type IProps = {
value?: string;
onChange?: (value?: string) => void;
placeholder?: ReactNode;
};
function PromptContent() {
@ -99,7 +100,7 @@ function PromptContent() {
);
}
export function PromptEditor({ value, onChange }: IProps) {
export function PromptEditor({ value, onChange, placeholder }: IProps) {
const { t } = useTranslation();
const initialConfig: InitialConfigType = {
namespace: 'PromptEditor',
@ -124,16 +125,25 @@ export function PromptEditor({ value, onChange }: IProps) {
);
return (
<LexicalComposer initialConfig={initialConfig}>
<RichTextPlugin
contentEditable={<PromptContent></PromptContent>}
placeholder={
<div className="absolute top-2 left-2">{t('common.pleaseInput')}</div>
}
ErrorBoundary={LexicalErrorBoundary}
/>
<VariablePickerMenuPlugin value={value}></VariablePickerMenuPlugin>
<VariableOnChangePlugin onChange={onValueChange}></VariableOnChangePlugin>
</LexicalComposer>
<div className="relative">
<LexicalComposer initialConfig={initialConfig}>
<RichTextPlugin
contentEditable={<PromptContent></PromptContent>}
placeholder={
<div
className="absolute top-10 left-2 text-text-sub-title"
data-xxx
>
{placeholder || t('common.pleaseInput')}
</div>
}
ErrorBoundary={LexicalErrorBoundary}
/>
<VariablePickerMenuPlugin value={value}></VariablePickerMenuPlugin>
<VariableOnChangePlugin
onChange={onValueChange}
></VariableOnChangePlugin>
</LexicalComposer>
</div>
);
}

View File

@ -789,7 +789,9 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
examples: 'Examples',
to: 'To',
msg: 'Messages',
messagePlaceholder: 'message',
msgTip:
'Output the variable content of the upstream component or the text entered by yourself.',
messagePlaceholder: `Please enter your message content, use '/' to quickly insert variables.`,
messageMsg: 'Please input message or delete this field.',
addField: 'Add option',
addMessage: 'Add message',
@ -813,7 +815,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
relevantDescription: `A component that uses the LLM to assess whether the upstream output is relevant to the user's latest query. Ensure you specify the next component for each judge result.`,
rewriteQuestionDescription: `A component that rewrites a user query from the Interact component, based on the context of previous dialogues.`,
messageDescription:
"A component that sends out a static message. If multiple messages are supplied, it randomly selects one to send. Ensure its downstream is 'Interact', the interface component.",
'This component returns the final data output of the workflow along with predefined message content. ',
keywordDescription: `A component that retrieves top N search results from user's input. Ensure the TopN value is set properly before use.`,
switchDescription: `A component that evaluates conditions based on the output of previous components and directs the flow of execution accordingly. It allows for complex branching logic by defining cases and specifying actions for each case or default action if no conditions are met.`,
wikipediaDescription: `A component that searches from wikipedia.org, using TopN to specify the number of search results. It supplements the existing knowledge bases.`,

View File

@ -761,7 +761,8 @@ export default {
examples: '範例',
to: '下一步',
msg: '訊息',
messagePlaceholder: '訊息',
msgTip: '輸出上游組件的變數內容或自行輸入的文字。',
messagePlaceholder: '請輸入您的訊息內容,使用‘/’快速插入變數。',
messageMsg: '請輸入訊息或刪除此欄位。',
addField: '新增字段',
addMessage: '新增訊息',
@ -786,7 +787,7 @@ export default {
relevantDescription: `此元件用來判斷upstream的輸出是否與使用者最新的問題相關『是』代表相關『否』代表不相關。`,
rewriteQuestionDescription: `此元件用於細化使用者的提問。通常,當使用者的原始提問無法從知識庫中檢索相關資訊時,此元件可協助您將問題變更為更符合知識庫表達方式的適當問題。`,
messageDescription:
'此元件用於向使用者發送靜態訊息。您可以準備幾條訊息,這些訊息將隨機選擇。',
'此元件用來傳回工作流程最後產生的資料內容和原先設定的文字內容。',
keywordDescription: `該組件用於從用戶的問題中提取關鍵字。 Top N指定需要提取的關鍵字數量。`,
switchDescription: `該組件用於根據前面組件的輸出評估條件,並相應地引導執行流程。通過定義各種情況並指定操作,或在不滿足條件時採取默認操作,實現複雜的分支邏輯。`,
wikipediaDescription: `此元件用於從 https://www.wikipedia.org/ 取得搜尋結果。通常,它作為知識庫的補充。 Top N 指定您需要調整的搜尋結果數。`,

View File

@ -789,7 +789,8 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
examples: '示例',
to: '下一步',
msg: '消息',
messagePlaceholder: '消息',
msgTip: '输出上游组件的变量内容或者自己输入的文本。',
messagePlaceholder: '请输入您的消息内容,使用‘/’快速插入变量。',
messageMsg: '请输入消息或删除此字段。',
addField: '新增字段',
addMessage: '新增消息',
@ -813,7 +814,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
relevantDescription: `该组件用来判断upstream的输出是否与用户最新的问题相关代表相关代表不相关。`,
rewriteQuestionDescription: `此组件用于细化用户的提问。通常,当用户的原始提问无法从知识库中检索到相关信息时,此组件可帮助您将问题更改为更符合知识库表达方式的适当问题。`,
messageDescription:
'此组件用于向用户发送静态信息。您可以准备几条消息,这些消息将被随机选择。',
'该组件用来返回工作流最后产生的数据内容和原先设置的文本内容。',
keywordDescription: `该组件用于从用户的问题中提取关键词。Top N指定需要提取的关键词数量。`,
switchDescription: `该组件用于根据前面组件的输出评估条件,并相应地引导执行流程。通过定义各种情况并指定操作,或在不满足条件时采取默认操作,实现复杂的分支逻辑。`,
wikipediaDescription: `此组件用于从 https://www.wikipedia.org/ 获取搜索结果。通常它作为知识库的补充。Top N 指定您需要调整的搜索结果数量。`,

View File

@ -127,7 +127,9 @@ export function useFormConfigMap() {
[Operator.Message]: {
component: MessageForm,
defaultValues: {},
schema: z.object({}),
schema: z.object({
content: z.array(z.string()).optional(),
}),
},
[Operator.Relevant]: {
component: RelevantForm,

View File

@ -49,11 +49,7 @@ export function BeginDynamicOptions() {
</div>
);
})}
<BlockButton
onClick={() => append({ value: '' })}
variant={'outline'}
type="button"
>
<BlockButton onClick={() => append({ value: '' })} type="button">
{t('flow.addVariable')}
</BlockButton>
</div>

View File

@ -1,6 +1,7 @@
import { BlockButton } from '@/components/ui/button';
import { RAGFlowNodeType } from '@/interfaces/database/flow';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Form, Input, Select } from 'antd';
import { MinusCircleOutlined } from '@ant-design/icons';
import { Form, Input, Select } from 'antd';
import { useTranslation } from 'react-i18next';
import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query';
import { FormCollapse } from '../components/dynamic-input-variable';
@ -49,14 +50,9 @@ export const DynamicInputVariable = ({
</div>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
block
icon={<PlusOutlined />}
>
<BlockButton onClick={() => add()}>
{t('flow.addVariable')}
</Button>
</BlockButton>
</Form.Item>
</>
)}

View File

@ -1,7 +1,7 @@
'use client';
import { FormContainer } from '@/components/form-container';
import { Button } from '@/components/ui/button';
import { BlockButton, Button } from '@/components/ui/button';
import {
FormControl,
FormField,
@ -12,7 +12,7 @@ import { Input } from '@/components/ui/input';
import { RAGFlowSelect } from '@/components/ui/select';
import { Separator } from '@/components/ui/separator';
import { RAGFlowNodeType } from '@/interfaces/database/flow';
import { Plus, X } from 'lucide-react';
import { X } from 'lucide-react';
import { ReactNode } from 'react';
import { useFieldArray, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
@ -92,14 +92,11 @@ export function DynamicVariableForm({ node, name = 'arguments' }: IProps) {
</div>
);
})}
<Button
<BlockButton
onClick={() => append({ name: '', component_id: undefined })}
className="mt-4 border-dashed w-full"
variant={'outline'}
>
<Plus />
{t('flow.addVariable')}
</Button>
</BlockButton>
</div>
);
}

View File

@ -1,4 +1,6 @@
import { Button } from '@/components/ui/button';
import { FormContainer } from '@/components/form-container';
import { PromptEditor } from '@/components/prompt-editor';
import { BlockButton, Button } from '@/components/ui/button';
import {
Form,
FormControl,
@ -7,73 +9,70 @@ import {
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { Textarea } from '@/components/ui/textarea';
import { PlusCircle, Trash2 } from 'lucide-react';
import { X } from 'lucide-react';
import { useFieldArray } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { INextOperatorForm } from '../../interface';
const MessageForm = ({ form }: INextOperatorForm) => {
const { t } = useTranslation();
const { fields, append, remove } = useFieldArray({
name: 'messages',
name: 'content',
control: form.control,
});
return (
<Form {...form}>
<form
className="space-y-6"
className="space-y-5 px-5"
autoComplete="off"
onSubmit={(e) => {
e.preventDefault();
}}
>
<FormItem>
<FormLabel>{t('flow.msg')}</FormLabel>
<div className="space-y-4">
{fields.map((field, index) => (
<div key={field.id} className="flex items-start gap-2">
<FormField
control={form.control}
name={`messages.${index}`}
render={({ field }) => (
<FormItem className="flex-1">
<FormControl>
<Textarea
{...field}
placeholder={t('flow.messagePlaceholder')}
rows={5}
/>
</FormControl>
</FormItem>
<FormContainer>
<FormItem>
<FormLabel tooltip={t('flow.msgTip')}>{t('flow.msg')}</FormLabel>
<div className="space-y-4">
{fields.map((field, index) => (
<div key={field.id} className="flex items-start gap-2">
<FormField
control={form.control}
name={`content.${index}.value`}
render={({ field }) => (
<FormItem className="flex-1">
<FormControl>
<PromptEditor
{...field}
placeholder={t('flow.messagePlaceholder')}
></PromptEditor>
</FormControl>
</FormItem>
)}
/>
{fields.length > 1 && (
<Button
type="button"
variant={'ghost'}
onClick={() => remove(index)}
>
<X />
</Button>
)}
/>
{fields.length > 1 && (
<Button
variant="ghost"
size="icon"
type="button"
onClick={() => remove(index)}
className="cursor-pointer text-colors-text-functional-danger"
>
<Trash2 className="h-4 w-4" />
</Button>
)}
</div>
))}
</div>
))}
<Button
type="button"
variant="outline"
onClick={() => append(' ')} // "" will cause the inability to add, refer to: https://github.com/orgs/react-hook-form/discussions/8485#discussioncomment-2961861
className="w-full mt-4"
>
<PlusCircle className="mr-2 h-4 w-4" />
{t('flow.addMessage')}
</Button>
</div>
<FormMessage />
</FormItem>
<BlockButton
type="button"
onClick={() => append({ value: '' })} // "" will cause the inability to add, refer to: https://github.com/orgs/react-hook-form/discussions/8485#discussioncomment-2961861
>
{t('flow.addMessage')}
</BlockButton>
</div>
<FormMessage />
</FormItem>
</FormContainer>
</form>
</Form>
);